Time difference in hh:mm format - javascript

I am calculating the difference between two times with the following function:
const calcTimeDiff = (time1: string, time2: string) => {
const timeStart = new Date()
const timeEnd = new Date()
const valueStart = time1.split(':')
const valueEnd = time2.split(':')
timeStart.setHours(+valueStart[0], +valueStart[1], 0, 0)
timeEnd.setHours(+valueEnd[0], +valueEnd[1], 0, 0)
const difference = timeEnd.getTime() - timeStart.getTime()
return format(difference, 'HH:mm') // date-fns
}
For example calcTimeDiff('08:45', '16:00') which should yield 07:15. However, I get 08:15 instead. My guess is that it is caused by timezone conflicts.
Debugging my code gave me the following insights:
console.log(difference, timeStart, timeEnd)
Thu Jan 01 1970 08:15:00 GMT+0100, Wed Aug 17 2022 08:45:00 GMT+0200, Wed Aug 17 2022 16:00:00 GMT+0200

Why not make use of the intervalToDuration method which is included in the date-fns library. This will return an object for you like below
{years: 0, months: 0, days: 0, hours...}
It can be implemented easily into your function like so:
const calcTimeDiff = (time1: string, time2: string) => {
const timeStart = new Date()
const timeEnd = new Date()
const valueStart = time1.split(':')
const valueEnd = time2.split(':')
timeStart.setHours(+valueStart[0], +valueStart[1], 0, 0)
timeEnd.setHours(+valueEnd[0], +valueEnd[1], 0, 0)
return intervalToDuration({ start: timeStart, end: timeEnd })
}

If you get string in hh:mm format, you don't need Date, just calculate:
const calcTimeDiff = (time1: string, time2: string) => {
const [h1, m1] = time1.split(':');
const [h2, m2] = time2.split(':');
let diff = (h2 - h1) * 60 + (m2 - m1);
if (diff < 0) diff += 24 * 60;
const hours = Math.floor(diff / 60);
const minutes = diff - hours * 60;
const hh = hours.toString().padStart(2, '0');
const mm = minutes.toString().padStart(2, '0');
return `${hh}:${mm}`;
}

I agree with #eugene anf thats the right way to go but in case you are looking for an answer in the format your code is written here is the code
const calcTimeDiff = (time1, time2) => {
const timeStart = new Date()
const timeEnd = new Date()
const valueStart = time1.split(':')
const valueEnd = time2.split(':')
timeStart.setHours(+valueStart[0], +valueStart[1], 0, 0)
timeEnd.setHours(+valueEnd[0], +valueEnd[1], 0, 0)
let difference = new Date();
difference.setHours(timeEnd.getHours() - timeStart.getHours())
difference.setMinutes(timeEnd.getMinutes() - timeStart.getMinutes())
return console.log(difference, timeStart, timeEnd) // date-fns
}
calcTimeDiff('08:45', '16:00');
In your code you are creating a date object using the difference in time which will lead you to a random time.

Solved it this way:
const calcTimeDiff = (time1: string, time2: string) => {
const timeStart = new Date()
const timeEnd = new Date()
const valueStart = time1.split(':')
const valueEnd = time2.split(':')
timeStart.setHours(+valueStart[0], +valueStart[1], 0, 0)
timeEnd.setHours(+valueEnd[0], +valueEnd[1], 0, 0)
const difference = new Date(timeEnd.getTime() - timeStart.getTime())
return format(
addMinutes(difference, difference.getTimezoneOffset()),
'HH:mm'
)
}

You can use moment.js to perform what you want.
This can be done using the diff method.
const calcTimeDiff = (time1, time2) => {
const start = moment(time1, 'HH:mm');
const end = moment(time2, 'HH:mm');
let diff = end.diff(start);
return moment.utc(diff).format("HH:mm");
}
console.log(calcTimeDiff("8:15", "16:15"))
console.log(calcTimeDiff("7:45", "19:21"))
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.29.4/moment.min.js"></script>

Related

Getting date range based on date and hour

I am trying to get individual dates ("2022-10-10") and hours ("2022-10-10T09") between an interval in UTC. I could get the individual dates by the following -
function getDatesInRange(startDate, endDate) {
const date = new Date(startDate.getTime());
const dates = [];
while (date <= endDate) {
const day = new Date(date).toISOString().split(':')[0].split('T')[0];
dates.push(day);
date.setDate(date.getDate() + 1);
}
return dates;
}
console.log(getDatesInRange(new Date('2022-10-10T20:50:59.938Z'), new Date('2022-10-15T23:50:59.938Z')));
Hence, the above returns - ["2022-10-10", "2022-10-11", "2022-10-12", "2022-10-13", "2022-10-14", "2022-10-15"]
I also want to return the hours of the start and end date and the rest should be dates. So i want to get in return - ["2022-10-10T20", "2022-10-10T21", "2022-10-10T22", "2022-10-10T23" "2022-10-11", "2022-10-12", "2022-10-13", "2022-10-14", "2022-10-15T00", "2022-10-15T01"]
Here is what i have as of now -
function getHoursInRange(startDate, endDate) {
let startDatePlusOne = new Date(startDate);
startDatePlusOne.setDate(startDatePlusOne.getDate() + 1);
let endDateMinusOne = new Date(endDate);
endDateMinusOne.setDate(endDateMinusOne.getDate() - 1);
const date = new Date(startDate.getTime());
console.log("Start date :", date);
let dates = getDatesInRange(startDatePlusOne, endDateMinusOne);
console.log("Only days : ", dates);
startDatePlusOne.setHours(0);
while (date < startDatePlusOne) {
const day = new Date(date).toISOString().split(':')[0];
dates.push(day);
date.setHours(date.getHours() + 1);
}
endDateMinusOne.setHours(23);
const edate = endDateMinusOne.getTime();
while (edate < endDate) {
const day = new Date(edate).toISOString().split(':')[0];
dates.push(day);
date.setHours(date.getHours() + 1);
}
return dates
}
For this use case, i am getting the days back excluding the start and end dates. But for getting each hour of start and end date it gets stuck somehow. Somehow i feel there is a better way to do this. Any ideas ?
You can do it a simpler way by incrementing the timestamp by 30 minutes at a time, and keeping a note of all non-duplicate hour strings and date strings:
function getDatesInRange(startDate, endDate) {
let h = new Set(), d = new Set(), t = [];
for(let i=startDate.getTime(); i<endDate.getTime(); i+=1000*1800) t.push(i);
[...t, endDate.getTime()].forEach(i=>{
let s = new Date(i).toISOString();
[[s.split(':')[0], h], [s.split('T')[0], d]].forEach(([s,r])=>r.add(s));
});
let firstDate = [...d.values()][0], lastDate = [...d.values()].pop();
return d.size===1 ? [...h.values()] : [
...[...h.values()].filter(v=>v.startsWith(firstDate)),
...[...d.values()].filter(v=>v!==firstDate && v!==lastDate),
...[...h.values()].filter(v=>v.startsWith(lastDate))];
}
console.log(getDatesInRange(
new Date('2022-10-10T20:50:59.938Z'), new Date('2022-10-15T23:50:59.938Z')));
dateRange constructs an array of Date objects corresponding to the supplied range, inclusive.
dayToString takes a date and creates an array of strings, one for each hour of the day between the specified UTC hour range, inclusive.
dateRangeToStrings accepts an array of dates and constructs an array of strings according to the rules laid-out in the question.
const twoDigit = (n) => String(n).padStart(2, '0')
const toISODateString = (date) => `${date.getUTCFullYear()}-${twoDigit(date.getUTCMonth() + 1)}-${twoDigit(date.getUTCDate())}`
const dateRange = (start, end, curr = new Date(start)) => {
const dates = []
while (curr <= end) {
dates.push(new Date(Date.UTC(curr.getUTCFullYear(), curr.getUTCMonth(), curr.getUTCDate())))
curr.setUTCDate(curr.getUTCDate() + 1)
}
return dates
}
const dayToString = (date, startUTCHour = 0, endUTCHour = 23) =>
Object.keys([...Array(24)])
.slice(startUTCHour, endUTCHour + 1)
.map((h)=>`${toISODateString(date)}T${twoDigit(h)}`)
const dateRangeToStrings = (arr, startUTCHour, endUTCHour) => {
const beginning = dayToString(arr[0], startUTCHour)
const middle = arr.slice(1, -1).map(toISODateString)
const end = dayToString(arr[arr.length - 1], 0, endUTCHour)
return beginning.concat(middle, end)
}
const getDatesInRange = (start, end) =>
dateRangeToStrings(dateRange(start, end),
start.getUTCHours(),
end.getUTCHours())
console.log(getDatesInRange(new Date('2022-10-10T20:50:59.938Z'),
new Date('2022-10-15T23:50:59.938Z')))

Calculate duration by start and end hour

I have this json :
{
endTime: "14:00:00"
startTime: "12:00:00"
}
I need to calculate duration, so I did like this :
let duration = endTime.slice(0, -3) - startTime.slice(0, -3);
But not working as expected. I have a js error : left-hand must be type number
Have an idea about that ?
Thx in advance.
Ok, I'm considering you are only receiving an object with endTime and startTime properties and not working with arrays.
In the following code block, you can transform your strings into dates and do calcs with them. In this example, I just subtracted endDate - startDate to get the difference in milliseconds and then I converted to seconds, minutes and hours.
const data = {
endTime: '14:00:00',
startTime: '12:00:00',
}
// separates the string in hours, minutes and seconds
const [startHours, startMinutes, startSeconds] = data.startTime.split(':')
const [endHours, endMinutes, endSeconds] = data.endTime.split(':')
// creates a Date instance to work with
const startDate = new Date()
const endDate = new Date()
// sets hour, minutes and seconds to startDate
startDate.setHours(startHours)
startDate.setMinutes(startMinutes)
startDate.setSeconds(startSeconds)
// sets hour, minutes and seconds to endDate
endDate.setHours(endHours)
endDate.setMinutes(endMinutes)
endDate.setSeconds(endSeconds)
const differenceInMilliseconds = endDate - startDate
const differenceInSeconds = differenceInMilliseconds / 1000
const differenceInMinutes = differenceInSeconds / 60
const differenceInHours = differenceInMinutes / 60
console.log(differenceInHours) // outputs 2 hours
Too many ways to do that, this is one of the simple ways.
Cast the time to a Date object, then get their timestamp (ms), finally get the duration:
const startTimeTs = new Date(`2021-04-01 ${startTime}`).valueOf();
const endTimeTs = new Date(`2021-04-01 ${endTime}`).valueOf();
const durationTs = endTimeTs - startTimeTs;
const durationInSecondes = durationTs / 1000;
const durationInMinutes = durationInSecondes / 60;
const durationInHours = durationInMinutes / 60;
const json = {
endTime: "14:00:00",
startTime: "12:00:00"
};
const start = new Date(2000, 3, 3, ...(json.startTime.split(':').map( x => Number(x))));
const end = new Date(2000, 3, 3, ...(json.endTime.split(':').map( x => Number(x))));
const output = document.getElementById('output');
output.textContent = ((end-start)*0.001)+ ' seconds difference';
<div id="output"></div>
There to many ways to do it, but if you need in same format as you got in json you can use something like it
const data = {
endTime: "14:00:00",
startTime: "12:00:00"
};
const { endTime, startTime } = data;
const endTimeArr = endTime.split(':').map(el => +el);
const startTimeArr = startTime.split(':').map(el => +el);
const resArr = endTimeArr.map((el, i) => el - startTimeArr[i]);
const res = resArr.join(':');
You can do it like this:
let times = {
endTime: "14:00",
startTime: "12:00:00"
};
function calculate(obj) {
let startTime = obj.startTime;
let endTime = obj.endTime;
let sum = new Date(parseInt(endTime)) - new Date(parseInt(startTime));
return sum;
}
console.log(calculate(times));
Convert the HH:MM:SS to seconds.
Subtract the two values - you get the seconds of duration.
Convert the seconds to HH:MM:SS.
/* helper functions */
const format = n =>
String(n).padStart(2, 0);
const time2seconds = time => {
const [hours, minutes, seconds] = time.split(":").map(Number);
return seconds + minutes * 60 + hours * 60 * 60;
}
const seconds2time = seconds => {
const hours = format(Math.floor(seconds / (60 * 60)));
const minutes = format(Math.floor(seconds / 60) % 60);
seconds = format(seconds % 60);
return `${hours}:${minutes}:${seconds}`;
}
/* /helper functions */
const toDuration = ({startTime, endTime}) =>
seconds2time(time2seconds(endTime) - time2seconds(startTime));
test({
endTime: "14:00:00",
startTime: "12:00:00"
})
test({
endTime: "15:30:00",
startTime: "11:00:00"
})
test({
endTime: "18:24:05",
startTime: "11:47:12"
})
function test(obj) {
const result = toDuration(obj);
console.log(`duration between ${obj.startTime} and ${obj.endTime} is: ${result}`);
}

Fill in empty blocks for each calendar month JS

I am currently able to fetch the given days of the current month as well as previous and future months using JS. What I would like to achieve, is if say December starts on a Tuesday I would like to pass in empty objects for Sunday and Monday. December also ends on a Thursday, so I would like to pass in empty objects for Friday and Saturday.
The code I am currently using to fetch each calendar month and display them is as follows:
import React, { useEffect, useState, useCallback } from "react";
import "./styles.css";
export default function App() {
const [visibleMonth, setVisibleMonth] = useState(new Date().getMonth());
const [calData, setCalData] = useState(null);
const [dates, setDates] = useState(null);
const getDaysArray = async (s, e) => {
let a = [];
for (let d = new Date(s); d <= e; d.setDate(d.getDate() + 1)) {
a.push(new Date(d).toString());
}
return a;
};
const currentMonth = useCallback(async () => {
let d = new Date();
d.setMonth(visibleMonth);
let firstDay = new Date(d.getFullYear(), d.getMonth(), 1);
let lastDay = new Date(firstDay.getFullYear(), firstDay.getMonth() + 1, 0);
let calendarMonth = d.toLocaleString("en-us", {
month: "long",
year: "numeric"
});
setCalData(calendarMonth);
const dates = await getDaysArray(firstDay, lastDay);
setDates(dates);
}, [visibleMonth]);
useEffect(() => {
currentMonth();
}, [currentMonth]);
const prevMonth = async () => {
let d = new Date();
d.setMonth(visibleMonth - 1);
setVisibleMonth((state) => visibleMonth - 1);
let firstDay = new Date(d.getFullYear(), d.getMonth(), 1);
let lastDay = new Date(firstDay.getFullYear(), firstDay.getMonth() + 1, 0);
let calendarMonth = d.toLocaleString("en-us", {
month: "long",
year: "numeric"
});
setCalData(calendarMonth);
const dates = await getDaysArray(firstDay, lastDay);
setDates(dates);
};
const nextMonth = async () => {
let d = new Date();
d.setMonth(visibleMonth + 1);
setVisibleMonth((state) => visibleMonth + 1);
let firstDay = new Date(d.getFullYear(), d.getMonth(), 1);
let lastDay = new Date(firstDay.getFullYear(), firstDay.getMonth() + 1, 0);
let calendarMonth = d.toLocaleString("en-us", {
month: "long",
year: "numeric"
});
setCalData(calendarMonth);
const dates = await getDaysArray(firstDay, lastDay);
setDates(dates);
};
return (
<div className="App">
<h1>{calData}</h1>
<button onClick={prevMonth}>Prev Month</button>
<button onClick={nextMonth}>Next Month</button>
{dates &&
dates.map((item, i) => {
return <div key={i}>{item}</div>;
})}
</div>
);
}
the reason I am attempting to do so is due to the grid I have created. As it stands now, every month starts at sunday and I am not successfully pairing the correct days of the month with the days on the calendar. For example december starts on a tuesday, yet my styling shows it starts on a sunday any help would be greatly appreciated. the console logs currently show the days each month should start and end on.
Attached is a code pen for debugging! https://codesandbox.io/s/heuristic-visvesvaraya-r9lcw?file=/src/App.js
Based upon your most recent comments and updates, I recommend the following changes to the getDaysArray() method:
Create the d date outside the loop in order to use getDay() (day of week #)
Fill the a[] with empty strings with the number of day of week # in a for loop
Finally, populate the date strings into the remainder of the a[] array.
That should do it:
const getDaysArray = async (s, e) => {
let a = [];
let d = new Date(s);
let emptyCount = d.getDay();
for(let i = 0; i < emptyCount; i++) {
a.push('');
}
for (d; d <= e; d.setDate(d.getDate() + 1)) {
a.push(new Date(d).toString());
}
return a;
};

Calculate the number of weekday and weekend hours between 2 timestamp params in javascript

I want to calculate the number of weekday-hours and weekend-hours within a given 2 timestamp range. It is sure the timestamp ranges are in hours, it doesn't contain minutes or seconds values in it.
attempt:
function getWeekdayWeekendHours(fromTimestamp, tillTimestamp) {
let weekdayhours = 0;
let weekendhours = 0;
// fix the GMT issue by decreasing timestamp by 5:30
const fromTime = fromTimestamp - 19800;
const tillTime = tillTimestamp - 19800;
let currentDate = new Date(fromTime * 1000);
const tillDate = new Date(tillTime * 1000);
while (currentDate < tillDate) {
if (currentDate.getDay() !== 0 && currentDate.getDay() !== 6)
weekdayhours += 1;
else weekendhours += 1;
currentDate = currentDate.addHours(1);
}
return { weekdayhours, weekendhours };
}
// eslint-disable-next-line no-extend-native
Date.prototype.addHours = function (h) {
this.setHours(this.getHours() + h);
return this;
};
Here's a function that should work, provided you don't mind using lodash & moment.
const moment = require("moment")
const lodash = require("lodash")
function getHours(ts1, ts2) {
const weekends = [6, 7] // the isoWeekday of saturday & sunday
const [m1, m2] = [moment(ts1), moment(ts2)].sort() // makes sure earlier ts is first
const numDays = Math.ceil(m2.diff(m1, "days", true)) + 1
let weekdayHrs = 0
let weekendHrs = 0
lodash.range(numDays).forEach(idx => {
let diffHours = 0
let start
let end
// figure out start, end timestamps
switch (idx) {
case 0:
start = m1
end = m1.clone().add(1, "days").hours(0).minutes(0).seconds(0).milliseconds(0)
break
case numDays - 1:
end = m2
start = m2.clone().hours(0).minutes(0).seconds(0).milliseconds(0)
break
default:
start = m1.clone().hours(0).minutes(0).seconds(0).milliseconds(0).add(idx, "days")
end = start.clone().add(1, "days")
end = end.isBefore(m2) ? end : m2
break
}
diffHours = end.diff(start, "hours")
const dayOfWeek = start.isoWeekday()
const isWeekend = weekends.includes(dayOfWeek)
if (isWeekend) {
weekendHrs += diffHours
} else {
weekdayHrs += diffHours
}
// you can remove these 2 lines from the function. This just prints the total per day.
const names = ["mon", "tue", "wed", "thu", "fri", "sat", "sun"]
console.log(idx, names[dayOfWeek - 1], start.format("MMM/DD hh:mm A"), "to", end.format("MMM/DD hh:mm A"), "=", diffHours, isWeekend ? "weekend hrs": "hrs")
})
return { weekdayHrs, weekendHrs, total: weekdayHrs + weekendHrs }
}
Here is some example output:
const ts1 = new Date(2019, 9, 18) // Oct 18 2019 12:00 AM
const ts2 = new Date(2019, 9, 25) // Oct 25 2019 12:00 AM
console.log(getHours(ts1, ts2))
// output:= { weekdayHrs: 120, weekendHrs: 48, total: 168 }
const ts3 = new Date(2019, 9, 18, 10) // Oct 18 2019 10:00 AM
const ts4 = new Date(2019, 9, 22, 13) // Oct 22 2019 1:00 PM
console.log(getHours(ts3, ts4))
// output:= { weekdayHrs: 64, weekendHrs: 48, total: 112 }

How to return number of dates that are before today, today and tomorrow from array

I have array full of dates. I would like to get 3 separate information from this one array.
Number of dates before today. To be more specific dates before now, so I need to include hours, minutes and seconds.
Number of dates between now and the end of the current date
Number of dates with tomorrow date
Example date from array:
"02/11/2019 11:22:28",
"05/14/2019 12:17:49",
"06/06/2019 09:00:00",
"05/02/2019 11:05:30"
I believe that moment.js could help here, but I am unable to make it work.
I don't think you need to import a JS framework to do this.
You can compare Dates in plain JavaScript.
const arrayOfDates = ["02/11/2019 11:22:28", "02/11/2020 11:22:28",
"04/03/2019 11:33:54"];
const TODAY = new Date();
const END_OF_TODAY = new Date(TODAY.getFullYear(), TODAY.getMonth(), TODAY.getDay(), 24, 59, 59);
const END_OF_TOMORROW = new Date(TODAY.getFullYear(), TODAY.getMonth(), TODAY.getDay() + 1, 24, 59, 59);
let datesBeforeToday = [];
let datesBetweenNowAndEndOfDate = [];
let datesWithTomorrowsDate = [];
for (var i=0; i<arrayOfDates.length; i++)
{
var dateInArray = new Date(arrayOfDates[i]);
// Before today
if (TODAY > dateInArray)
{
datesBeforeToday.push(dateInArray);
}
// between now - end of today
else if (END_OF_TODAY >= dateInArray)
{
datesBetweenNowAndEndOfDate.push(dateInArray);
}
// between end of today - end of tomorrow
else if (END_OF_TOMORROW >= dateInArray)
{
datesWithTomorrowsDate.push(dateInArray);
}
}
// 1) Number of dates before 'now'
console.log(datesBeforeToday.length);
// 2) Number of dates between 'now' and the end of the current date
console.log(datesBetweenNowAndEndOfDate.length);
// 3) Number of dates with tomorrow date
console.log(datesWithTomorrowsDate.length);
This is one way to do it without using moment.js:
var dates = [
"11/02/2018 11:22:28",
"02/02/2019 11:22:28",
"04/04/2019 11:22:28",
"10/03/2019 11:22:28",
"11/02/2019 11:22:28"
];
var res = { before: 0, tomorrow: 0, after: 0 }
var tomorrow00 = new Date(new Date().getTime() + 24 * 60 * 60 * 1000);
tomorrow00.setHours(0, 0, 0, 0);
var tomorrow24 = new Date(new Date().getTime() + 48 * 60 * 60 * 1000);
tomorrow24.setHours(0, 0, 0, 0);
tomorrow24.setTime(tomorrow24.getTime() - 1)
dates.forEach((date)=>{
var current = new Date(date);
if ((current >= tomorrow00) && (current <= tomorrow24))
res.tomorrow++;
else if (current < Date.now())
res.before++;
else if (current > Date.now())
res.after++
})
console.log(res);
This is a way to solving USING moment.js
var dates = [
"11/02/2018 11:22:28",
"02/02/2019 11:22:28",
"04/04/2019 11:22:28",
"10/03/2019 11:22:28",
"11/02/2019 11:22:28"
];
var res = { before: 0, tomorrow: 0, after: 0 };
const tomorrow00 = moment().add(1, 'day').startOf('day');
const tomorrow24 = moment().add(1, 'day').endOf('day');
dates.forEach((date)=>{
var current = moment(date, "MM/DD/YYYY HH:mm:ss");
if ((current.isAfter(tomorrow00)) && (current.isBefore(tomorrow24)) )
res.tomorrow++;
else if (current.isBefore(moment().startOf('day')))
res.before++;
else if (current.isAfter(moment().endOf('day')))
res.after++
})
console.log(res);
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.24.0/moment.min.js"></script>

Categories