Related
i want to get the list of next week days in given format "March 12 Sunday" and then convert that is final list of week days as given below.
Here is a my current code with which i am trying to get desire output but that returns "06/12/22" format..
Current code :
const nextWeek = [...Array(7).keys()].map(days => new Date(Date.now() + 86400000 * days).toLocaleDateString('en-us', { weekday:"long", month:"short", day:"numeric"}))
console.log("== > ",nextWeek)
current output :
["09/17/22", "09/18/22", "09/19/22", "09/20/22", "09/21/22", "09/22/22", "09/23/22"]
first i want this output
["Sunday, March 4", "Monday, March 4", "Tuesday, March 4", "Wednesday, March 4", "Thursday, March 4", "Friday, March 4", "Saturday, March 4"]
and then final desire output is:
const nextWeekdata = [
{ id: 1, name: "Sunday" ,date:21,Month:"March" },
{ id: 2, name: "Monday" ,date:22,Month:"March" },
{ id: 3, name: "Tuesday" ,date:23,Month:"March" },
{ id: 4, name: "Wednesday" ,date:24,Month:"March" },
{ id: 5, name: "Thursday" ,date:25,Month:"March" },
{ id: 6, name: "Friday" ,date:26,Month:"March" },
{ id: 7, name: "Saturday" ,date:27,Month:"March" },
];
If you want to calculate the range of dates for next week, break your logic into smaller chunks.
You should have three functions:
Function that gets the date for a given day of the week; next week
Function that gets the range of all the dates next week
Function to map the dates to object data
const getNextDay = (dayIndex) => {
const today = new Date();
today.setDate(today.getDate() + (dayIndex - 1 - today.getDay() + 7) % 7 + 1);
return today;
};
const getNextWeek = () => [...Array(7).keys()].map(getNextDay);
const nextWeek = getNextWeek().map((date, index) => ({
id: index + 1,
name: date.toLocaleDateString('en-us', { weekday: 'long' }),
date: date.getDate(),
month: date.toLocaleDateString('en-us', { month: 'long' }),
year: date.getFullYear()
}));
console.log(nextWeek);
.as-console-wrapper { top: 0; max-height: 100% !important; }
const nextWeek = [...Array(7).keys()].map((days) =>
new Date(Date.now() + 86400000 * days).toLocaleDateString("en-us", {
weekday: "long",
month: "short",
day: "numeric",
})
);
let newArray = [];
nextWeek.forEach((item, index) => {
let data = item.split(",");
let secondLength = data[1].length;
newArray.push({
id: index + 1,
name: data[0],
date: data[1].substring(secondLength - 2),
month: data[1].substring(1, secondLength - 3),
});
});
console.log("days", newArray);
does this work? yes
is this the right way to do it? nope
is this the easy way? for me, it is.
Actually, when I use your current code I get this as a result:
(7) ['Saturday, Sep 17', 'Sunday, Sep 18', 'Monday, Sep 19', 'Tuesday, Sep 20', 'Wednesday, Sep 21', 'Thursday, Sep 22', 'Friday, Sep 23']
Which means it works as you wish; what browser are you using?
Regarding the Second Question of yours, I'd have used something such as this:
class weekDay {
constructer(id, x) {
let name = x.split(", ")[0];
let day = x.split(", ")[1].split(" ")[1];
let month = x.split(", ")[1].split(" ")[0];
this.id = id;
this.name = name;
this.day = day;
this.month = month;
}
}
const nextWeek = [...Array(7).keys()].map(days => new Date(Date.now() + 86400000 * days).toLocaleDateString('en-us', { weekday:"long", month:"long", day:"numeric"}))
var arr = [];
for (let i=0; i<=6; i++) {
arr.push(new weekDay(i+1, nextWeek[i]));
}
This is my way of doing it, but take heed that I haven't tested it so it might have some syntax errors, if there was any, please notify me.
I have a date with a time, like this
const date = {year: 2020, month: 12, day: 31};
const time = {hours: 16, minutes: 2};
How do I get UTC representation of that time depending on a timezone?
(without using any libraries)
convetToUTC(date, time, "Europe/Moscow") // => <UTC timestamp>
convetToUTC(date, time, "America/New_York") // => <UTC timestamp>
Examples
convetToUTC(
{year: 2021, month: 7, day: 30},
{hours: 16, minutes: 15},
"Europe/Moscow"
) // => 1627650900
convetToUTC(
{year: 2021, month: 7, day: 30},
{hours: 16, minutes: 15},
"America/New_York"
) // => 1627676100
Piggy-backing on Achempion's response, I fixed the timezone offset calculation. The timezone date should be subtracted from the UTC date. The result of this difference should be in minutes.
You will need to then convert the minute offset back into milliseconds and subtract this from the date.
/**
* Calculates the timezone offset of a particular time zone.
* #param {String} timeZone - a database time zone name
* #param {Date} date - a date for determining if DST is accounted for
* #return {Number} returns an offset in minutes
* #see https://stackoverflow.com/a/68593283/1762224
*/
const getTimeZoneOffset = (timeZone = 'UTC', date = new Date()) => {
const utcDate = new Date(date.toLocaleString('en-US', { timeZone: 'UTC' }));
const tzDate = new Date(date.toLocaleString('en-US', { timeZone }));
return (tzDate.getTime() - utcDate.getTime()) / 6e4;
}
const defaultDateConfig = { year: 0, month: 0, date: 0 };
const defaultTimeConfig = { hours: 0, minutes: 0, seconds: 0 };
const convetToUTC = (dateConfig, timeConfig, timeZone) => {
const { year, month, date } = { ...defaultDateConfig, ...dateConfig };
const { hours, minutes, seconds } = { ...defaultTimeConfig, ...timeConfig };
const d = new Date(Date.UTC(year, month - 1, date, hours, minutes, seconds));
const offsetMs = getTimeZoneOffset(timeZone, d) * 6e4;
return (d.getTime() - offsetMs) / 1e3;
};
// Main
const date = { year: 2021, month: 7, date: 30 };
const time = { hours: 16, minutes: 15 };
console.log(convetToUTC(date, time, 'America/New_York')); // 1627676100
console.log(convetToUTC(date, time, 'Europe/Moscow')); // 1627650900
const dateWithTimeZone = (timeZone, year, month, day, hour, minute, second) => {
let date = new Date(Date.UTC(year, month, day, hour, minute, second));
let utcDate = new Date(date.toLocaleString('en-US', { timeZone: "UTC" }));
let tzDate = new Date(date.toLocaleString('en-US', { timeZone: timeZone }));
let offset = utcDate.getTime() - tzDate.getTime();
date.setTime( date.getTime() + offset );
return date;
};
dateWithTimeZone("America/New_York", 2021, 7 - 1, 30, 16, 15, 0).getTime() / 1000)
// => 1627676100
dateWithTimeZone("Europe/Moscow", 2021, 7 - 1, 30, 16, 15, 0).getTime() / 1000)
// => 1627650900
7 - 1 used to illustrate that function accepts month's index, not month's number
I have last 30 days data getting from MS SQL server Database and i want to group JSON object based on week numbers using node JS . I have JSON object in below format
var data = [{name:"abc",status:"Pending",created_timestamp:"2020-01-12 12:00:12"},
{name:"abc1",status:"Pending",created_timestamp:"2020-12-12 12:00:12"},
{name:"abc2",status:"Pending",created_timestamp:"2020-17-12 12:00:12"},
{name:"abc3",status:"Rejected",created_timestamp:"2020-20-12 12:00:12"},
{name:"abc4",status:"Rejected",created_timestamp:"2020-22-12 12:00:12"}]
i want to get output in below format:
var graphDetails = [{Week:1,
Pending:1,
Rejected:0},
{Week:2,
Pending:2,
Rejected:0},
{Week:3,
Pending:1,
Rejected:1}...]
var data = [
{ name: "abc", status: "Pending", created_timestamp: "2020-01-12 12:00:12" },
{ name: "abc1", status: "Pending", created_timestamp: "2020-12-12 12:00:12" },
{ name: "abc2", status: "Pending", created_timestamp: "2020-17-12 12:00:12" },
{
name: "abc3",
status: "Rejected",
created_timestamp: "2020-20-12 12:00:12",
},
{
name: "abc4",
status: "Rejected",
created_timestamp: "2020-22-12 12:00:12",
},
];
function getWeekNumber(d) {
// Copy date so don't modify original
d = new Date(Date.UTC(d.getFullYear(), d.getMonth(), d.getDate()));
// Set to nearest Thursday: current date + 4 - current day number
// Make Sunday's day number 7
d.setUTCDate(d.getUTCDate() + 4 - (d.getUTCDay() || 7));
// Get first day of year
var yearStart = new Date(Date.UTC(d.getUTCFullYear(), 0, 1));
// Calculate full weeks to nearest Thursday
var weekNo = Math.ceil(((d - yearStart) / 86400000 + 1) / 7);
// Return array of year and week number
return [d.getUTCFullYear(), weekNo];
}
var updated = {};
data.forEach((element) => {
let week = getWeekNumber(new Date(element.created_timestamp));
let weekDetail = updated[week];
switch (element.status) {
case "pending":
weekDetail.pending = weekDetail.pending + 1;
break;
default:
weekDetail.rejected = weekDetail.rejected + 1;
break;
}
updated[week] = {
week: week,
pending: weekDetail.pending,
rejected: weekDetail.rejected,
};
});
Hope this will help you out from your problem
So I have the data in below format
const data = [
{ date: '01-07-2019' },
{ date: '02-07-2019' },
{ date: '03-07-2019' },
{ date: '04-07-2019' },
{ date: '05-07-2019' },
{ date: '06-07-2019' },
{ date: '07-07-2019' },
{ date: '08-07-2019' },
{ date: '09-07-2019' },
{ date: '10-07-2019' },
{ date: '15-07-2019' },
{ date: '16-07-2019' },
{ date: '20-07-2019' },
{ date: '21-07-2019' },
{ date: '22-07-2019' },
{ date: '23-07-2019' }
]
So I have to count the regular interval dates. For example on date { date: '10-07-2019' }, { date: '20-07-2019' } and on { date: '23-07-2019' } it breaks so count should be again started with 1.
const ouput = [{
startDate: '01-07-2019',
endDate: '10-07-2019',
count: 10
}, {
startDate: '15-07-2019',
endDate: '16-07-2019',
count: 2
}, {
startDate: '20-07-2019',
endDate: '23-07-2019',
count: 4
}]
I did that
const output = Object.values(data.reduce((a, { startDate, endDate }, i) => {
const startTime = moment(data[i].date)
const endTime = moment(data[i + 1] && data[i + 1].date)
if (moment.duration(endTime.diff(startTime)).asDays === 1) {
a.startDate = startDate
a.startDate = endDate
}
a.count++;
return a;
}, {}));
But it is not giving what I expect. Please help.
I would do that with a function generator to handle the desired aggregation.
The below code will loop the dates, take a pair, check whether the start date exists, update the end date and automatically yield the value if necessary.
Comments are directly in the code below, the code assumes the initial array is already sorted as the example you mentioned.
As a side note, you're actually including the last date in the count, while, effectively, it should be one day less than your count. Further comments about that are available below in the function generator code.
const data = [
{ date: '01-07-2019' },
{ date: '02-07-2019' },
{ date: '03-07-2019' },
{ date: '04-07-2019' },
{ date: '05-07-2019' },
{ date: '06-07-2019' },
{ date: '07-07-2019' },
{ date: '08-07-2019' },
{ date: '09-07-2019' },
{ date: '10-07-2019' },
{ date: '15-07-2019' },
{ date: '16-07-2019' },
{ date: '20-07-2019' },
{ date: '21-07-2019' },
{ date: '22-07-2019' },
{ date: '23-07-2019' }
];
// Counts intervals of consecutive dates.
function* countIntervals(dates) {
// declare an initial accumulator.
let acc = {
count: 0
};
for (let i = 0; i < dates.length; i++) {
// get the currently looped value and the next one.
const [curr, next] = [moment(dates[i].date, 'DD-MM-YYYY'), dates[i+1] ? moment(dates[i+1].date, 'DD-MM-YYYY') : null];
// if the current date and next days are valid and if the difference in days between them is 1..
if (curr && next && (next.diff(curr, "days") === 1)) {
// Then keep track of the start date if not set, update the end date and increase the count of days.
acc.startDate = acc.startDate || dates[i].date, acc.endDate = dates[i+1].date, acc.count++;
}
else {
// otherwise, if the accumulator has a start date, yield the value.
if (acc && acc.startDate) {
acc.count++; // <-- comment this if you don't want the last date to be included.
yield Object.assign({}, acc);
// and init again the accumulator.
acc = {
count: 0
};
}
}
}
// if the loop is finished and the progression continued, yield the current accumulator.
if (acc.startDate) yield acc;
}
// usage...
const intervals = [...countIntervals(data)];
console.log(intervals);
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.24.0/moment.min.js"></script>
Here you go, Try this
const data = [
{ date: "01-07-2019" },
{ date: "02-07-2019" },
{ date: "03-07-2019" },
{ date: "04-07-2019" },
{ date: "05-07-2019" },
{ date: "06-07-2019" },
{ date: "07-07-2019" },
{ date: "08-07-2019" },
{ date: "09-07-2019" },
{ date: "10-07-2019" },
{ date: "15-07-2019" },
{ date: "16-07-2019" },
{ date: "20-07-2019" },
{ date: "21-07-2019" },
{ date: "22-07-2019" },
{ date: "23-07-2019" }
];
function to parse date
function parseDate(input) {
var parts = input.split("-");
// new Date(year, month [, day [, hours[, minutes[, seconds[, ms]]]]])
return new Date(parts[2], parts[1] - 1, parts[0]); // Note: months are 0-based
}
function to get date difference
function dateDiff(date1, date2) {
date1 = parseDate(date1);
date2 = parseDate(date2);
let diffTime = Math.abs(date2.getTime() - date1.getTime());
let diffDays = Math.ceil(diffTime / (1000 * 60 * 60 * 24));
return diffDays;
}
Required output
const output = data.reduce(function(resultSet, currentValue, currentIndex, arr) {
if (resultSet.length == 0) {
resultSet.push({
startDate: currentValue.date,
endDate: currentValue.date,
count: 1
});
}
else{
let dateDiffrence = dateDiff(resultSet[resultSet.length-1].endDate, currentValue.date);
console.log(dateDiffrence);
if(dateDiffrence == 1){
resultSet[resultSet.length-1].endDate = currentValue.date;
resultSet[resultSet.length-1].count++;
}else{
resultSet.push({
startDate: currentValue.date,
endDate: currentValue.date,
count: 1
});
}
}
return resultSet;
}, []);
Yet another possible solution.
const parseDate = (str) => {
const [d, m, y] = str.split('-');
return +new Date(y, m - 1, d)
}
const output = data.reduce((a, {
date
}, i) => {
const cur = parseDate(date);
const lastDate = data[i - 1] && data[i - 1].date || date;
const last = parseDate(lastDate || date);
if (cur - last > 1000 * 60 * 60 * 24) a.push({count: 0});
const {
startDate = date,
count
} = a.pop();
a.push({
startDate,
endDate: date,
count: count + 1
})
return a;
}, [{
count: 0
}])
console.log (output)
<script>
const data = [
{ date: '01-07-2019' },
{ date: '02-07-2019' },
{ date: '03-07-2019' },
{ date: '04-07-2019' },
{ date: '05-07-2019' },
{ date: '06-07-2019' },
{ date: '07-07-2019' },
{ date: '08-07-2019' },
{ date: '09-07-2019' },
{ date: '10-07-2019' },
{ date: '15-07-2019' },
{ date: '16-07-2019' },
{ date: '20-07-2019' },
{ date: '21-07-2019' },
{ date: '22-07-2019' },
{ date: '23-07-2019' }
]</script>
If you construct UTC dates there will be no need to use moment.js. With UTC every day is 24 hours and DST does not apply. This solution features a boilerplate function to handle the creation of the UTC date from your date string format.
const data = [
{ date: '01-07-2019' },
{ date: '02-07-2019' },
{ date: '03-07-2019' },
{ date: '04-07-2019' },
{ date: '05-07-2019' },
{ date: '06-07-2019' },
{ date: '07-07-2019' },
{ date: '08-07-2019' },
{ date: '09-07-2019' },
{ date: '10-07-2019' },
{ date: '15-07-2019' },
{ date: '16-07-2019' },
{ date: '20-07-2019' },
{ date: '21-07-2019' },
{ date: '22-07-2019' },
{ date: '23-07-2019' }
];
const ONE_DAY = 1000 * 60 * 60 * 24;
function dateStrToUTC(dateStr) {
const dateParts = dateStr.split('-');
const utcDate = new Date();
utcDate.setUTCFullYear(dateParts[2]);
utcDate.setUTCMonth(dateParts[1] - 1);
utcDate.setUTCDate(dateParts[0]);
utcDate.setUTCHours(0);
utcDate.setUTCMinutes(0);
utcDate.setUTCSeconds(0);
utcDate.setUTCMilliseconds(0);
return utcDate;
}
function getRegularIntervals(accumulator, currentValue) {
const index = accumulator.length - 1;
let daysPassed = 0;
if (index > -1) {
daysPassed = (dateStrToUTC(currentValue.date) - dateStrToUTC(accumulator[index].endDate)) / ONE_DAY;
}
if (index > -1 && 1 == daysPassed) {
accumulator[index].endDate = currentValue.date;
accumulator[index].count++;
} else {
accumulator.push({
startDate: currentValue.date,
endDate: currentValue.date,
count: 1
});
}
return accumulator;
}
const output = data.reduce(getRegularIntervals, []);
console.log(output);
Output as expected:
[
{
"startDate": "01-07-2019",
"endDate": "10-07-2019",
"count": 10
},
{
"startDate": "15-07-2019",
"endDate": "16-07-2019",
"count": 2
},
{
"startDate": "20-07-2019",
"endDate": "23-07-2019",
"count": 4
}
]
I liked your approach of using reduce function.
Going with the same, I just added some more logic in there and here is the final code.
// initially lets assume first date is the start as well as end date
var dateIntervalObject = {
startDate: data[0].date,
endDate: data[0].date,
count: 1
};
var result = data.reduce((resultArray, obj, i) => {
if(i > 0) {
var startTime = moment(dateIntervalObject.endDate, "DD-MM-YYYY");
var endTime = moment(obj.date, "DD-MM-YYYY");
if (endTime.diff(startTime, 'days') === 1) {
dateIntervalObject.endDate = obj.date;
dateIntervalObject.count += 1;
// remove the latest object in array, to replace with new
resultArray.pop();
} else {
dateIntervalObject = {
startDate: obj.date,
endDate: obj.date,
count: 1
};
}
// push the date Interval object in the array
resultArray.push(dateIntervalObject);
}
return resultArray;
}, [dateIntervalObject]);
console.log('result: ',result);
Note:
When initialState of the accumulator is passed to reduce function it starts iterating from 0th index, which in our case have already been initialized in the dateIntervalObject and therefore the first iteration with index value 0 is skipped.
Also, if the interval is not changing, we don't need to add another object to our result array but instead update the end date of the last element of our result array. Therefore, first pop and then push to just update the end date and count value.
Hope this helps!
I am trying to implement a function using reduce that allows me to group an array of objects { date: '2019-03-11', count: 8 } by weeks. So far, I was able to group dates by weeks but I am having trouble to add together count if the date falls in the same week.
const dates = [
{ date: '2019-03-11', count: 8 },
{ date: '2019-03-12', count: 7 },
{ date: '2019-03-09', count: 6 },
{ date: '2019-02-27', count: 10 },
{ date: '2019-02-26', count: 11 },
{ date: '2019-02-22', count: 12 },
{ date: '2019-04-21', count: 3 },
{ date: '2019-04-18', count: 2 },
{ date: '2019-04-17', count: 4 },
{ date: '2019-04-19', count: 5 }
];
Date.prototype.getWeek = function() {
const onejan = new Date(this.getFullYear(), 0, 1);
return Math.ceil(((this - onejan) / 86400000 + onejan.getDay() + 1) / 7);
};
const groups = dates.reduce(function(acc, item) {
const today = new Date(item.date);
const weekNumber = today.getWeek(today);
// check if the week number exists
if (typeof acc[weekNumber] === 'undefined') {
acc[weekNumber] = [];
}
acc[weekNumber].push(item.date, item.count);
return acc;
}, {});
console.log(groups);
Current Result
Desired Result
[
{ weekStart: '2019-02-17', count: 12 },
{ weekStart: '2019-02-24', count: 21 },
{ weekStart: '2019-03-03', count: 6 },
{ weekStart: '2019-03-10', count: 15 },
{ weekStart: '2019-04-14', count: 11 },
{ weekStart: '2019-04-21', count: 21 }
]
where weekStart is the first date of the week (Sunday) by which it was grouped
SOLUTION
const dates = [
{ date: '2019-02-24', count: 10 },
{ date: '2019-02-25', count: 11 },
{ date: '2019-02-26', count: 12 },
{ date: '2019-03-09', count: 8 },
{ date: '2019-03-10', count: 7 },
{ date: '2019-03-11', count: 6 },
{ date: '2019-04-14', count: 3 },
{ date: '2019-04-15', count: 2 },
{ date: '2019-04-16', count: 4 },
{ date: '2019-04-22', count: 5 }
];
/**
* Returns the week number for this date. dowOffset is the day of week the week
* "starts" on for your locale - it can be from 0 to 6. If dowOffset is 1 (Monday),
* the week returned is the ISO 8601 week number.
* #param int dowOffset
* #return int
*/
Date.prototype.getWeek = function(dowOffset) {
/*getWeek() was developed by Nick Baicoianu at MeanFreePath: http://www.epoch-calendar.com */
dowOffset = typeof dowOffset == 'int' ? dowOffset : 0; //default dowOffset to zero
var newYear = new Date(this.getFullYear(), 0, 1);
var day = newYear.getDay() - dowOffset; //the day of week the year begins on
day = day >= 0 ? day : day + 7;
var daynum =
Math.floor(
(this.getTime() -
newYear.getTime() -
(this.getTimezoneOffset() - newYear.getTimezoneOffset()) * 60000) /
86400000
) + 1;
var weeknum;
//if the year starts before the middle of a week
if (day < 4) {
weeknum = Math.floor((daynum + day - 1) / 7) + 1;
if (weeknum > 52) {
nYear = new Date(this.getFullYear() + 1, 0, 1);
nday = nYear.getDay() - dowOffset;
nday = nday >= 0 ? nday : nday + 7;
/*if the next year starts before the middle of
the week, it is week #1 of that year*/
weeknum = nday < 4 ? 1 : 53;
}
} else {
weeknum = Math.floor((daynum + day - 1) / 7);
}
return weeknum;
};
function getWeekStart(date) {
var offset = new Date(date).getDay();
return new Date(new Date(date) - offset * 24 * 60 * 60 * 1000)
.toISOString()
.slice(0, 10);
}
function groupWeeks(dates) {
const groupsByWeekNumber = dates.reduce(function(acc, item) {
const today = new Date(item.date);
const weekNumber = today.getWeek();
// check if the week number exists
if (typeof acc[weekNumber] === 'undefined') {
acc[weekNumber] = [];
}
acc[weekNumber].push(item);
return acc;
}, []);
return groupsByWeekNumber.map(function(group) {
return {
weekStart: getWeekStart(group[0].date),
count: group.reduce(function(acc, item) {
return acc + item.count;
}, 0)
};
});
}
console.log(groupWeeks(dates));
You could take the weekday as offset and subtract the milliseconds from the given date.
Reducing works with a callback and a clsoure over the key with the week start date.
(m, { date, count }) => // outer callback with map and object
(k => m.set(k, (m.get(k) || 0) + count)) // closure over k and updating the count in map
(getWeekStart(date)) // get value for k
function getWeekStart(date) {
var offset = new Date(date).getDay();
return new Date(new Date(date) - offset * 24 * 60 * 60 * 1000).toISOString().slice(0, 10);
}
const
dates = [{ date: '2019-03-11', count: 8 }, { date: '2019-03-12', count: 7 }, { date: '2019-03-09', count: 6 }, { date: '2019-02-27', count: 10 }, { date: '2019-02-26', count: 11 }, { date: '2019-02-22', count: 12 }, { date: '2019-04-21', count: 3 }, { date: '2019-04-18', count: 2 }, { date: '2019-04-17', count: 4 }, { date: '2019-04-19', count: 5 }],
result = Array.from(
dates.reduce((m, { date, count }) =>
(k => m.set(k, (m.get(k) || 0) + count))(getWeekStart(date)),
new Map),
([date, count]) => ({ date, count })
);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }