如何在 JavaScript 中计算 2 个时区的差异?
例如,东部和中部的区别是 1.我下面的解决方案感觉很hacky.有没有更简单/更好的方法?
For example, the difference in Eastern and Central is 1. My solution below feels hacky. Is there a easier / better way?
var diff = (parseInt(moment().tz("America/New_York").format("ZZ")) - parseInt(moment().tz("America/Chicago").format("ZZ"))) / 100;
我的示例是使用 Momentjs 库.
My example is using the Momentjs library.
推荐答案
不可能计算两个任意时区之间的差异.您只能计算特定时刻的差异.
It's impossible to calculate the difference between two arbitrary time zones. You can only calculate a difference for a specific moment in time.
- 目前伦敦和纽约之间的时差为 4 小时(写于 2015 年 3 月 25 日).
- 但几周前相差 5 小时,而几周后将是 5 小时.
- 每个时区在不同时间点切换夏令时偏移量.
- 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.
- 伦敦和巴黎之间总是一小时,因为它们在同一时间切换.
- 亚利桑那州和夏威夷之间总是 3 小时,因为两者都没有夏令时.
- 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.
请注意,在美国,使用 DST 的每个时区实际上会在不同的时间切换.它们都在其本地时间的凌晨 2:00 切换,但不是在同一时间通用.
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.
- 所以在芝加哥和纽约之间,通常相隔 1 小时
- 但每年有两个短暂的时期,它们要么相隔 2 小时,要么具有相同的确切时间.
- 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.
另请参阅时区标签wiki中的时区!=偏移".
See also "Time Zone != Offset" in the timezone tag wiki.
现在关于时刻时区,您在评论中说:
Now with regard to moment-timezone, you said in comments:
网络服务器位于东部时区;我们在中环.我需要用户时区和服务器的差异.
Web 服务器的时区无关紧要.您应该能够在世界任何地方进行托管,而不会影响您的应用程序.如果你不能,那么你做错了.
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;
如果代码在服务器上运行(在 node.js 应用程序中),那么您将需要知道用户的时区.只需像这样更改第一行:
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
更新答案:
这可以在支持 ECMAScript 国际化 API 并已完全实现 IANA 时区支持的环境中完成.这是当今大多数浏览器.
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;
}
示例用法:
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.
上述函数在 full-icu
国际化 支持已安装(这是 Node 13 和更新版本的默认设置).如果您有带有 system-icu
或 small-icu
的旧版本,则可以使用此修改后的功能.它也可以在浏览器和 full-icu
环境中工作,但更大一些.(我在 Linux 上的 Node 8.17.0 和 Windows 上的 Node 12.13.1 上对此进行了测试.)
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');
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;
}
请注意,无论哪种方式,我们必须通过 Intl
才能正确应用时区.
Note that either way, we must go through Intl
to have the time zone applied properly.
相关文章