Reputation: 34553
I have a use case where a user selects a Date and Time in the future in their time zone. In this case, US Central Time. I would like to convert the Date given their timezone into Epoch seconds, which I need to pass to a 3rd party API.
Here's part of my unit test code so far:
moment.tz.add('America/Chicago');
// This value is returned by Mongoose as a Date, which is why I'm using Date() here
const publishAt = new Date('2017-07-26T12:00:00.000Z');
const publishAtEpoch = moment.tz(publishAt, 'America/Chicago').valueOf() / 1000;
Which results in:
1501070400
instead of:
1501088400
which is incorrect according to: https://www.epochconverter.com/timezones?q=1501070400&tz=America%2FChicago
The value 1501070400 is 5 hours too early.
If I pass in a naive string for the Date: 2017-07-26 12:00:00
it works as expected.
If I use a naive Date: new Date('2017-07-26 12:00:00');
the value is 1 hour too early, as my timezone is US Eastern.
What am I doing wrong? I thought moment-timezone was supposed to handle the offset, account for Daylight Savings Time, etc.
Upvotes: 1
Views: 1185
Reputation: 31482
Your publishAt
Date is created using an string that ends with Z
, so it represents time in UTC. As you can see publishAt
(getTime()
) value in milliseconds since the Unix Epoch is 1501070400000
.
1501070400000
is 26 July 2017 12:00:00 UTC
(see records with 0
Offset
In seconds in the table in your link).
In my opinion, the problem is that you are expecting to get 1501088400000
instead of 1501070400000
that is the "real" value of new Date('2017-07-26T12:00:00.000Z')
.
moment.tz('2017-07-26 12:00:00', 'America/Chicago')
gives 1501088400000
because you are telling to moment to parse your string as time in Chicago, while new Date('2017-07-26 12:00:00')
creates date for your local timezone.
Please note that moment-timezone docs states:
Unix timestamps and
Date
objects refer to specific points in time, thus it doesn't make sense to use the time zone offset when constructing. Usingmoment.tz(Number|Date, zone)
is functionally equivalent tomoment(Number|Date).tz(zone)
.
Here a snippet showing the described results:
const publishAt = new Date('2017-07-26T12:00:00.000Z');
console.log(publishAt.getTime()); // 1501070400000
const publishAtEpoch = moment.tz(publishAt, 'America/Chicago').valueOf() / 1000;
console.log(publishAtEpoch); // 1501070400
const timeChicago = moment.tz('2017-07-26T12:00:00.000', 'America/Chicago');
console.log(timeChicago.valueOf() / 1000); // 1501088400
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.18.1/moment.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment-timezone/0.5.13/moment-timezone-with-data-2012-2022.min.js"></script>
Here a snippet that parses milliseconds (both 1501070400000
and 1501088400000
), then it shows the value in local time, UTC and Chicago timezone. This is something similar to what epochconverter.com does and maybe could help you understand why the site is showing these results.
const m1Local = moment(1501070400000);
console.log(m1Local.format()); // 1501070400000 local time
const m1Utc = moment(1501070400000).utc();
console.log(m1Utc.format()); // 2017-07-26T12:00:00Z
const m1Chicago = moment(1501070400000).tz('America/Chicago');
console.log(m1Chicago.format()); // 2017-07-26T07:00:00-05:00
const m2Local = moment(1501088400000);
console.log(m2Local.format()); // 1501088400000 local time
const m2Utc = moment(1501088400000).utc();
console.log(m2Utc.format()); // 2017-07-26T17:00:00Z
const m2Chicago = moment(1501088400000).tz('America/Chicago');
console.log(m2Chicago.format()); // 2017-07-26T12:00:00-05:00
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.18.1/moment.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment-timezone/0.5.13/moment-timezone-with-data-2012-2022.min.js"></script>
As additional info, note that moment has unix()
method that:
moment#unix
outputs a Unix timestamp (the number of seconds since the Unix Epoch).This value is floored to the nearest second, and does not include a milliseconds component.
if you want to avoid to divide by 1000
the value returned by valueOf()
.
Further readings that could be useful: Local vs UTC vs Offset guide, timezone tag info page.
Upvotes: 2