-
-
Notifications
You must be signed in to change notification settings - Fork 2.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add days across Daylight Saving Time #1271
Comments
This is an important issue for us, but there is a workaround possible...
export const addDaysExtended = (dateWithTZ, days) => {
const clone = dayjs(dateWithTZ);
const tz = clone.$x.$timezone;
return clone.date(clone.date() + days).tz(tz, true);
};
const date1 = dayjs.tz('2020-10-24', 'Europe/London').add(2, 'day').toISOString();
const date2 = dayjs.tz('2020-10-26', 'Europe/London').toISOString();
const date3 = addDaysExtended(dayjs.tz('2020-10-24', 'Europe/London'), 2).toISOString();
console.log({ date1, date2, date3 });
// {
// date1: '2020-10-25T23:00:00.000Z',
// date2: '2020-10-26T00:00:00.000Z',
// date3: '2020-10-26T00:00:00.000Z'
// } |
I suppose this is the reason why I get wrong dates using .startOf()/.endOf() I am calculating the "this year" time period as follows: from = dayjs().tz(timeZone, true).startOf('year'); This used to work before the daylight saving time was applied, but right now it produces wrong results: |
refs https://github.com/TryGhost/Team/issues/588 - date-fns proved to be unable to manipulate dates consistently in UTC timezone. Keeping all calculations and formatting in UTC is key to have consistency in dates when dealing in inter-system dates - day.js also failed the test for correct UTC manipulation. See iamkun/dayjs#1271 for example bug which prevents from consistent correct calculation - luxon was the best option which WORKED. It's also a recommended successor for moment.js with really nice docs and active support
We completely switched our project to dayjs and at the end noticed that dayjs does not support the daylight saving time. I can only warn against using this library. Operations such as add cause unexpected results. As soon as a calculation goes over the limit of the time change, the result is wrong. We will continue to use moment.js. This library can only be used in countries without daylight saving time. Example: dayjs('25-10-2020', 'DD-MM-YYYY') result = -3600000 |
Running into this issue as well, where I'm trying to calculate the start of a given isoWeek. When crossing DST, the calculation starts returning strange results. |
We are seeing this issue too. Migrating from |
FYI, I have created a dayjs plugin to solve this issue. The code is quite simple and largely inspired by moment-timezone. We will update it regularly and we used it already in production. Be careful, const date1 = dayjs('2020-10-24').tz('Europe/London').add(2, 'day').toISOString();
const date2 = dayjs('2020-10-26').tz('Europe/London').toISOString(); |
Looks like dayjs used to behave this way but was "fixed" in #586. |
refs https://github.com/TryGhost/Team/issues/588 - date-fns proved to be unable to manipulate dates consistently in UTC timezone. Keeping all calculations and formatting in UTC is key to have consistency in dates when dealing in inter-system dates - day.js also failed the test for correct UTC manipulation. See iamkun/dayjs#1271 for example bug which prevents from consistent correct calculation - luxon was the best option which WORKED. It's also a recommended successor for moment.js with really nice docs and active support
refs https://github.com/TryGhost/Team/issues/588 - date-fns proved to be unable to manipulate dates consistently in UTC timezone. Keeping all calculations and formatting in UTC is key to have consistency in dates when dealing in inter-system dates - day.js also failed the test for correct UTC manipulation. See iamkun/dayjs#1271 for example bug which prevents from consistent correct calculation - luxon was the best option which WORKED. It's also a recommended successor for moment.js with really nice docs and active support
Still seems to behave this way |
Any update on this? Still behaving like this:
|
I just opened an issue that looks similar to this. |
Hello everyone, I'd like to inform you that my team is currently in the process of transitioning from using "moment" to "dayjs". Our objective is to calculate the addition of days within a specific timezone while ensuring that the hour remains consistent even during daylight saving periods. We've noticed that the dayjs ".add" function essentially appends a certain amount of time. Although it appears to handle hour changes correctly, we've encountered issues with the accuracy of the returned ISO string and the "date" function. We ended with a small plugin, to add, substract correctly in a specific timezone. // @ts-nocheck
import { PluginFunc } from 'dayjs'
const plugin: PluginFunc = (_, dayjsClass, d) => {
// eslint-disable-next-line no-param-reassign, func-names
dayjsClass.prototype.addInTz = function (...args) {
const timezone = this.$x.$timezone
if (!timezone) {
throw new Error('No timezone set')
}
return d.tz(
d(this.toDate())
.tz(timezone)
.add(...args)
.format('YYYY-MM-DD HH:mm:ss'),
timezone,
)
}
// eslint-disable-next-line no-param-reassign, func-names
dayjsClass.prototype.subtractInTz = function (...args) {
const timezone = this.$x.$timezone
if (!timezone) {
throw new Error('No timezone set')
}
return d.tz(
d(this.toDate())
.tz(timezone)
.subtract(...args)
.format('YYYY-MM-DD HH:mm:ss'),
timezone,
)
}
}
export default plugin Some tests examples ( all pass) import dayjs from 'dayjs'
import utc from 'dayjs/plugin/utc'
import timezone from 'dayjs/plugin/timezone'
// eslint-disable-next-line import/no-named-as-default
import dayjsTzCalc from '.'
dayjs.extend(utc)
dayjs.extend(timezone)
dayjs.extend(dayjsTzCalc)
describe('dayjsTzCalc', () => {
it('should add in the tz taking in count saving day light', () => {
// Us change their time zone on 2021-03-14
const originalDate = dayjs('2021-03-12T00:00:00.000Z').tz(
'America/New_York',
)
expect(originalDate.hour()).toEqual(19)
// We now add 31 days
const dateToCheck = originalDate.addInTz(31, 'day')
// We expect to have a shift of 1 hour due to saving day light
expect(dateToCheck.toISOString()).toEqual('2021-04-11T23:00:00.000Z')
// We expect the same hour as the original date
expect(dateToCheck.hour()).toEqual(19)
})
it('should subtract in the tz taking in count saving day light', () => {
// Us change their time zone on 2021-03-14
const originalDate = dayjs('2021-04-12T23:00:00.000Z').tz(
'America/New_York',
)
expect(originalDate.hour()).toEqual(19)
// We now add 31 days
const dateToCheck = originalDate.subtractInTz(31, 'day')
// We expect to have a shift of 1 hour due to saving day light
expect(dateToCheck.toISOString()).toEqual('2021-03-13T00:00:00.000Z')
// We expect the same hour as the original date
expect(dateToCheck.hour()).toEqual(19)
})
it('should work across daylight saving time', () => {
const originalDate = dayjs('2021-03-12T00:00:00.000Z').tz(
'America/New_York',
)
expect(originalDate.hour()).toEqual(19)
const dateToCheck = originalDate.addInTz(8, 'month')
expect(dateToCheck.toISOString()).toEqual('2021-11-12T00:00:00.000Z')
expect(dateToCheck.hour()).toEqual(19)
const dateToCheck2 = dateToCheck.subtractInTz(8, 'month')
expect(dateToCheck2.toISOString()).toEqual('2021-03-12T00:00:00.000Z')
expect(dateToCheck2.hour()).toEqual(19)
})
}) Little note, it should be possible to overload the "add" and "substract" original function, and detect a current timezone ( or not) to handle the correct addition, but in my team we prefer to tell clearly that we perform an addition/substraction in a TZ or directly on the UTC ( without daylight savings effects) |
To solve this issue that @MarkSFrancis posted we should do the manipulation before apply the timezone, for some reason the .tz function from Day.js is not exactly the same as Moment.js so we can have different results. On Day.js docs they explain how it works: Try this:
Both dates are now correct considering the daylight savings. |
The original code works if you run it in @MarkSFrancis tested in Santiago, Chile where DST ends in September. My guess is because DST is timed differently in |
Still doesn't work for me |
@Moumouls I was struggling with this issue today and your plugin finally helped me determine the exact cause and fixed it, so thank you for sharing! I hope this or something similar will make its way into the main branch. Do you know if there's a PR already with a fix? |
When adding days to a time, it's assuming each day is 24 hours long. This is not a correct assumption when crossing over daylight saving time.
Steps to recreate
Expected behavior
date1
anddate2
should be the same, as adding the number of "days" to the date should've allowed for the fact that not all days are 24 hours.Information
The text was updated successfully, but these errors were encountered: