British Summer Time with jQuery Datepicker (calculation lost 1 day) - javascript

I use Vue.js with jQuery Datepicker and see the issue with calculation of how many days are left to the selected date:
Calculation the difference in days:
getDifferenceInDays: function(date1, date2) {
const diffTime = date2.getTime() - date1.getTime(); //Math.abs(...);
const diffDays = Math.ceil(diffTime / (1000 * 60 * 60 * 24));
return diffDays;
},
As a result I don't see 13 days left. Perhaps the issue related to the British Summer Time which ends on 27th October:
Could you suggest any idea how to fix this issue.
Many thanks!

You're right, this is caused by a Daylight Saving Time (DST) transition, specifically the end of DST in the UK on the 27th of October.
Dates, Times and Timezones are really tricky in JavaScript. The native Date object is very awkward to deal with when we're accessing different timezones.
I would suggest checking out a library that is better at this kind of thing: moment and moment-timezone are very good at handling DST changes correctly.
The reason you're getting the incorrect result is that you're looking at the difference between dates in the UTC timezone (since getTime() always returns the number of milliseconds since 1970-01-01 00:00 UTC (the Unix Epoch)).
The result (in your case) will be 13.04 (or 13 + 1/24) days since the UTC offset changes between the two dates (from UTC+01 to UTC+00).
For example, 12:00 on the 14th of October London Time is 11:00 UTC,
while 12:00 on the 27th of October London Time is 12:00 UTC. So the difference will be 313 hours, or 13.04 days.
Since you're calling Math.ceil, this will round up to 14, which is not correct.
This works when both dates are within the same UTC offset (e.g. both in summer, both in winter). But then they are either side of a DST change, you will get an incorrect result.
The code below will calculate the days between two dates in the specified timezone. You can pass a third argument of true to moment.diff to get the fractional days. (This will be 0 in this case).
If you don't wish to use another library such as moment, you can simply use Math.round to get the approximate difference in days. This will not be as robust, since if date2 is less than 12 hours ahead of date1 you'll get zero days difference.
As long as you're aware of this, it may suffice for your needs. I've added a getDifferenceInDaysRounding to show this.
const CURRENT_TIMEZONE = "Europe/London";
// Date 1 is 12:00, 14th October, London time
const date1 = moment.tz("2019-10-14T12:00:00", CURRENT_TIMEZONE).toDate();
// Date 2 is 12:00, 27th October, London time
const date2 = moment.tz("2019-10-27T12:00:00", CURRENT_TIMEZONE).toDate();
function getDifferenceInDaysOriginal (date1, date2) {
const diffTime = date2.getTime() - date1.getTime();
let diff1 = diffTime / (1000 * 60 * 60 * 24);
const diffDays = Math.ceil(diffTime / (1000 * 60 * 60 * 24));
return diffDays;
}
// Use with caution.. this will not work in all cases!
function getDifferenceInDaysRounding (date1, date2) {
const diffTime = date2.getTime() - date1.getTime();
const diffDays = Math.round(diffTime / (1000 * 60 * 60 * 24));
return diffDays;
}
function getDifferenceInDaysTimezoneAware(date1, date2, timezone) {
// Ensure the dates are in the correct timezone.
const dateLocal1 = moment.tz(date1.getTime(), timezone);
const dateLocal2 = moment.tz(date2.getTime(), timezone);
// Pass false to return the integer number of days. NB, this will be 0 for less than 24 hours difference. Pass true to get the fractional days.
return dateLocal2.diff(dateLocal1, "days", false);
}
console.log("Original result:", getDifferenceInDaysOriginal(date1, date2));
console.log("Get difference (rounding):", getDifferenceInDaysRounding(date1, date2));
console.log("Timezone-aware result:", getDifferenceInDaysTimezoneAware(date1, date2, CURRENT_TIMEZONE));
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.24.0/moment.min.js"></script>
<script src="https://momentjs.com/downloads/moment-timezone-with-data-1970-2030.js"></script>

Related

How to correctly calculate difference in days between 2 dates with DST change?

I have a task to get days difference between 2 dates. My solution is like here https://stackoverflow.com/a/543152/3917754 just I use Math.ceil instead of Math.round - cause 1 day and something is more than 1 day.
It was fine until I got dates between DST change. For example:
In my timezone DST change was on 30 Oct.
So when I'm trying to find days diff between dates 20 Oct and 10 Nov in result I get 23 instead of 22.
There are solution how to identify DST date but is it good solution to add/substract 1 day if date is/isn't dst?
function datediff(toDate, fromDate) {
const millisecondsPerDay = 1000 * 60 * 60 * 24; // milliseconds in day
fromDate.setHours(0, 0, 0, 0); // Start just after midnight
toDate.setHours(23, 59, 59, 999); // End just before midnight
const millisBetween = toDate.getTime() - fromDate.getTime();
var days = Math.ceil(millisBetween / millisecondsPerDay);
return days;
}
var startDate = new Date('2022-10-20');
var endDate = new Date('2022-11-10');
console.log('From date: ', startDate);
console.log('To date: ', endDate);
console.log(datediff(endDate, startDate));

Rounding date by multiple weeks

It is easy to round dates to the start of the current week like so:
function roundWeek() {
var current = new Date();
var startOfWeek = current.getDate() - current.getDay();
console.log(new Date(current.setDate(startOfWeek)));
}
roundWeek();
This works fine when rounding by one week, but it gets a bit more complex when rounding by multiple weeks.
For instance, say I would like to round in three-week periods. I know this would require an epoch (such as new Date(0)) to be able to properly calculate the starting date of the three-week period, but this too causes an issue:
January 1, 1970 (which is new Date(0)) occured on a Thursday, so assuming I used it in the following manner, it would always calculate the start of the week to be on a Thursday:
function roundWeek(weeks) {
var current = new Date();
const WEEK_IN_MS = 60*60*24*7*1000; //sec * min * hr * day * ms
var index = Math.floor( (current.getTime() / (WEEK_IN_MS*weeks)) );
var startOfPeriod = new Date( index * WEEK_IN_MS*weeks );
console.log( startOfPeriod );
}
roundWeek(3);
How can this be modified so that it properly starts on a Sunday instead of a Thursday? Is there a better way of doing this? And most importantly: Would a leap year cause issues with this?
You need to decide whether you want the three-week period to start the Sunday before Jan 1, 1970 (12/28/69) or after. For example, which Sunday would Jan 14, 1970 round to? Then you can calculate the difference between Thursday and either the previous Sunday or next Sunday and add or subtract (depending on the decision you made) that to your rounded solution.
Here's a variation of you function that also takes a date as input for testing.
function roundWeek(weeks, d) {
var current = new Date(d);
var first_sunday = 60 * 60 * 24 * 4 * 1000 // seconds between Sunday and Thursday
const WEEK_IN_MS = 60 * 60 * 24 * 7 * 1000; // week in ms
var index = Math.floor((current.getTime() / (WEEK_IN_MS * weeks)));
console.log(`${weeks}-week periods from ${d.getMonth()+1}/${d.getDate()}/${d.getFullYear()} ${index}`)
var startOfPeriod = new Date(index * WEEK_IN_MS * weeks - first_sunday);
console.log("1st day of period:", startOfPeriod);
}
// round to three weeks starting Sunday before Jan 1 1970
roundWeek(3, new Date('January 2, 1970')); // Dec 28, 1968
roundWeek(3, new Date('January 14, 1970')); // Dec 28, 1968
roundWeek(3, new Date('January 22, 1970')); // next three weeks
roundWeek(3, new Date('May 16, 2018')); // since today. Sundaay May 6
Since leap years don't change how long a week is, they should be irrelevant.

Invalid date issue using new Date()

I am passing timestamp to below function and it is returning me date string properly but when i am executing below line it is giving me error invalid date.
var postDate = new Date(this.ConvertServerDateToLocal(timestamp));
//postDate returns invalid date object.
ConvertServerDateToLocal: function(dateInput){
// EST - UTC offset: 4 hours
var offset = 4.0,
/*
- calculate the difference between the server EST date and UTC
- the value returned by the getTime method is the number of milliseconds since 1 January 1970 00:00:00 UTC.
- the time-zone offset is the difference, in minutes, between UTC and local time
- 60000 milliseconds = 60 seconds = 1 minute
*/
serverDate = new Date(dateInput),
utc = serverDate.getTime() - (serverDate.getTimezoneOffset() * 60000),
/*
- apply the offset between UTC and EST (4 hours)
- 3600000 milliseconds = 3600 seconds = 60 minutes = 1 hour
*/
clientDate = new Date(utc + (3600000 * offset));
return clientDate.toLocaleString();
}
Below is example timestamp i am passing to ConvertServerDateToLocal() function.
timestamp = "Nov 22, 2017 23:05:58" // Throwing invalid date after output
timestamp = "Nov 09, 2017 18:30:19" // Working properly
function ConvertServerDateToLocal(dateInput) {
// EST - UTC offset: 4 hours
dateInput = "Nov 09, 2017 18:30:19";
var offset = 4.0,
/*
- calculate the difference between the server EST date and UTC
- the value returned by the getTime method is the number of milliseconds since 1 January 1970 00:00:00 UTC.
- the time-zone offset is the difference, in minutes, between UTC and local time
- 60000 milliseconds = 60 seconds = 1 minute
*/
serverDate = new Date(dateInput.toString());
utc = serverDate.getTime() - (serverDate.getTimezoneOffset() * 60000);
/*
- apply the offset between UTC and EST (4 hours)
- 3600000 milliseconds = 3600 seconds = 60 minutes = 1 hour
*/
clientDate = new Date(utc + (3600000 * offset));
//return clientDate.toLocaleString();
alert(clientDate.toLocaleString());
}
Converting the input paramter to string worked for me. But I am not sure why one date worked and the other didn't work. If you found the answer, please post it here. I also use Dates a lot and they are always a pain.

Javascript - unexpected behavior in dates calculations

I'm a newbie and recently started to read Beginning Javascript, by McPeak and Wilton. The authors propose an exercise about dates calculation. This is the exercise
Using the Date type, calculate the date 12 months from now.
I tried to solve it with this code
//gets today's date
var today = new Date();
//this line transforms the date in milliseconds
var daysAsMilliseconds = 1000* 60 * 60 * 24 * today.getDate();
//creates a new Date object
console.log(new Date(today.setDate(365) + daysAsMilliseconds));
The result I get here is correct(August 11th 2018).
Later, I wonder if it was really necessary to create 2 variables and tried this solution:
var today = new Date();
console.log(new Date(today.setDate(365) + (1000 * 60 * 60 * 24 * today.getDate())));
Here the solution was incorrect. The console showed August 31 2018. Why?
If necessary, here you will find the repl.it with the code
You call setDate, before you call getDate , therefore getDate will always return 365. Simply swapp it:
new Date((1000 * 60 * 60 * 24 * today.getDate()) + today.setDate(365))
Or its may easier to work with months directly:
today.setMonth(today.getMonth() + 12);
var intwelvemonths = today;
All you need to do is add 1 to the year:
var yearFromNow = new Date();
yearFromNow.setYear(yearFromNow.getFullYear() + 1);
Setting the date to 365 makes no sense; .setDate() is for day-of-month, so setting it to that constant moves the date a year (usually) from the last day of the previous month. And you don't need to do any other math outside of the date API; just increment the year, and you're done.
You're calling today.setDate(365) before you're adding the results of today.getDate(): today.getDate() will give the date that you set, not today's date.
Changing the order of operations will do the trick:
var today = new Date();
new Date((1000 * 60 * 60 * 24 * today.getDate()) + today.setDate(365));
I recommend you to use a package as moment.js because it manage a lot of date formats, and it has very good implementations for date managing.
Using moment js for add.
moment().add(Number, String);
Example
var m = moment(new Date(2011, 2, 12, 5, 0, 0));
m.hours(); // 5
m.add(1, 'days').hours(); // 5
For more docs see moment().add() docs

Javascript: Calculate time difference between 2 days with conversion to UTC

I have a start and end dates, I need to convert them to UTC and calculate how many days are in between (including).
So for example:
(01/08/15 10:00 GMT+3) - (04/08/15 10:00 GMT+3) will return 4
(01/08/15 00:00 GMT+3) - (04/08/15 10:00 GMT+3) will return 5
The following code works for those dates like the first case, but not for the second (where after the conversion there is an additional day):
var startDateInUTC = new Date(start.getUTCFullYear(), start.getUTCMonth(), start.getUTCDate(), start.getUTCHours(), start.getUTCMinutes(), start.getUTCSeconds());
var endDateInUTC = new Date(end.getUTCFullYear(), end.getUTCMonth(), end.getUTCDate(), end.getUTCHours(), end.getUTCMinutes(), end.getUTCSeconds());
var totalDays = Math.floor((endDateInUTC - startDateInUTC) / (1000 * 60 * 60 * 24)) + 1;
I tried changing the Math.floor to Math.round but that just adds me a day in some scenarios.
What am I doing wrong?
function calculate(start, end)
{
var startDateInUTC = new Date(start.getUTCFullYear(), start.getUTCMonth(), start.getUTCDate(), start.getUTCHours(), start.getUTCMinutes(), start.getUTCSeconds());
var endDateInUTC = new Date(end.getUTCFullYear(), end.getUTCMonth(), end.getUTCDate(), end.getUTCHours(), end.getUTCMinutes(), end.getUTCSeconds());
return Math.floor(millisecondsToDays = (Date.parse(endDateInUTC) - Date.parse(startDateInUTC)) / 1000 / 3600 / 24);
}
console.log(calculate(new Date("2015/08/01 10:00:00"), new Date("2015/08/04 10:00:00")));
console.log(calculate(new Date("2015/08/01 00:00:00"), new Date("2015/08/04 10:00:00")));
//the answer in both cases will be 3
Use Date.parse here. It will convert the dates into timeStamps. you can subtract these and then calculate the amount back to days. Use Math.floor to round down, since 6.25 is 6 days and 6 hours.
timeStamps are the amount of milliseconds that have passed since 1970/01/01 00:00:00. That date is always UTC. When you have two timestamps you can calculate the difference between them. Date.parse() returns the timestamp on a valid date. new Date(timestamp) will return the date based upon the timestamp.
To get date barriers you can do an extra calculation:
(start time + 24 * days + end time) / 24
Round this figure down and you get the day barriers.
Example:
21 + 24 * 3 + 7 = 100
103 / 24 = 4.1666666.....
Math.floor(4.166666) = 4;
I ended up gathering a pretty simple solution combining some bits of Mouser's answer (Thanks!)
function calcStreamDaysInUTC(start, end) {
try {
// Translate to UTC
var startDateInUTC = new Date(start.getUTCFullYear(), start.getUTCMonth(), start.getUTCDate(), start.getUTCHours(), start.getUTCMinutes(), start.getUTCSeconds());
var endDateInUTC = new Date(end.getUTCFullYear(), end.getUTCMonth(), end.getUTCDate(), end.getUTCHours(), end.getUTCMinutes(), end.getUTCSeconds());
// Reset everything but the date
startDateInUTC.setHours(0);
startDateInUTC.setMinutes(0);
startDateInUTC.setSeconds(0);
endDateInUTC.setHours(0);
endDateInUTC.setMinutes(0);
endDateInUTC.setSeconds(0);
var totalDays = (endDateInUTC - startDateInUTC) / (1000 * 60 * 60 * 24) + 1;
return totalDays;
} catch (e) {
return -1;
}
}

Categories

Resources