iamkun / dayjs

⏰ Day.js 2kB immutable date-time library alternative to Moment.js with the same modern API
https://day.js.org
MIT License
46.93k stars 2.3k forks source link

Wrong result when manipulating timezone date #1843

Open Pierrebthlr opened 2 years ago

Pierrebthlr commented 2 years ago

Describe the bug When I use dates in a timezone and add a duration the result time looks bad

const dayjs = require('dayjs');
const timezone = require('dayjs/plugin/timezone');
const utc = require('dayjs/plugin/utc');

dayjs.extend(utc);
dayjs.extend(timezone);

// Accra timezone is in UTC + 0 so UTC time and Accra time should be same
const startDate = dayjs.utc('2022-04-05 12:00:00').tz('Africa/Accra')
console.log(startDate.format('YYYY-MM-DD HH:mm:ss'))  // Result => '2022-04-05 12:00:00'
const endDate =  startDate.add(1, 'h')
console.log(endDate.format('YYYY-MM-DD HH:mm:ss')) // Result => '2022-04-05 11:00:00' 

Expected behavior As I add an hour I expect to get '2022-04-05 13:00:00' and not '2022-04-05 11:00:00'

It seems to be related to the browser timezone. I am personally in Central European Summer Time (GMT+2). But if I change the timezone of Chrome with devTools for 'Africa/Accra'. The problem doesn't seem to happen again Obviously when I look at the open issues I am not the only one to have problems with the timezone, is there anything planned to fix this? Thank you in advance :)

Information

vajsm commented 2 years ago

Same observation. I believe this has something to do with switching between Standard and Daylight Saving Time. If I were to guess, I think dayjs is taking it's data regarding time zone offsets for time zones with offsets ~00:00 from the wrong file in IANA TZ database and this causes the issues with formatting or date time math.

Here's a sample script that demonstrates the problem. I am parsing an UTC ISO timestamp to dayjs date and I want it to interpret it as a local time in the provided timezone (Europe/London).

Let's run the function twice; once for 2022-02-17 which will be the standard time (+00:00) and once for 2022-06-17 which is the summer time (+01:00).

var dayjs = require('dayjs');
var timezone = require('dayjs/plugin/timezone');
var utc = require('dayjs/plugin/utc')
var advanced = require("dayjs/plugin/advancedFormat")

dayjs.extend(utc);
dayjs.extend(timezone);
dayjs.extend(advanced);

function test(date, tz) {
  let parsedDate = dayjs(date).tz(tz);

  console.log("ISO string\t", parsedDate.toISOString());
  console.log("Timezone\t", parsedDate.format("z"));
}

const tz = "Europe/London";                 // +00:00 STD (GMT), +01:00 DST (BST)

test("2022-02-17T00:00:00.000Z", tz);       // Date in UTC, will fall into STD of the target timezone

test("2022-06-17T00:00:00.000Z", tz);       // Date in UTC, will fall into DST of the target timezone

So for the 2022-02-17 we get:

--- Result:
ISO string       2022-02-17T01:00:00.000Z
Format           2022-02-17T00:00:00+00:00
Timezone         GMT

--- Expected:
ISO string       2022-02-17T00:00:00.000Z
Format           2022-02-17T00:00:00+00:00  // or 2022-02-17T00:00:00Z
Timezone         Europe/London

There's 1-hour difference between the expected results and the actual results for ISO string. Note that timezone plugin interpreted the timezone as GMT where it should interpret it as Europe/London which is what I provided as an argument.

GMT timezone in IANA database (link to Etc/GMT) has UTC offset set to +00:00 both for DST and STD. image

Europe/London, however, has 1-hour difference: image

The results for the 2022-06-17:

--- Result
ISO string       2022-06-17T00:00:00.000Z
Format           2022-06-17T01:00:00+01:00
Timezone         GMT+1

--- Expected
ISO string       2022-06-17T00:00:00.000Z  // or 2022-06-17T01:00:00.000+01:00
Format           2022-06-17T01:00:00+01:00
Timezone         Europe/London

Now, both ISO string and format make sense, but the timezone is displayed as GMT+1 which I would not expect. ETC/GMT+1 means UTC-01:00 in IANA database and that's not a match for Europe/London.

image

I'm also in CEST timezone (+02:00) for what it's worth.

Not sure if that's exactly what happens, I may dig a bit more into this when I have time, but I will leave the results of my investigation here in case it makes sense to anyone else willing to fix the issues with timezone plugin.

See: https://en.wikipedia.org/wiki/List_of_tz_database_time_zones