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.71k stars 2.28k forks source link

Timezone conversion incorrect when daylight saving in effect #1260

Open PaulJNewell77 opened 3 years ago

PaulJNewell77 commented 3 years ago

Describe the bug Dayjs does not seem to convert timezone correctly when DST is in effect. It produces different results from Moment. The Moment results seem intuitively what I'd expect, so believe to be correct unless there is some subtlety I'm missing.

Expected behavior With this code:

    const when = '2020-07-30T12:00:00+00:00'; // Midday GMT in summer
    console.log('Moment London: ' + moment(when).tz('Europe/London').format('HH:mm Z'));
    console.log('Moment New York: ' + moment(when).tz('America/New_York').format('HH:mm Z'));

    console.log('Dayjs London: ' + dayjs(when).tz('Europe/London').format('HH:mm Z'));
    console.log('Dayjs New York: ' + dayjs(when).tz('America/New_York').format('HH:mm Z'));

The output is:

    Moment London: 13:00 +01:00
    Moment New York: 08:00 -04:00
    Dayjs London: 12:00 +00:00 // Expect 13:00 +01:00
    Dayjs New York: 08:00 -05:00 // Expect 08:00 -04:00

Information

alexanderankin commented 3 years ago

is this whats going wrong in the test suite of this library? i ran it and some tests were off by an hour.

PaulJNewell77 commented 3 years ago

Hi, any thoughts on this issue @iamkun?

lucasrcosta commented 3 years ago

I can confirm that tests are failing in a locale affected by daylight savings (I'm in America/Vancouver).

I found out because tests stared breaking locally, and I made some experiments showing output different from Moment: https://runkit.com/lucasrcosta/5feca83aea7c09001b879b3a

I tried sorting it out on the code and removing the current offset from localUtcOffset on timezone/index.js fixed one test, but I couldn't figure out solutions for the others. It does seem related to not fixing DST.

Summary of all failing tests

 FAIL  test/plugin/dayOfYear.test.js
  ● DayOfYear set

    expect(received).toBe(expected) // Object.is equality

    Expected value to be:
      "2015-01-04T00:00:00.000Z"
    Received:
      "2014-01-05T00:00:00.000Z"

      31 |   expect(dayjs('2015-01-01T00:00:00.000Z')
      32 |     .dayOfYear(4)
    > 33 |     .toISOString()).toBe('2015-01-04T00:00:00.000Z')
      34 | 
      35 |   expect(dayjs('2015-01-01T00:00:00.000Z')
      36 |     .dayOfYear(32)

      at Object.<anonymous> (test/plugin/dayOfYear.test.js:33:21)

 FAIL  test/plugin/localizedFormat.test.js
  ● Should not interpolate characters inside square brackets

    expect(received).toBe(expected) // Object.is equality

    Expected value to be:
      "1970 l 1970"
    Received:
      "1969 l 1969"

      29 | 
      30 |   expect(actualDate.format('[l]')).toBe('l')
    > 31 |   expect(actualDate.format('YYYY [l] YYYY')).toBe('1970 l 1970')
      32 |   expect(actualDate.format('l [l] l')).toBe('1/1/1970 l 1/1/1970')
      33 |   expect(actualDate.format('[L LL LLL LLLL]')).toBe(expectedDate.format('[L LL LLL LLLL]'))
      34 | 

      at Object.<anonymous> (test/plugin/localizedFormat.test.js:31:46)

 FAIL  test/plugin/timezone.test.js
  ● Parse › parse timestamp, js Date, Day.js object

    expect(received).toBe(expected) // Object.is equality

    Expected value to be:
      "2020-08-07T12:00:00-07:00"
    Received:
      "2020-08-07T12:00:00-08:00"

      49 |     const Timestamp = dayjs.tz(d.getTime(), VAN)
      50 |     const Tmoment = moment.tz(d, VAN)
    > 51 |     expect(TjsDate.format()).toBe(result)
      52 |     expect(Tdayjs.format()).toBe(result)
      53 |     expect(Timestamp.format()).toBe(result)
      54 |     expect(Tmoment.format()).toBe(result)

      at Object.<anonymous> (test/plugin/timezone.test.js:51:30)

  ● Parse › parse and convert between timezones

    expect(received).toBe(expected) // Object.is equality

    Expected value to be:
      "2014-06-01T09:00:00-07:00"
    Received:
      "2014-06-01T09:00:00-08:00"

      57 |   it('parse and convert between timezones', () => {
      58 |     const newYork = dayjs.tz('2014-06-01 12:00', NY)
    > 59 |     expect(newYork.tz('America/Los_Angeles').format()).toBe('2014-06-01T09:00:00-07:00')
      60 |     expect(newYork.tz('Europe/London').format()).toBe('2014-06-01T17:00:00+01:00')
      61 |   })
      62 | 

      at Object.<anonymous> (test/plugin/timezone.test.js:59:56)

  ● Convert › convert to target time

    expect(received).toBe(expected) // Object.is equality

    Expected value to be:
      "2014-06-01T05:00:00-07:00"
    Received:
      "2014-06-01T05:00:00-08:00"

      74 |     const losAngeles = dayjs('2014-06-01T12:00:00Z').tz('America/Los_Angeles')
      75 |     const MlosAngeles = moment('2014-06-01T12:00:00Z').tz('America/Los_Angeles')
    > 76 |     expect(losAngeles.format()).toBe('2014-06-01T05:00:00-07:00')
      77 |     expect(losAngeles.format()).toBe(MlosAngeles.format())
      78 |     expect(losAngeles.valueOf()).toBe(1401624000000)
      79 |     expect(losAngeles.valueOf()).toBe(MlosAngeles.valueOf())

      at Object.<anonymous> (test/plugin/timezone.test.js:76:33)

  ● Convert › convert to target time

    expect(received).toBe(expected) // Object.is equality

    Expected value to be:
      "2014-06-01T05:00:00-07:00"
    Received:
      "2014-06-01T05:00:00-08:00"

      85 |     [dayjs, moment].forEach((_) => {
      86 |       const losAngeles = _('2014-06-01T12:00:00Z').tz('America/Los_Angeles')
    > 87 |       expect(losAngeles.format()).toBe('2014-06-01T05:00:00-07:00')
      88 |       expect(losAngeles.valueOf()).toBe(1401624000000)
      89 |     })
      90 |   })

      at forEach (test/plugin/timezone.test.js:87:35)
          at Array.forEach (<anonymous>)
      at Object.<anonymous> (test/plugin/timezone.test.js:85:21)

  ● Convert › convert from time with timezone to target time

    expect(received).toBe(expected) // Object.is equality

    Expected value to be:
      "2014-06-01T12:00:00Z"
    Received:
      "2014-06-01T12:00:00-01:00"

      93 |     const losAngelesInUTC = dayjs('2014-06-01T05:00:00-07:00').tz('UTC')
      94 |     const MlosAngelesInUTC = moment('2014-06-01T05:00:00-07:00').tz('UTC')
    > 95 |     expect(losAngelesInUTC.format()).toBe('2014-06-01T12:00:00Z')
      96 |     expect(losAngelesInUTC.format()).toBe(MlosAngelesInUTC.format())
      97 |   })
      98 | 

      at Object.<anonymous> (test/plugin/timezone.test.js:95:38)

  ● Convert › format Z

    expect(received).toBe(expected) // Object.is equality

    Expected value to be:
      "+09:00"
    Received:
      "+08:00"

      115 |     [dayjs, moment].forEach((_) => {
      116 |       const t = _('2020-08-06T03:48:10.258Z').tz(TOKYO)
    > 117 |       expect(t.format('Z')).toBe('+09:00')
      118 |     })
      119 |   })
      120 | })

      at forEach (test/plugin/timezone.test.js:117:29)
          at Array.forEach (<anonymous>)
      at Object.<anonymous> (test/plugin/timezone.test.js:115:21)

  ● DST, a time that never existed Spring Forward › 2012-03-11 02:00:00

    expect(received).toBe(expected) // Object.is equality

    Expected value to be:
      "2012-03-11T03:00:00-04:00"
    Received:
      "2012-03-11T04:00:00-04:00"

      143 |     const d = dayjs.tz(s, NY)
      144 |     const m = moment.tz(s, NY)
    > 145 |     expect(d.format()).toBe('2012-03-11T03:00:00-04:00')
      146 |     expect(d.format()).toBe(m.format())
      147 |     expect(d.valueOf()).toBe(m.valueOf())
      148 |     expect(d.valueOf()).toBe(1331449200000)

      at Object.<anonymous> (test/plugin/timezone.test.js:145:24)

  ● DST, a time that never existed Spring Forward › 2012-03-11 02:59:59

    expect(received).toBe(expected) // Object.is equality

    Expected value to be:
      "2012-03-11T03:59:59-04:00"
    Received:
      "2012-03-11T04:59:59-04:00"

      154 |     const d = dayjs.tz(s, NY)
      155 |     const m = moment.tz(s, NY)
    > 156 |     expect(d.format()).toBe('2012-03-11T03:59:59-04:00')
      157 |     expect(d.format()).toBe(m.format())
      158 |     expect(d.valueOf()).toBe(m.valueOf())
      159 |     expect(d.valueOf()).toBe(1331452799000)

      at Object.<anonymous> (test/plugin/timezone.test.js:156:24)

  ● DST, a time that never existed Spring Forward › 2012-03-11 03:00:00

    expect(received).toBe(expected) // Object.is equality

    Expected value to be:
      "2012-03-11T03:00:00-04:00"
    Received:
      "2012-03-11T04:00:00-04:00"

      165 |     const d = dayjs.tz(s, NY)
      166 |     const m = moment.tz(s, NY)
    > 167 |     expect(d.format()).toBe('2012-03-11T03:00:00-04:00')
      168 |     expect(d.format()).toBe(m.format())
      169 |     expect(d.valueOf()).toBe(m.valueOf())
      170 |     expect(d.valueOf()).toBe(1331449200000)

      at Object.<anonymous> (test/plugin/timezone.test.js:167:24)

  ● DST, a time that never existed Fall Back › 2012-11-04 02:00:00

    expect(received).toBe(expected) // Object.is equality

    Expected value to be:
      "2012-11-04T02:00:00-05:00"
    Received:
      "2012-11-04T01:00:00-05:00"

      204 |     [dayjs, moment].forEach((_) => {
      205 |       const d = _.tz(s, NY)
    > 206 |       expect(d.format()).toBe('2012-11-04T02:00:00-05:00')
      207 |       expect(d.utcOffset()).toBe(-300)
      208 |       expect(d.valueOf()).toBe(1352012400000)
      209 |     })

      at forEach (test/plugin/timezone.test.js:206:26)
          at Array.forEach (<anonymous>)
      at Object.<anonymous> (test/plugin/timezone.test.js:204:21)

Test Suites: 3 failed, 63 passed, 66 total
Tests:       12 failed, 601 passed, 613 total
Snapshots:   0 total
Time:        12.936s
Ran all test suites.
addisonElliott commented 3 years ago

This bug sounds similar to the recent comment in this one: https://github.com/iamkun/dayjs/issues/1340#issuecomment-764769936

I'm going to cross-post the bug and how it can be fixed. Let me know if that doesn't actually fix this.

Not sure your bug is similar to the OPs, unless I'm missing the connection.

But I did confirm and figure out what the problem is with your case. It's actually a pretty easy fix (I think).

The problem

I'm in CST, which has an offset of -5/-6 (depending on DST). Let's use this as an example.

Given the input you gave: 2020-03-29T00:30:00Z

The code converts to local machines timezone (at this time it was -5 for me). Then it converts to the desired timezone. These are the results:

  • 2020-03-28T20:30:00-05:00
  • 2020-03-29T03:30:00+02:00

Then these two dates are subtracted (disregard the offset), giving -07:00. Next, we take local machine offset (-5) and subtract from -7. -5 - (-7) = +2. Correct answer.

That's how it should work. Let's look at the code and I'll show you the bug.

  proto.tz = function (timezone = defaultTimezone, keepLocalTime) {
    const oldOffset = this.utcOffset()
    const target = this.toDate().toLocaleString('en-US', { timeZone: timezone })
    const diff = Math.round((this.toDate() - new Date(target)) / 1000 / 60)
    let ins = d(target).$set(MS, this.$ms).utcOffset(localUtcOffset - diff, true)
    if (keepLocalTime) {
      const newOffset = ins.utcOffset()
      ins = ins.add(oldOffset - newOffset, MIN)
    }
    ins.$x.$timezone = timezone
    return ins
  }

Source: https://github.com/iamkun/dayjs/blob/dev/src/plugin/timezone/index.js#L101

Check out that it takes localUtcOffset and subtracts the diff. That localUtcOffset is obtained by doing the following. The problem is that DST is applied now so the offset for me is -06:00, giving the offset of +01:00 that we're seeing.

  const localUtcOffset = d().utcOffset()

Source: https://github.com/iamkun/dayjs/blob/dev/src/plugin/timezone/index.js#L39

The fix

Replace localUtcOffset with this.utcOffset() and that will return the UTC offset at that date

What you should do

I haven't tested this but I think it'll work.

If that works for you, I'd encourage you to submit a PR and fix this.

When was the bug introduced

Bug has existed ever since the timezone plugin was added.

https://github.com/iamkun/dayjs/pull/974/files

addisonElliott commented 3 years ago

A fix was applied in #1352 that may fix this bug. Can someone try with the latest changes and see if the issue still persists?

lucasrcosta commented 3 years ago

Still brakes here, although a little different...


Summary of all failing tests
 FAIL  test/plugin/timezone.test.js (5.167s)
  ● Convert › convert to target time

    expect(received).toBe(expected) // Object.is equality

    Expected value to be:
      1401624000000
    Received:
      1401620400000

      76 |     expect(losAngeles.format()).toBe('2014-06-01T05:00:00-07:00')
      77 |     expect(losAngeles.format()).toBe(MlosAngeles.format())
    > 78 |     expect(losAngeles.valueOf()).toBe(1401624000000)
      79 |     expect(losAngeles.valueOf()).toBe(MlosAngeles.valueOf())
      80 |     expect(losAngeles.utcOffset()).toBe(-420)
      81 |     expect(losAngeles.utcOffset()).toBe(MlosAngeles.utcOffset())

      at Object.<anonymous> (test/plugin/timezone.test.js:78:34)

  ● Convert › convert to target time

    expect(received).toBe(expected) // Object.is equality

    Expected value to be:
      1401624000000
    Received:
      1401620400000

      86 |       const losAngeles = _('2014-06-01T12:00:00Z').tz('America/Los_Angeles')
      87 |       expect(losAngeles.format()).toBe('2014-06-01T05:00:00-07:00')
    > 88 |       expect(losAngeles.valueOf()).toBe(1401624000000)
      89 |     })
      90 |   })
      91 | 

      at forEach (test/plugin/timezone.test.js:88:36)
          at Array.forEach (<anonymous>)
      at Object.<anonymous> (test/plugin/timezone.test.js:85:21)

  ● DST, a time that never existed Spring Forward › 2012-03-11 02:00:00

    expect(received).toBe(expected) // Object.is equality

    Expected value to be:
      "2012-03-11T03:00:00-04:00"
    Received:
      "2012-03-11T04:00:00-04:00"

      145 |     const d = dayjs.tz(s, NY)
      146 |     const m = moment.tz(s, NY)
    > 147 |     expect(d.format()).toBe('2012-03-11T03:00:00-04:00')
      148 |     expect(d.format()).toBe(m.format())
      149 |     expect(d.valueOf()).toBe(m.valueOf())
      150 |     expect(d.valueOf()).toBe(1331449200000)

      at Object.<anonymous> (test/plugin/timezone.test.js:147:24)

  ● DST, a time that never existed Spring Forward › 2012-03-11 02:59:59

    expect(received).toBe(expected) // Object.is equality

    Expected value to be:
      "2012-03-11T03:59:59-04:00"
    Received:
      "2012-03-11T04:59:59-04:00"

      156 |     const d = dayjs.tz(s, NY)
      157 |     const m = moment.tz(s, NY)
    > 158 |     expect(d.format()).toBe('2012-03-11T03:59:59-04:00')
      159 |     expect(d.format()).toBe(m.format())
      160 |     expect(d.valueOf()).toBe(m.valueOf())
      161 |     expect(d.valueOf()).toBe(1331452799000)

      at Object.<anonymous> (test/plugin/timezone.test.js:158:24)

  ● DST, a time that never existed Spring Forward › 2012-03-11 03:00:00

    expect(received).toBe(expected) // Object.is equality

    Expected value to be:
      "2012-03-11T03:00:00-04:00"
    Received:
      "2012-03-11T04:00:00-04:00"

      167 |     const d = dayjs.tz(s, NY)
      168 |     const m = moment.tz(s, NY)
    > 169 |     expect(d.format()).toBe('2012-03-11T03:00:00-04:00')
      170 |     expect(d.format()).toBe(m.format())
      171 |     expect(d.valueOf()).toBe(m.valueOf())
      172 |     expect(d.valueOf()).toBe(1331449200000)

      at Object.<anonymous> (test/plugin/timezone.test.js:169:24)

  ● DST, a time that never existed Fall Back › 2012-11-04 02:00:00

    expect(received).toBe(expected) // Object.is equality

    Expected value to be:
      "2012-11-04T02:00:00-05:00"
    Received:
      "2012-11-04T01:00:00-05:00"

      206 |     [dayjs, moment].forEach((_) => {
      207 |       const d = _.tz(s, NY)
    > 208 |       expect(d.format()).toBe('2012-11-04T02:00:00-05:00')
      209 |       expect(d.utcOffset()).toBe(-300)
      210 |       expect(d.valueOf()).toBe(1352012400000)
      211 |     })

      at forEach (test/plugin/timezone.test.js:208:26)
          at Array.forEach (<anonymous>)
      at Object.<anonymous> (test/plugin/timezone.test.js:206:21)

 FAIL  test/plugin/localizedFormat.test.js
  ● Should not interpolate characters inside square brackets

    expect(received).toBe(expected) // Object.is equality

    Expected value to be:
      "1970 l 1970"
    Received:
      "1969 l 1969"

      29 | 
      30 |   expect(actualDate.format('[l]')).toBe('l')
    > 31 |   expect(actualDate.format('YYYY [l] YYYY')).toBe('1970 l 1970')
      32 |   expect(actualDate.format('l [l] l')).toBe('1/1/1970 l 1/1/1970')
      33 |   expect(actualDate.format('[L LL LLL LLLL]')).toBe(expectedDate.format('[L LL LLL LLLL]'))
      34 | 

      at Object.<anonymous> (test/plugin/localizedFormat.test.js:31:46)

 FAIL  test/plugin/dayOfYear.test.js
  ● DayOfYear set

    expect(received).toBe(expected) // Object.is equality

    Expected value to be:
      "2015-01-04T00:00:00.000Z"
    Received:
      "2014-01-05T00:00:00.000Z"

      31 |   expect(dayjs('2015-01-01T00:00:00.000Z')
      32 |     .dayOfYear(4)
    > 33 |     .toISOString()).toBe('2015-01-04T00:00:00.000Z')
      34 | 
      35 |   expect(dayjs('2015-01-01T00:00:00.000Z')
      36 |     .dayOfYear(32)

      at Object.<anonymous> (test/plugin/dayOfYear.test.js:33:21)

Test Suites: 3 failed, 68 passed, 71 total
Tests:       8 failed, 648 passed, 656 total
Snapshots:   0 total
Time:        12.402s
Ran all test suites.
error Command failed with exit code 1.
info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command.

dayjs on  dev is 📦 v0.0.0-development took 20s 982ms 
➜ git pull
Already up to date.```
sts-ryan-holton commented 3 years ago

I'm getting the same problem now that we're in BST, I'm in Europe/London timezone and this is what I'm getting:

Clearly wrong, any suggestions?

hhamud commented 3 years ago

Has this been fixed?. I am having the same issue with a few different time zones that apply daylight savings time, or has anyone been successful with any work around?,

addisonElliott commented 3 years ago

It sounds like it hasn't. I thought #1352 would fix it but others have reported not. Note that these changes are not officially released. In other words, you have to use the dev branch in order to get the latest changes.

If you're interested, try with the dev branch and see if it's still broken

hhamud commented 3 years ago

It sounds like it hasn't. I thought #1352 would fix it but others have reported not. Note that these changes are not officially released. In other words, you have to use the dev branch in order to get the latest changes.

If you're interested, try with the dev branch and see if it's still broken

Thanks for the tip. I've tried to install the dev branch however it still does not fix my issue.

dobeerman commented 3 years ago

Seems the issue still in place.

I tried to run this script in different time zones and got quite diverged results.

console.log(dayjs("2027-10-31T00:00:00Z").tz("Europe/Berlin").format());

console.log(dayjs("2027-10-31T01:00:00Z").tz("Europe/Berlin").format()); // +1hr

ServerTime: Wed Sep 15 2021 08:14:59 GMT+0200 (Central European Summer Time)

2027-10-31T02:00:00+02:00
2027-10-31T02:00:00Z

ServerTime: Wed Sep 15 2021 07:15:53 GMT+0100 (British Summer Time)

2027-10-31T02:00:00+03:00
2027-10-31T02:00:00+01:00

ServerTime: Wed Sep 15 2021 16:16:46 GMT+1000 (Vladivostok Standard Time)

2027-10-31T02:00:00+02:00
2027-10-31T02:00:00+01:00

This issue affects also development process when remote developers run it on their own local machines across the world.

I expect to have the same result which is not related to the server's timezone.

Please correct me if I'm wrong.

CoreyKovalik commented 2 years ago

Seems related to https://github.com/iamkun/dayjs/issues/1412

and also related to: https://github.com/iamkun/dayjs/issues/1408

Serg-Mois commented 2 years ago

The problem is still relevant.

CoreyKovalik commented 2 years ago

The problem is still relevant.

Last November we moved from dayjs to luxon and never looked back. This March we now have no issues traversing the DST boundary :)

Serg-Mois commented 2 years ago

The problem is still relevant.

Last November we moved from dayjs to luxon and never looked back. This March we now have no issues traversing the DST boundary :)

@CoreyKovalik we have a big project, it will be much longer to switch to luxon, due to the API differences.

AComasSamcla commented 2 years ago

a year and a half after this bug was reported and it's still no fixed? damn... should i revert to momentjs after all?

Serg-Mois commented 2 years ago

@AComasSamcla I didn't check, but looks like fixed in https://github.com/iamkun/dayjs/releases/tag/v1.11.2

fix UTC plugin .valueOf not taking DST into account (https://github.com/iamkun/dayjs/issues/1448) (27d1c50)

AComasSamcla commented 2 years ago

@Serg-Mois oh nice, i checked it and seems fixed now, so probably this issue could be closed by @iamkun too. Thanks!

thomasnal commented 1 year ago

The issue remains,

    "dayjs": {
      "version": "1.11.5",

EDIT: updated to 1.11.6, the issue remains.

dayjs.utc("2022-04-22 15:08:45").unix() // wrong, expected: 1650640125, actual: 1650636525 
dayjs.utc("2022-08-22 15:08:45").unix() // wrong, expected: 1661180925, actual: 1661177325
dayjs.utc("2022-01-22 15:08:45").unix() // ok, expected: 1642864125, actual: 1642864125

Verified with unixtimestamp.com and python.

EDIT2: If the time is specified this way then the conversion is correct,

dayjs.utc("2022-04-22T15:08:45.000Z").unix() // ok, expected: 1650640125, actual: 1650640125
WarrenBuffering commented 11 months ago

was there ever a solution for this?

jonah-ullman commented 10 months ago

Just noting that this is still an issue in 1.11.10

sanket-mundada commented 4 months ago

Any updates on this issue? Still facing the same in latest version

vladimircosta11 commented 2 months ago

I am with the same problem.

Aki0x137 commented 2 months ago

Same here, facing issues with DST handling.

nabak9 commented 1 month ago

It's been almost 4 years and still no fix.. I just came across the same issue.
It's happening when subtracting 6 months, off by 1 hour

njoshi22 commented 1 month ago

Ha, I came across the same issue and couldn't figure it out until reading this --- is this still not fixed?

amir-huseinspahic commented 1 week ago

I'm not sure if I got this wrong, but for whatever reason setting the 'keepLocalTime' to 'false' will correctly display the time in regions affected by DST.

I have a function like this:

    function getHRT(datetime) {
        return dayjs(datetime)
            .tz(timezone, false) // Put the 'false' here when setting the timezone.
            .format(date_format + ', ' + time_format);
    }

I'm working with a VILT stack, not sure if that has anything to do with it. Can someone else please confirm?

triemstr commented 2 days ago

@amir-huseinspahic I don't think it is meant for that. See when keepLocalTime was introduced: https://github.com/iamkun/dayjs/issues/1149

It seems to me that if you want to say it is 6:00 in timezone 1 and want to change it to 6:00 in timezone 2, then pass in keepLocalTime = true. Otherwise, it will change the time to match the same moment 6:00 in timezone 1.

If keepLocalTime works, that's interesting, but it may not always.

triemstr commented 2 days ago

Workaround for me due to confusion on timezones is to use Date directly for this purpose (which I only need in one spot in my code) whereas I use dayjs in many areas.

const date1 = new Date('December 1, 1975 23:15:30 GMT');
const date2 = new Date('August 1, 1975 23:15:30 GMT');
const date3 = new Date('January 1, 1975 23:15:30 GMT');

console.log(date1.getTimezoneOffset());
// Expected output: your local timezone offset in minutes from GMT
// In Pacific time (your results will vary): 480.

console.log(date2.getTimezoneOffset());
// Expected output: your local timezone offset in minutes from GMT
// In Pacific time (your results will vary): 420.

console.log(date1.getTimezoneOffset() === date2.getTimezoneOffset());
// Expected output: false -- Since different offsets due to daylight savings in summer.

console.log(date1.getTimezoneOffset() === date3.getTimezoneOffset());
// Expected output: true -- Same offset in winter despite two different winter dates.

tl;dr - I really just use this only in my code:

const tzOffsetHours = new Date().getTimezoneOffset()/60;
console.log(tzOffsetHours);
// Expected output today, September 17, 2024 (still daylight savings) in the Pacific Time zone in hours: 7

You could instead pull your dayjs formatted date and put into the Date object or compare to the system time somehow or find some other way to use the IANA database if issues with dayjs until dayjs is updated. I played with Date code here: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/getTimezoneOffset

Another workaround: https://github.com/iamkun/dayjs/issues/1388#issuecomment-1436092421