Open matthew-dev opened 3 years ago
There is another inconsistency when trying to get offset for the same hour that DST change occurs when browser's time zone and selected one are equal to Europe/Bratislava:
getTimezoneOffset(
"Europe/Bratislava",
utcToZonedTime("2022-03-27T01:00:00.000Z", "Europe/Bratislava")
) / (60 * 1000 * -1)
// -60
// ❌ WRONG! that date refers to 3 AM in local time, which is having offset UTC+2 - summer time (SELČ)
new Date('2022-03-27T01:00:00.000Z').getTimezoneOffset(),
// ✅ -120
format(new Date('2022-03-27T01:00:00.000Z'), 'yyyy-MM-dd HH:mm:ss zzz', { timeZone: "Europe/Bratislava" }),
// 2022-03-27 03:00:00 SELČ
I tried the same behavior for Europe/London
time zone (for both browser and selection), but couldn't replicate the same issue:
getTimezoneOffset(
"Europe/London",
utcToZonedTime("2022-03-27T01:00:00.000Z", "Europe/London")
) / (60 * 1000 * -1),
// ✅ -60
new Date('2022-03-27T01:00:00.000Z').getTimezoneOffset(),
// ✅ -60
format(new Date('2022-03-27T01:00:00.000Z'), 'yyyy-MM-dd HH:mm:ss zzz', { timeZone: "Europe/London" })
// 2022-03-27 02:00:00 GMT+1
I also tried the transition from summertime to wintertime and unfortunately, there is another issue related to this. This time, format
seems to provide correct time values(but not the correct timezone), and getTimeZoneOffset
also provides me with some misaligned values 😥
Browser's time zone - Europe/Bratislava
Selected time zone - Europe/London
/**
* UTC to Europe/London evaluated in Europe/Bratislava TZ
* note that DST change in Europe/Bratislava happens at 3AM -> 2AM
*/
getTimezoneOffset(
"Europe/London",
utcToZonedTime("2021-10-31T00:00:00.000Z", "Europe/London")
) / (60 * 1000 * -1)
// ✅ -60
getTimezoneOffset(
"Europe/London",
utcToZonedTime("2021-10-31T01:00:00.000Z", "Europe/London")
) / (60 * 1000 * -1)
// ❌ -60
// WRONG! should be 0 as it refers to 1 AM being aligned to UTC TZ - DST change was just applied
getTimezoneOffset(
"Europe/London",
utcToZonedTime("2021-10-31T02:00:00.000Z", "Europe/London")
) / (60 * 1000 * -1)
// ❌ -60
// WRONG! should be 0 as it refers to 2 AM being aligned to UTC TZ
getTimezoneOffs7et(
"Europe/London",
utcToZonedTime("2021-10-31T03:00:00.000Z", "Europe/London")
) / (60 * 1000 * -1)
// ✅ 0
Checks for the format helper:
format(
utcToZonedTime("2021-10-31T00:00:00.000Z", "Europe/London"),
"yyyy-MM-dd HH:mm:ss zzz",
{ timeZone: "Europe/London" }
)
// ✅ 2021-10-31 01:00:00 GMT+1
format(
utcToZonedTime("2021-10-31T01:00:00.000Z", "Europe/London"),
"yyyy-MM-dd HH:mm:ss zzz",
{ timeZone: "Europe/London" }
)
// ❌ 2021-10-31 01:00:00 GMT+1
// time value is fine, but timezone should be GMT
format(
utcToZonedTime("2021-10-31T02:00:00.000Z", "Europe/London"),
"yyyy-MM-dd HH:mm:ss zzz",
{ timeZone: "Europe/London" }
)
// ❌ 2021-10-31 02:00:00 GMT+1
// time value is fine, but timezone should be GMT
format(
utcToZonedTime("2021-10-31T03:00:00.000Z", "Europe/London"),
"yyyy-MM-dd HH:mm:ss zzz",
{ timeZone: "Europe/London" }
)
// ✅ 2021-10-31 03:00:00 GMT
Vice versa (browser's time zone Europe/London, selected one Europe/Bratislava):
/**
* UTC to Europe/Bratislava evaluated in Europe/London TZ
* note that DST change in Europe/London happens at 2 AM -> 1 AM
*/
getTimezoneOffset(
"Europe/Bratislava",
utcToZonedTime("2021-10-31T00:00:00.000Z", "Europe/Bratislava")
) / (60 * 1000 * -1),
// ❌ -60
// WRONG! should be -120 as it refers to 2 AM (1 hour before DST change will be applied)
getTimezoneOffset(
"Europe/Bratislava",
utcToZonedTime("2021-10-31T01:00:00.000Z", "Europe/Bratislava")
) / (60 * 1000 * -1),
// ✅ -60
getTimezoneOffset(
"Europe/Bratislava",
utcToZonedTime("2021-10-31T02:00:00.000Z", "Europe/Bratislava")
) / (60 * 1000 * -1),
// ✅ -60
getTimezoneOffset(
"Europe/Bratislava",
utcToZonedTime("2021-10-31T03:00:00.000Z", "Europe/Bratislava")
) / (60 * 1000 * -1)
// ✅ -60
Some more checks for the format helper:
format(
utcToZonedTime("2021-10-31T00:00:00.000Z", "Europe/Bratislava"),
"yyyy-MM-dd HH:mm:ss zzz",
{ timeZone: "Europe/Bratislava" }
)
// ❌ 2021-10-31 02:00:00 SEČ
// time value is fine, but timezone should be SELČ (UTC+2)
format(
utcToZonedTime("2021-10-31T01:00:00.000Z", "Europe/Bratislava"),
"yyyy-MM-dd HH:mm:ss zzz",
{ timeZone: "Europe/Bratislava" }
)
// ✅ 2021-10-31 02:00:00 SEČ
format(
utcToZonedTime("2021-10-31T02:00:00.000Z", "Europe/Bratislava"),
"yyyy-MM-dd HH:mm:ss zzz",
{ timeZone: "Europe/Bratislava" }
)
// ✅ 2021-10-31 03:00:00 SEČ
format(
utcToZonedTime("2021-10-31T03:00:00.000Z", "Europe/Bratislava"),
"yyyy-MM-dd HH:mm:ss zzz",
{ timeZone: "Europe/Bratislava" }
)
// ✅ 2021-10-31 04:00:00 SEČ
Also, as for my previous comment, there is a wrong offset served for UTC 01:00 time when both timezones (browser's and selected one) are the same...
/**
* Browser & selected time zone are equal to _Europe/Bratislava_
*/
getTimezoneOffset(
"Europe/Bratislava",
utcToZonedTime("2021-10-31T01:00:00.000Z", "Europe/Bratislava")
) / (60 * 1000 * -1)
// -120
// ❌ WRONG! that date refers to 2 AM in local time, which is having offset UTC+1
// DST change was just applied
new Date('2022-03-27T01:00:00.000Z').getTimezoneOffset(),
// ✅ -60
format(new Date('2021-10-31T01:00:00.000Z'), 'yyyy-MM-dd HH:mm:ss zzz', { timeZone: "Europe/Bratislava" }),
// 2021-10-31 02:00:00 SEČ
/**
* Browser & selected time zone are equal to _Europe/London
*/
getTimezoneOffset(
"Europe/London",
utcToZonedTime("2021-10-31T01:00:00.000Z", "Europe/London")
) / (60 * 1000 * -1)
// -60
// ❌ WRONG! that date refers to 1 AM in local time, aligned to GMT as DST change was just applied
new Date('2022-03-27T01:00:00.000Z').getTimezoneOffset(),
// ✅ 0
format(new Date('2021-10-31T01:00:00.000Z'), 'yyyy-MM-dd HH:mm:ss zzz', { timeZone: "Europe/London" }),
// 2021-10-31 01:00:00 GMT
Thank you for the detailed investigation you've done @matthew-dev and for providing all this information. Many DST related issues have been fixed in 1.2.2
, and it will be interesting to know whether some or all of these issues have gone away now.
Do you still have these tests to hand to check? It would be useful if you have time to create a PR with these tests (including failing ones) so we can track this more easily.
Hi. I experimented very similar issues with v1.2.2 and v1.3.0. In short, depending on the system date and the desired timezone, there is an issue in handling either the moment before DST or the moment after DST. Also, it seems that new issues are duplicate of this one, e.g. #154, #159, and #167
For example, this case tests the moment before DST kicks in. The original date is written in UTC format, and it is converted to Europe/Rome +01:00.
it('should handle DST for right before DST kicks in', () => {
expect(
format(utcToZonedTime('2022-03-27T00:59:59Z', 'Europe/Rome'), 'xxx', { timeZone: 'Europe/Rome' })
).toBe('+01:00');
});
In my opinion, if I understand correctly the rationale behind date-fns-tz, this test should be completely independent of the system time.
Do you think I am missing something, or is there a bug?
Also, isn't this issue very similar to the one that should be fixed in #108 and #93?
@marnusw Any update regarding this issue ? Do you need a PR from us ?
@Gadzy I'm quite short on time at the moment, so yes, please put together a PR if you can. If you would add / update tests to show the problem, and then implement the solution in a subsequent commit that will help a lot with making the review a quick one.
⚠️ I guess there is an issue regarding having DST change in both browser's time zone and selected time zone within the same day but happening in a different hour.
Lets' consider the API provides us with a date (in ISO string format), which we need to show in the chosen time zone.
Browser's time zone - Europe/Bratislava Selected time zone - Europe/London
I prepared some dates around DST change, just one of them seems to be not aligned.
I tried to list offsets for those dates, but all of them seem to be fine.
When we switch those 2 timezones (browser to Europe/London and selected one to Europe/Bratislava), there is also 1 misaligned value, but this time happening at a different hour.
Same as for the previous check, all the listed offsets seem to be fine.
The issue was spotted on the latest version
1.1.4
. Thanks.