dayjs icon indicating copy to clipboard operation
dayjs copied to clipboard

`dayjs.tz` incorrectly handles offsets containing minutes and seconds

Open tbaliukynas opened this issue 2 years ago • 3 comments

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): image

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)

tbaliukynas avatar May 18 '22 21:05 tbaliukynas

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

tbaliukynas avatar May 22 '22 17:05 tbaliukynas

The same problem as me

luzhiy123 avatar May 27 '22 09:05 luzhiy123

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.

BePo65 avatar Jun 08 '22 17:06 BePo65