How to get time difference from ZoneDateTime in javascript - javascript

How can I get the time difference in seconds between two date-times one is from the backend in the form of ZoneDateTime and the other will be the current time at the frontend while response get received.
Response time from backend is -
callStartTime:{
chronology: {id: 'ISO', calendarType: 'iso8601'}
dayOfMonth: 8
dayOfWeek: "TUESDAY"
dayOfYear: 39
hour: 16
minute: 15
month: "FEBRUARY"
monthValue: 2
nano: 240000000
offset: {totalSeconds: 19800, id: '+05:30', rules: {…}}
second: 29
year: 2022
zone: {id: 'Asia/Calcutta', rules: {…}}
From this How can I find the difference between current time and this time in seconds ?

Calculate the equivalent ECMAScript time value from the data. Subtract that from the current time value and you have the difference in milliseconds, e.g.
function getTimeDiff(data, date = new Date()) {
let utc = new Date(Date.UTC(
data.year,
data.monthValue - 1,
data.dayOfMonth,
data.hour,
data.minute,
data.second
) - data.offset.totalSeconds*1e3);
return date - utc;
}
function msToTime(ms) {
let days = ms / 8.64e7 | 0;
let hrs = (ms%8.64e7) / 3.6e6 | 0;
let mins = (ms%3.6e6) / 6e4 | 0;
let secs = (ms%6e4) / 1e3 | 0;
let z = n => (n<10? '0':'') + n;
return `${days? days + ' day' + (days == 1? ' ' : 's ') : ''}${z(hrs)}:` +
`${z(mins)}:${z(secs)}`;
}
let callStartTime = {
chronology: {id: 'ISO', calendarType: 'iso8601'},
dayOfMonth: 8,
dayOfWeek: "TUESDAY",
dayOfYear: 39,
hour: 16,
minute: 15,
month: "FEBRUARY",
monthValue: 2,
nano: 240000000,
offset: {totalSeconds: 19800, id: '+05:30', rules: {}},
second: 29,
year: 2022,
zone: {id: 'Asia/Calcutta', rules: {}}
};
['2022-02-08T16:15:29+05:30', // 0
null // now
].forEach(ts => {
let data = callStartTime;
let compDate = ts? new Date(ts) : new Date();
let diffMs = getTimeDiff(data, compDate);
console.log(`${ts || compDate.toISOString()} : ` +
`${diffMs} ms (${msToTime(diffMs)})`
);
});

Related

Biweekly View with DHTMLX Gantt Chart

In my dhtmlx gantt chart, I've configured my scales to show the year and Quarter (3 PIs per quarter)
gantt.config.scales = [
{ name: 'year', format: '%Y' },
{ name: 'quarter', template: date => `PI-${(Math.floor((new Date(date).getMonth() / 3)) + 1 )}` },
]
This gives me the result
| 2022 |
| PI-1 | PI-2 | PI-3 |
I now want to add 2-week increments to my scale to represent sprints per Quarter (6 sprints per quarter, 24 sprints per year)
| 2022 |
| PI-1 | PI-2 | PI-3 |
| S1 | S2 | S3 | S4 | S5 | S6 |
I can't figure out how to structure my template to achieve this. Any thoughts?
It should be noted that if the increment is 2 weeks, then there will be more than 24 sprints in a year. The same can be said about months, one quarter is equal to 3 months, which contain a non-integer number of weeks, so the boundaries of the quarter and sprint scales cannot coincide.
To set sprints in the scale, you need to use Custom time units.
And so I can offer the following solution:
If the increment is to be strictly 2 weeks, first determine the start date of each sprint. It is also necessary to set the start for the first sprint, for example, I set it to start on Monday of the first week of the year:
// specify the start date of the first sprint
gantt.config.project_start = new Date(2022, 0, 3);
// find a sprint for a given date
function findSprint(date) {
const firstSprint = gantt.date.week_start(new Date(gantt.config.project_start));
let currentDate = firstSprint;
let direction = 1;
if (date < firstSprint) {
direction = -1;
}
const increment = 2 * direction;
let nextDate = gantt.date.add(currentDate, increment, "week");
let num = 0;
while (!(currentDate.valueOf() <= date.valueOf() && nextDate.valueOf() > date.valueOf())) {
if (increment > 0) {
currentDate = nextDate;
nextDate = gantt.date.add(currentDate, increment, "week");
} else {
nextDate = currentDate;
currentDate = gantt.date.add(currentDate, increment, "week");
}
num += 1 * direction;
}
return {
sprintStart: currentDate,
sprintEnd: nextDate,
sprintNumber: num
}
}
// custom scale unit definition
gantt.date.sprint_start = function (date) {
return findSprint(date).sprintStart;
};
The next step is to specify that the increment will be two weeks:
gantt.date.add_sprint = function (date, inc) {
return gantt.date.add(gantt.date.sprint_start(date), inc * 2, "week");
};
And finally, add a new unit to the scale:
gantt.config.scales = [
{ unit: "year", step: 1, format: "%Y" },
{
unit: 'quarter',
format: date => {
return `PI-${(Math.floor((new Date(date).getMonth() / 3)) + 1)}`
}
},
{ unit: 'sprint', step: 1, template: function (date) {
const sprintInfo = findSprint(date);
return `Sprint ${sprintInfo.sprintNumber + 1}, (${gantt.templates.date_grid(sprintInfo.sprintStart)} - ${gantt.templates.date_grid(new Date(sprintInfo.sprintEnd - 1))})`
}
}
];
Please see an example: https://snippet.dhtmlx.com/15u2bd85.
Sometimes for some reason the borders of the quarter and year scales do not match, and this is most likely a bug, if this happens to you, please contact dhtmlx technical support.
If you want to have 24 sprints in a year, you can set specific dates for each sprint, it might look something like this:
const sprints = [
{ name: 'S1', start_date: new Date(2022,00,01), end_date: new Date(2022,00,15) },
{ name: 'S2', start_date: new Date(2022,00,15), end_date: new Date(2022,01,01) },
{ name: 'S3', start_date: new Date(2022,01,01), end_date: new Date(2022,01,15) },
{ name: 'S4', start_date: new Date(2022,01,15), end_date: new Date(2022,02,01) },
{ name: 'S5', start_date: new Date(2022,02,01), end_date: new Date(2022,02,15) },
{ name: 'S6', start_date: new Date(2022,02,15), end_date: new Date(2022,03,01) },
...
];
You need to add the given sprints to the unit:
gantt.date.sprints_start = function(date) {
return date;
};
function getSprint(date,type) {
const tempDate = new Date(date);
for (let i = 0; i < sprints.length; i++) {
if (+tempDate >= +sprints[i].start_date && +tempDate < +sprints[i].end_date) {
if (type == 'scaleUnit') {
return sprints[i].end_date;
}
if (type == 'template') {
return "<div class='sprint'>"+sprints[i].name+"</div>";
}
}
}
if (type == 'scaleUnit') {
const newDate = gantt.date.add(date, 1,'day');
return newDate;
}
if (type == 'template') {
return gantt.date.date_to_str("%m-%d")(date);
}
}
gantt.date.add_sprints = function(date, inc) {
return getSprint(date, 'scaleUnit');
};
const sprintsTemplate = function(date) {
return getSprint(date,'template');
}
and also add a new unit to the scale:
gantt.config.scales = [
{ unit: "year", step: 1, format: "%Y" },
{
unit: 'quarter',
format: date => {
return `PI-${(Math.floor((new Date(date).getMonth() / 3)) + 1 )}`
}
},
{ unit: 'month', step: 1, format: "%M" },
{ unit: 'sprints', step: 1, template: sprintsTemplate },
];
Here is an example: https://snippet.dhtmlx.com/0xznw5m9

How to add the values ​in an array of objects depending on the date value of each object

I have this array:
[{start_date: "2022-12-05T04:00:00Z" ,distance: 1000, time: 3600}
,{start_date: "2022-02-07T04:00:00Z" ,distance: 1500, time: 6400},
{start_date: "2022-12-08T04:00:00Z" ,distance: 1000, time: 1300}]
I want to add the distance and time values ​​grouping them by the month indicated by the start_date value. For example, if two start_dates have the same month 2022-12-01 and 2022-12-08, how can I add the distance and time values ​​of those two months?
so i get a new array like this:
[{month: 12 ,total distance: 2000, total time: 4900},
{month: 02 , total distance: 1500, total time: 6400} ]
you can use reduce to group them by month which will give an object like
{
12: {
distance: 2000,
month: 12,
time: 4900
},
2: {
distance: 1500,
month: 2,
time: 6400
}
}
and using Object.values get the values array of it
let x = [{start_date: "2022-12-05T04:00:00Z" ,distance: 1000, time: 3600},{start_date: "2022-02-07T04:00:00Z" ,distance: 1500, time: 6400},{start_date: "2022-12-08T04:00:00Z" ,distance: 1000, time: 1300}]
let res = Object.values(x.reduce((acc,{start_date,distance,time})=> {
let month = new Date(start_date).getMonth()+1
if(!acc[month])acc[month] = {totalDistance:0,totalTime:0,month:month}
acc[month].totalDistance+=distance
acc[month].totalTime+=time
return acc;
},{}))
console.log(res)
You can use a object as a dictionary and save a accumulated value of time and distance per month key. Then, reduce all keys to an array with the requested format.
const groupPerMonth = (list) => {
const extractMonth = (stringDate) => {
const month = new Date(stringDate).getMonth() + 1;
return month < 10 ? `0${month}` : `${month}`;
}
const months = {};
for (const item of list) {
const month = extractMonth(item.start_date);
if (!(month in months)) {
months[month] = {
distance: 0,
total_time: 0,
};
}
months[month].distance += item.distance;
months[month].total_time += item.time;
}
const result = [];
for (const month in months) {
result.push({
month,
...months[month]
});
}
return result;
};
And test it:
console.log(
groupPerMonth([
{ start_date: "2022-12-05T04:00:00Z", distance: 1000, time: 3600 },
{ start_date: "2022-02-07T04:00:00Z", distance: 1500, time: 6400 },
{ start_date: "2022-12-08T04:00:00Z", distance: 1000, time: 1300 },
])
);
Output:
[
{ month: '12', distance: 2000, total_time: 4900 },
{ month: '02', distance: 1500, total_time: 6400 }
]
There might be different solutions to this, but one way to solve this is by using the lodash library to solve this. We can first group by month, followed by mapping each grouped item and adding the distance and time values in each group using reduce:
const list = [
{start_date: "2022-12-05T04:00:00Z" ,distance: 1000, time: 3600},
{start_date: "2022-02-07T04:00:00Z" ,distance: 1500, time: 6400},
{start_date: "2022-12-08T04:00:00Z" ,distance: 1000, time: 1300}
]
const grouped = _.groupBy(list, item => {
const date = new Date(item.start_date)
return date.getMonth() + 1
})
const groupedAndMapped = _.map(grouped, function(groupedList, date){
return {
month: date,
total_distance: _.reduce(groupedList, (total, current) => {
return total + current.distance
}, 0),
total_time:_.reduce(groupedList, (total, current) => {
return total + current.time
}, 0)
}
})
One improvement you could do is to format the month to an "MM-YYYY" format or something similar as its possible your data set can include items with different years.

Js return the start date and end date on a string

I have strings like this:
(Feb 28-Mar 1)
(Mar 2-3)
I would like me to return an object as you can see below, someone to give some suggestion how can I do?
function rD(text){
let date = text.replace('(', '').replace(')', '').split(' ');
//const [start, end] = date[2].split("-").map(Number);
return date;
}
console.log(rD("(Feb 28-Mar 1)"))
console.log(rD("(Mar 2-3)"))
Return:
[
{
month: 2,
day: 28
},
{
month: 3,
day: 1
}
]
[
{
month: 3,
day: 2
},
{
month: 3,
day: 3
}
]
I'd suggest using a regex pattern to parse each span.
From this we can get the startMonth, startDay, endMonth, endDay. We can then create a getMonthNumber() function to turn the abbreviated month name (Jan, Feb, etc.) to a number.
function getMonthNumber(month) {
const lookup = { jan: 01, feb: 02, mar: 03, apr: 04, may: 05, jun: 06, jul: 07, aug: 08, sep: 09, oct: 10, nov: 11, dec: 12};
return lookup[(month + '').toLowerCase()]
}
function parseSpan(str) {
const pattern = /\(([a-z]{3})\s+(\d{1,2})\-([a-z]{3})?\s?(\d{1,2})\)/i
const [, startMonth, startDay, endMonth, endDay] = str.match(pattern);
return [
{ month: getMonthNumber(startMonth), day: +startDay },
{ month: getMonthNumber(endMonth || startMonth), day: +endDay }
];
}
let testInputs = [
'(Feb 28-Mar 1)',
'(Mar 2-3)',
'(Sep 28-Oct 31)',
'(Jan 3-17)'
]
testInputs.map(parseSpan).forEach(span => console.log(span))
.as-console-wrapper { max-height: 100% !important; }
I'd remove the parentheses first and then split by /[ -]/. This way you get an array in one of these two forms
["Feb", "28", "Mar", "1"]
or
["Mar", "2", "3"]
Now if the array has 4 elements the first and third are always the month and second and forth are the day. If the array has 3 elements, the first is a month for both, start and end, the second and third are the days.
For getting the number of the month you can have a simple lookup like
{ Jan:1, Feb:2, ... }
let months = { Jan: 1, Feb: 2, Mar: 3 /* you get the idea*/}
let spans = ["(Jan 28 - Feb 3)", "(Mar 1-3)"]
let parse = (span) => {
let parts = span.replace(/[()]/g, "").split(/[ -]/).filter(x => !!x);
switch (parts.length) {
case 4: return [{month: months[parts[0]], date: +parts[1]}, {month: months[parts[2]], date: +parts[3]}];
case 3: return [{month: months[parts[0]], date: +parts[1]}, {month: months[parts[0]], date: +parts[2]}];
default: return undefined;
}
}
console.log(parse(spans[0]));
console.log(parse(spans[1]))
You can try this
function rangeCalcFunc(range = null) {
if(range && range.length){
const [start, end] = range.substring(1, range.length-1).split("-");
console.log(start);console.log(end);
const [startMon, startDt] = start.split(" ");
const [endMon, endDt] = end.split(" ");
return [
{
month: calcMonthInNumber(startMon.trim()),
date: startDt
},
{
month: calcMonthInNumber(endMon.trim()),
date: endDt
}
]
}
}
function calcMonthInNumber(month) {
switch(month.toLowerCase()){
case 'jan': return '01'
case 'feb': return '02'
//add for other months
default: break;
}
}
console.log(rangeCalcFunc("(Jan 28-Feb 1)"));
First we are going to create a mapper for the months. like this:
let MonthsMapper = new Map([['Jan', 1], ['Feb', 2], ['Mar', 3] /*...*/])
Then we need a function which cutes the string into chunks by removing the parenthesis and splitting it by its hyphen.
The first chunks are the start and end dates.
With these two dates we can further get the start month, start day, end month, and end day. (By splitting our chunks by there whitespaces)
There is only one special case I can see from your example and that is the case when the end date does not specify a month, in which case it is implicitly the start month.
let DateObjectParser = (dateString) => {
const [startDate, endDate] = dateString.replace(/[()]/g, '').split('-')
const [startMonth, startDay] = startDate.split(' ')
let [endMonth, endDay] = endDate.split(' ')
// in case a second month is not provided
if (endDay === undefined) {
endDay = endMonth
endMonth = startMonth
}
return [
{
month: MonthsMapper.get(startMonth),
day: parseInt(startDay),
},
{
month: MonthsMapper.get(endMonth),
day: parseInt(endDay),
}
]
}
Besides the code, the advantage to this approach is that it works if the input is something like Jan 1-3-Mar 7, while all the other answers don't put this into consideration, thus returning undefined or an error.
Simple regex and a list of month to numbers should work, and it seems the most straightforward to me. Using String.prototype.match we can "ignore" all the extra data (i.e. the parentheses and dashes between months), just extract the necessary data:
function rD(str) {
// have a list of the numbers of each month
const monthNum = {
jan: 1,
feb: 2,
mar: 3,
apr: 4,
may: 5,
jun: 6,
jul: 7,
aug: 8,
sep: 9,
oct: 10,
nov: 11,
dec: 12
};
// regex to extract the patterns "Feb 23" or "Aug 15-23"
let spl = str.match(/[a-z]{3} \d{1,2}(\-\d{1,2})?/gi);
// loop through matches
const result = spl.map(s => {
// first 3 letters is the month; get the month number
const month = monthNum[s.substr(0, 3).toLowerCase()],
rest = s.substr(4); // get the rest of the string sans the month and extra space
const e = rest.split("-");
return e.map(q => ({
month,
day: +q
}));
}).flat(); // our array may occasionally be multidimensional if the user provides something like "Nov 7-12". We flatten the array to fix that
return result;
}
console.log("(Feb 28-Mar 1):", rD("(Feb 28-Mar 1)"));
console.log("(Mar 2-3):", rD("(Mar 2-3)"));
console.log("(Nov 7-12-Dec 15):", rD("(Nov 7-12-Dec 15)"));
.as-console-wrapper { min-height: 100% !important; }
Added all the steps inline in below code snippet.
// const str = "(Feb 28-Mar 1)";
const str = "(Mar 2-3)";
// An Object which contains numeric value of months.
const monthMap = {
Jan: 1,
Feb: 2,
Mar: 3
};
/**
* convertStringToObject() method used to convert the input string into an object.
* #param : inputString
*/
function convertStringToObject(inputString) {
// Removing open and closed paranthesis and splitting the inputString with '-'
const split = inputString.replace('(', '').replace(')', '').split('-');
// If splitted array element does not contain month, appending that.
split[1] = (split[1].trim().length === 1) ? split[0].split(' ')[0] + ' ' + split[1] : split[1];
// Looping over the splitted array and then creating the object with keys 'month' and 'day'
return split.map((item) => {
const splittedItem = item.split(' ');
return {
month: monthMap[splittedItem[0]],
day: Number(splittedItem[1])
}
});
}
// Calling the convertStringToObject() method by passing the input string as a param.
console.log(convertStringToObject(str));

Get sum of array values based on date values of other array

So i have problem like this:
I have 2 arrays:
one from mysql query that contains date when order have been created and total sum of it
const ordersData = [
0: {id: 1, data: '2021-11-23T00:00:00.000Z', price: 394}
1: {id: 2, data: '2021-11-23T00:00:00.000Z', price: 315.3}
2: {id: 3, data: '2021-11-23T00:00:00.000Z', price: 16445}
...
6: {id: 7, data: '2021-11-23T00:00:00.000Z', price: 200}
7: {id: 8, data: '2021-12-22T00:00:00.000Z', price: 618}
]
second is array is where i have monthly sum, month, first and last day of month
const pastMonthsData = [
0: {
month: "december",
firstDay: Wed Dec 01 2021,
lastDay: Fri Dec 31 2021,
totalMonthSum: x
},
1: {
month: "november",
firstDay: Mon Nov 01 2021,
lastDay: Tue Nov 30 2021,
totalMonthSum: x
}
]
I need to check if date from order array is in between date of pastMonthsData and add price to totalMonthSum.
So far i created func like this but it only works for december, for november there is no result.
pastMonthsData.forEach((el, i) => {
el.totalMonthSum = ordersData.reduce((total, item) => {
let itemDate = new Date(item.data);
if(el.firstDay.getTime() < itemDate.getTime() && itemDate.getTime() < el.lastDay.getTime()) {
return total + item.price
} else {
return 0
}
})
});
Two fixes:
initialize the accumulated total to zero
return the accumulated total, rather than zero, when a date is not in range
For example:
pastMonthsData.forEach((el, i) => {
el.totalMonthSum = ordersData.reduce((total, item) => {
let itemDate = new Date(item.data);
if (el.firstDay.getTime() < itemDate.getTime() && itemDate.getTime() < el.lastDay.getTime()) {
return total + item.price;
} else {
return total;
}
}, 0)
});

Sum values per month from array of objects in JavaScript

I have an array of objects like this:
var json = [ { day: '01-01-2018', hour: '00:00', value: '121' }, { day: '01-02-2018', hour: '05:24', value: '131' }, { day: '26-01-2018', hour: '00:07', value: '101' }, { day: '16-02-2018', hour: '08:02', value: '56' }, { day: '20-02-2018', hour: '12:02', value: '123' }, { day: '24-03-2018', hour: '10:11', value: '45' }];
I would like to change format of date from DD-MM-YYYY to YYYY-MM-DD and then sum values per month to create a chart.
My attempt:
var mapDateVolume = [];
for (var i = 0; i < json.length; i++)
{
var allDate = json[i].day;
var oneVolume = json[i].value;
var splitedDate = allDate.split("-");
var newOrderY = splitedDate[2] + "-"
var newOrderM = splitedDate[1] + "-";
var newOrderD = splitedDate[0];
var newOrderAllDate = newOrderY + newOrderM + newOrderD;
var newOrder = {};
newOrder[newOrderAllDate] = oneVolume;
mapDateVolume.push(newOrder);
}
var result = [];
for (var i = 0; i < mapDateVolume.length; i++){
var key = Object.keys(mapDateVolume)[i].split("-")[0] + "/";
key += Object.keys(mapDateVolume)[i].split("-")[1];
var value = Object.values(mapDateVolume)[i];
var oldValue = Object.keys(result)[i] != null ? Object.keys(result)[i] : 0;
var newResult = {};
newResult[key] = value;
result.push(newResult);
}
for (var i = 0; i < result.length; i++) {
xAxis.push(Object.keys(result)[i]);
yAxis.push(Object.values(result)[i]);
}
I use Chart.js and it works fine with days:
for ( var i = 0; i < jsonAll.length; i++ )
{
xAxis.push(json[i].day+'-'+json[i].hour);
yAxis.push(json[i].value);
}
I know that something is wrong with this sum because I see only an empty chart. Unfortunately, I have little experience with JavaScript.
First of all lets format whole collection (data array).
We need to:
Date format: DD-MM-YYYY to YYYY-MM-DD.
Value property is a string type we need it as number.
Simple with Array#Map:
const collection = data.map(x => ({ ...x, day: x.day.split("-").reverse().join("-"), value: Number(x.value)}));
Next thing is to use again Array#Map to replace day property to Month.
const mapDayToMonth = collection.map(x => ({...x, day: new Date(x.day).getMonth()}));
The getMonth() method returns the month (from 0 to 11) for the specified date, according to local time. So, 0 is January, 1 is February ...etc
Last step Array#Reduce to sum value per Month:
const sumPerMonth = mapDayToMonth.reduce((acc, cur) => {
acc[cur.day] = acc[cur.day] + cur.value || cur.value;
return acc;
}, {})
Full example JSFiddle:
const data = [ { day: '01-01-2018', hour: '00:00', value: '121' }, { day: '01-02-2018', hour: '05:24', value: '131' }, { day: '26-01-2018', hour: '00:07', value: '101' }, { day: '16-02-2018', hour: '08:02', value: '56' }, { day: '20-02-2018', hour: '12:02', value: '123' }, { day: '24-03-2018', hour: '10:11', value: '45' }];
const collection = data.map(x => ({ ...x, day: x.day.split("-").reverse().join("-"), value: Number(x.value)}));
console.log(collection);
const mapDayToMonth = collection.map(x => ({...x, day: new Date(x.day).getMonth()}));
console.log(mapDayToMonth);
const sumPerMonth = mapDayToMonth.reduce((acc, cur) => {
acc[cur.day] = acc[cur.day] + cur.value || cur.value; // increment or initialize to cur.value
return acc;
}, {})
console.log(sumPerMonth)
Useful links:
Array Map
Array Reduce
Number Object
Spread Syntax

Categories

Resources