dayjs
dayjs copied to clipboard
`dayjs.tz` incorrectly handles offsets containing minutes and seconds
Describe the bug
Creating a date with a timezone offset that contains minutes and seconds is not properly handled by dayjs.tz
function. Offset seems to be rounded to a nearest hour and newly created dayjs.Dayjs
object provides incorrect time information.
For example, timezone in Kyiv up until 1924 had an offset of UTC+02:02:04 (https://www.timeanddate.com/time/zone/ukraine/kyiv):
In Moment.js library this edge case is handled correctly. Here is a simple test that demonstrates how dayjs.tz
should behave:
describe('tz', () => {
describe('given a timezone with UTC+02:02:04 offset', () => {
test('handles it correctly', () => {
const kiev = dayjs('1900-06-01T12:00:00Z').tz('Europe/Kiev');
const Mkiev = moment('1900-06-01T12:00:00Z').tz('Europe/Kiev');
expect(kiev.format()).toBe('1900-06-01T14:02:04+02:02');
expect(kiev.format()).toBe(Mkiev.format());
expect(kiev.valueOf()).toBe(-2195899200000);
expect(kiev.valueOf()).toBe(Mkiev.valueOf());
expect(kiev.utcOffset()).toBe(122.06666666666666);
expect(kiev.utcOffset()).toBe(Mkiev.utcOffset());
expect(kiev.utc().format()).toBe("1900-06-01T12:00:00Z");
expect(kiev.utc().format()).toBe(Mkiev.utc().format());
});
});
});
Information
- Day.js Version: 1.11.2
- OS: Windows 10, version 21H2
- Browser: Node.js 16.15.0
- Time zone: Europe/Vilnius (UTC offset: +02:00; DST: +03:00)
Also, it seems that V8 JavaScript engine returns an integer part of the offset when calling Date.prototype.getTimezoneOffset()
. Because of that, new Date("1900-06-01T12:00:00Z").getTimezoneOffset()
returns -122
in Node.js and Chromium-based browsers, but in Firefox we get -122.06666666666666
. Related issue: https://github.com/nodejs/node/issues/33306
The same problem as me
Perhaps the documentation is not too clear (at least not the starting page for time zone).
You have to differentiate between
'parsing in time zone' (dayjs.tz(isoString, 'Australia/Sydney') - gives '2022-02-28T13:57:02+11:00')
and
'converting to time zone' (dayjs(isoString).tz('Australia/Sydney') - gives '2022-03-01T00:57:02+11:00')
So if you use the 'parsing' syntax and you get a better result:
const kievAsDayjs = dayjs.tz('1900-06-01T12:00:00', 'Europe/Kiev')
// gets correct offset with 02:02:04; only strange format, as offset does not show seconds
// moment just drops the seconds
console.log(kievAsDayjs.format('YYYY-MM-DD HH:mm:ss.SSSZ')) // '1900-06-01 12:00:00.000+02:2.066666666666663'
// value = GMT: Friday, 1. June 1900 09:57:56
// the offset (02:02:04) is part of the value
console.log(kievAsDayjs.valueOf()) // -2195906524000
// offset is the same as with moment
console.log(kievAsDayjs.utcOffset()) // 122.06666666666666
// offset from time zone gets also part of utc time
console.log(kievAsDayjs.utc().format('YYYY-MM-DD HH:mm:ss.SSSZ')) // '1900-06-01 09:57:56.000+00:00'
IMHO the display formats of dayjs are more logical as the formats of moment; but for this unusual case it is different to what moment does.