moment-timezone icon indicating copy to clipboard operation
moment-timezone copied to clipboard

moment.tz(timezone, keepTime=true) doesn't keep time for days of DST switch

Open iximiuz opened this issue 6 years ago • 10 comments

Could somebody explain the next behaviour:

I have a UTC time. Let's say 2018-11-02T05:00:00.000Z. And I want to change the timezone to America/Los_Angeles, but keep the 2018-11-02T05:00:00.000 part. Looks like moment('2018-11-02T05:00:00.000Z').tz('America/Los_Angeles', true) is a solution for this task. But for days and timezones when there is a DST/STD transition it produces strange results (for example for 4th of November in this timezone it gives me 4 AM instead of 5AM local time).

// run.js
const moment = require('moment-timezone');
const tz = 'America/Los_Angeles';

const t1 = moment('2018-11-02T05:00:00.000Z');
console.log(t1.format(), moment(t1).tz(tz, true).format(), moment(t1).tz(tz, true).toISOString());

const t2 = moment('2018-11-03T05:00:00.000Z');
console.log(t2.format(), moment(t2).tz(tz, true).format(), moment(t2).tz(tz, true).toISOString());

const t3 = moment('2018-11-04T05:00:00.000Z');
console.log(t3.format(), moment(t3).tz(tz, true).format(), moment(t3).tz(tz, true).toISOString());

const t4 = moment('2018-11-05T05:00:00.000Z');
console.log(t4.format(), moment(t4).tz(tz, true).format(), moment(t4).tz(tz, true).toISOString());

$ TZ=UTC node run.js
2018-11-02T05:00:00+00:00 2018-11-02T05:00:00-07:00 2018-11-02T12:00:00.000Z
2018-11-03T05:00:00+00:00 2018-11-03T05:00:00-07:00 2018-11-03T12:00:00.000Z
2018-11-04T05:00:00+00:00 2018-11-04T04:00:00-08:00 2018-11-04T12:00:00.000Z  // !!! 4 instead of 5
2018-11-05T05:00:00+00:00 2018-11-05T05:00:00-08:00 2018-11-05T13:00:00.000Z

At the same time if I change the time to 2018-11-03T10:00:00.000Z everything works as expected. I suspect because the DST/STD happens at 9AM UTC.

node version: v8.7.0
moment version: 2.22.2
moment-timezone version: 0.5.21

iximiuz avatar Jul 09 '18 16:07 iximiuz

I experience the same issue

navasiloy avatar Jul 12 '18 09:07 navasiloy

@Ostrovski do you use somewhere in the code something like this moment.tz.setDefault(timezone); ?

navasiloy avatar Jul 12 '18 13:07 navasiloy

@navasiloy no, I don't use this function. The minimal code to reproduce the problem one can find in the original issue.

iximiuz avatar Jul 12 '18 15:07 iximiuz

I've also experienced the same issue. Here's what I've discovered.

Let's say that the local machine is running on the Europe/London timezone. If we happen to convert a time in America/Chicago on the day of daylight savings time (Clock moves forward an hour on 2019-03-10T02:00:00.000), there will be a five hour window (from 3 to 8) where console.log(moment('2019-03-10T03:00:00.000').tz('America/Chicago', true).toJSON());

will convert the time to UTC using a six hour offset instead of a five hour offset.

However, if my local timezone is in America/Chicago, console.log(moment('2019-03-10T03:00:00.000').tz('America/Chicago', true).toJSON()); will convert the time to UTC with the correct offset.

An easy way to recreate this is the following:

Wrong UTC time moment.tz.setDefault('Europe/London'); console.log(moment('2019-03-10T07:00:00.000').tz('America/Chicago', true).toJSON());

Correct UTC time moment.tz.setDefault('America/Chicago'); console.log(moment('2019-03-10T07:00:00.000').tz('America/Chicago', true).toJSON());

johnkoehn avatar Sep 13 '18 12:09 johnkoehn

Is there any solution to this issue?

raliasadil avatar Jan 28 '19 17:01 raliasadil

I hope the solution below helps @raliasadil. My scenario for the solution is a bit strange. Basically I had users who wanted to select a time and a timezone and have that be the date for a change to occur to a system. For example, they wanted a change to occur at 12:00 in America/Chicago time but they were in London. The API was in UTC.

const selectedTimeZone = 'America/Chicago';
const localTimeZone = moment.tz.guess();
moment.tz.setDefault(selectedTimeZone);
const effectiveDate = moment('2019-03-10T07:00:00.000').tz(selectedTimeZone, true).toJSON();
moment.tz.setDefault(localTimeZone);
console.log(effectiveDate);

At this point I'd recommended using Luxon. Unfortunately on UI projects, a lot of UI components use moment as a first class citizen. The shift to Luxon will take a couple years.

johnkoehn avatar Feb 10 '19 03:02 johnkoehn

Same issue here. "Fixed" with the moment.tz.setDefault(userTimezone) workaround...

My scenario :

1- User can choose the starting hour of his working day

2- I use the below code to retrieve current day and starting at the moment chosen by user

const startfulldate = moment()
.hour(startHourChosenByUser)
.minute(startMinuteChosenByUser)
.second(0)
.millisecond(0)
.tz(userTimezone, true)
.toDate();

3- Code was giving a wrong time (+1 hour) on next DST days (29th March 2020 for France)

4- Fixed by adding the setDefault() function above code

moment.tz.setDefault(userTimezone)

fabienpatou avatar Mar 27 '20 10:03 fabienpatou

We are facing this exact issue in our project

zuzusik avatar Oct 22 '20 21:10 zuzusik

Here is a workaround we ended up using:

moment.tz(momentDate.format('YYYY-MM-DDTHH:mm:ss'), tz)

this is basically the equivalent of doing momentDate.tz(tz, true), but it is not affected by the bug

Note: this workaround doesn't keep milliseconds - if required to keep that as well - .format pattern should be extended

zuzusik avatar Oct 23 '20 15:10 zuzusik

It's Daylight Savings Time again, and I believe I'm hitting this bug. I assume we didn't hit this bug last year because of what someone else said earlier:

there will be a five hour window (from 3 to 8) where [the library] will convert the time to UTC using a six hour offset instead of a five hour offset.

and we didn't happen to observe the behavior before 08:00...

fw-aaron avatar Mar 15 '21 12:03 fw-aaron