It's impossible to calculate the difference between two arbitrary time zones. You can only calculate a difference for a specific moment in time.
- It's currently 4 hours difference between London and New York (writing this on Mar 25, 2015).
- But it was 5 hours difference a few weeks ago, and it will be 5 a few weeks from now.
- Each time zone switches offsets for daylight saving time at a different point in time.
This is true in the general case between two time zones. However some time zones either switch exactly at the same time, or don't switch at all.
- There is always one hour between London and Paris, because they switch at the same moment in time.
- There are always 3 hours between Arizona and Hawaii, because neither have DST.
Keep in mind that in the United States, each time zone that uses DST actually switches at a different moment in time. They all switch at 2:00 AM in their local time, but not at the same universal moment in time.
- So between Chicago and New York, there is usually 1 hour apart
- But for two brief periods each year they are either 2 hours apart, or have the same exact time.
See also "Time Zone != Offset" in the timezone tag wiki.
Now with regard to moment-timezone, you said in comments:
The web server is in the Eastern time zone; we're in Central. I need the difference in the user's timezone and the server.
The time zone of the web server is irrelevant. You should be able to host from anywhere in the world without affecting your application. If you can't, then you're doing it wrong.
You can get the current time difference between your time zone (US Central time) and the user's. You don't even need to know the user's exact time zone for this, if the code is running in the browser:
var now = moment();
var localOffset = now.utcOffset();
now.tz("America/Chicago"); // your time zone, not necessarily the server's
var centralOffset = now.utcOffset();
var diffInMinutes = localOffset - centralOffset;
If instead the code was running on the server (in a node.js app), then you would need to know the user's time zone. Just change the first line like this:
var now = moment.tz("America/New_York"); // their time zone
Updated answer:
This can be done without Moment, in environments that support the ECMAScript Internationalization API and have fully implemented IANA time zone support. This is most browsers these days.
function getTimeZoneOffset(date, timeZone) {
// Abuse the Intl API to get a local ISO 8601 string for a given time zone.
let iso = date.toLocaleString('en-CA', { timeZone, hour12: false }).replace(', ', 'T');
// Include the milliseconds from the original timestamp
iso += '.' + date.getMilliseconds().toString().padStart(3, '0');
// Lie to the Date object constructor that it's a UTC time.
const lie = new Date(iso + 'Z');
// Return the difference in timestamps, as minutes
// Positive values are West of GMT, opposite of ISO 8601
// this matches the output of `Date.getTimeZoneOffset`
return -(lie - date) / 60 / 1000;
}
Example usage:
getTimeZoneOffset(new Date(2020, 3, 13), 'America/New_York') //=> 240
getTimeZoneOffset(new Date(2020, 3, 13), 'Asia/Shanghai') //=> -480
If you want the difference between them, you can simply subtract the results.
Node.js
The above function works in Node.js where the full-icu
internationalization support is installed (which is the default for Node 13 and newer). If you have an older version with either system-icu
or small-icu
, you can use this modified function. It will work in browsers and full-icu
environments also, but is a bit larger. (I have tested this on Node 8.17.0 on Linux, and Node 12.13.1 on Windows.)
function getTimeZoneOffset(date, timeZone) {
// Abuse the Intl API to get a local ISO 8601 string for a given time zone.
const options = {timeZone, calendar: 'iso8601', year: 'numeric', month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit', second: '2-digit', hour12: false};
const dateTimeFormat = new Intl.DateTimeFormat(undefined, options);
const parts = dateTimeFormat.formatToParts(date);
const map = new Map(parts.map(x => [x.type, x.value]));
const year = map.get('year');
const month = map.get('month');
const day = map.get('day');
const hour = `${map.get('hour') % 24}`.padStart(2, "0"); // Sometimes hour value comes as 24
const minute = map.get('minute');
const second = map.get('second');
const ms = date.getMilliseconds().toString().padStart(3, '0');
const iso = `${year}-${month}-${day}T${hour}:${minute}:${second}.${ms}`;
// Lie to the Date object constructor that it's a UTC time.
const lie = new Date(iso + 'Z');
// Return the difference in timestamps, as minutes
// Positive values are West of GMT, opposite of ISO 8601
// this matches the output of `Date.getTimeZoneOffset`
return -(lie - date) / 60 / 1000;
}
Note that either way, we must go through Intl
to have the time zone applied properly.
(moment().tz("America/New_York").zone() - moment().zone()) / 60
? I guess that's a little better. – Gillis