关于datetime:在JavaScript中给定TimeZone字符串计算UTC偏移量

Calculate the UTC offset given a TimeZone string in JavaScript

使用标准JS库(ECMA5),不使用momentjs或外部库,如何在给定TimeZone字符串(如"Europe / Rome"或"America / Los_Angeles")的情况下计算UTC偏移量?

UTC偏移可能取决于它是否为DST,因此如果解决方案需要将本地客户端日期转换为指定的时区字符串,则有意义。 目标只是知道UTC的偏移量。

1
2
3
4
5
function getUtcOffset(timezone) {
  // return int value.
  // > 0 if +GMT
  // < 0 if -GMT.
}

ECMAScript(ECMA-262)中没有可以执行您请求的操作的功能。这只是因为标准ECMAScript对本地计算机和UTC之外的时区一无所知。

但是,在支持ECMAScript国际化API(ECMA-402)并完全支持IANA时区数据库标识符的浏览器中,您可以将这样的函数组合在一起:

1
2
3
4
5
6
7
function getTimezoneOffset(d, tz) {
  var a = d.toLocaleString("ja", {timeZone: tz}).split(/[\/\s:]/);
  a[1]--;
  var t1 = Date.UTC.apply(null, a);
  var t2 = new Date(d).setMilliseconds(0);
  return (t2 - t1) / 60 / 1000;
}

这适用于当前版本的Chrome,也许还可以在其他一些地方使用。但它无疑保证无处不在。特别是,它不适用于任何版本的Internet Explorer浏览器。

用法示例(在Chrome中):

1
2
3
4
getTimezoneOffset(new Date(2016, 0, 1),"America/New_York") // 300
getTimezoneOffset(new Date(2016, 6, 1),"America/New_York") // 240
getTimezoneOffset(new Date(2016, 0, 1),"Europe/Paris") // -60
getTimezoneOffset(new Date(2016, 6, 1),"Europe/Paris") // -120

有关此特定功能的一些注意事项:

  • 就像我提到的那样,它无处不在。最终,由于所有浏览器都会达到现代标准,但目前还不会。

  • 传入的日期确实会影响结果。这是由于夏令时和其他时区异常造成的。您可以使用new Date()传递当前日期,但结果将根据您调用函数的时间而更改。请参阅timezone标记wiki中的"time zone!= offset"。

  • 此函数的结果与Date.getTimezoneOffset相同 - 以分钟为单位,正值为UTC的西。如果您正在使用ISO8601偏移,则需要转换为小时并反转符号。

  • 该函数依赖于toLocaleString函数的时区格式化功能。我选择了'ja'文化,因为日期部分已经按照数组的正确顺序。这确实是一个黑客行为。理想情况下,有一个API可以让您访问时区信息,而不会在格式化时将其绑定到区域设置。不幸的是,这个特定API的设计者错误地将时区与区域设置相关联。这是来自各种语言的一些其他API所犯的错误,不幸的是这里被带入了JavaScript。

    明确重申:ECMA-402中唯一的时区功能是在格式化字符串时应用时区,这是一个设计缺陷,恕我直言。

  • 我上面的示例用法部分中有一个错误,它举例说明了此API错误的部分原因。特别是,在创建Date对象时无法指定时区。我传入的1月1日和7月1日日期是在本地时区创建的,而不是在指定的时区中创建的。因此,输出可能与转换附近的预期不完全相同。为了解决这个问题,可能会被黑客攻击,但我会将其作为练习留给您。

再次 - 虽然这个答案满足要求的标准,因为没有涉及外部库,我强烈建议不要在任何生产代码中使用它。如果你打算做一些重要的事情,我会使用我在这里列出的一个库。我的个人偏好是moment.js与moment-timezone插件,因为我帮助维护该库。因人而异


你查看了时刻时区吗?

1
moment.tz("America/Los_Angeles").utcOffset();


你必须:

  • 获取当前日期时间(将为您提供当前设备当前时区的日期时间)
  • 获取当前时区偏移量
  • 将日期时间转换为新/所需时区
  • 获取当前日期时间和转换后的日期时间之间的差异
  • 将所述差异添加到当前时区偏移量

例如,以小时为单位返回时区:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
function getTimezoneOffset(tz, hereDate) {
    hereDate = new Date(hereDate || Date.now());
    hereDate.setMilliseconds(0); // for nice rounding

    const
    hereOffsetHrs = hereDate.getTimezoneOffset() / 60 * -1,
    thereLocaleStr = hereDate.toLocaleString('en-US', {timeZone: tz}),
    thereDate = new Date(thereLocaleStr),
    diffHrs = (thereDate.getTime() - hereDate.getTime()) / 1000 / 60 / 60,
    thereOffsetHrs = hereOffsetHrs + diffHrs;

    console.log(tz, thereDate, 'UTC'+(thereOffsetHrs < 0 ? '' : '+')+thereOffsetHrs);
    return thereOffsetHrs;
}


getTimezoneOffset('America/New_York', new Date(2016, 0, 1));
getTimezoneOffset('America/New_York', new Date(2016, 6, 1));
getTimezoneOffset('Europe/Paris', new Date(2016, 0, 1));
getTimezoneOffset('Europe/Paris', new Date(2016, 6, 1));
getTimezoneOffset('Australia/Sydney', new Date(2016, 0, 1));
getTimezoneOffset('Australia/Sydney', new Date(2016, 6, 1));
getTimezoneOffset('Australia/Sydney');
getTimezoneOffset('Australia/Adelaide');

哪个输出像

1
2
3
4
5
6
7
8
America/New_York 2015-12-30T22:00:00.000Z UTC-5
America/New_York 2016-06-30T01:00:00.000Z UTC-4
Europe/Paris 2015-12-31T04:00:00.000Z UTC+1
Europe/Paris 2016-06-30T07:00:00.000Z UTC+2
Australia/Sydney 2015-12-31T14:00:00.000Z UTC+11
Australia/Sydney 2016-06-30T15:00:00.000Z UTC+10
Australia/Sydney 2019-08-14T03:04:21.000Z UTC+10
Australia/Adelaide 2019-08-14T02:34:21.000Z UTC+9.5

对于可能出现在这个问题上的其他人,JavaScript中的标准Date函数有一种方法可以获得UTC的偏移,称为getTimezoneOffset。

用法很简单(例如):new Date()。getTimezoneOffset()。这将返回当前时区与UTC的分钟数。注意:如果当前时区早于UTC,则返回负数。例如,如果用户的时区是UTC + 1,则返回-60。