关于时区:如何将JavaScript日期初始化为特定时区

How to initialize a JavaScript Date to a particular time zone

我将特定时区中的日期时间作为字符串,并希望将其转换为本地时间。但是,我不知道如何在日期对象中设置时区。

例如,我有Feb 28 2013 7:00 PM ET,,那么我可以

1
2
3
4
5
6
var mydate = new Date();
mydate.setFullYear(2013);
mydate.setMonth(02);
mydate.setDate(28);
mydate.setHours(7);
mydate.setMinutes(00);

据我所知,我可以设置UTC时间或本地时间。但是,如何在另一个时区设置时间?

我试着用加减法从UTC中减去偏移量,但我不知道如何抵消日光节约。我不确定我是否朝着正确的方向走。

如何在javascript中将时间从不同时区转换为本地时间?


背景

javascript的Date对象在内部跟踪UTC中的时间,但通常在运行它的计算机的本地时间接受输入和输出。它没有在其他时区工作的任何设施。它可以解析和输出UTC或本地日期,但不能直接与其他时区一起工作。

准确地说,Date对象的内部表示是一个单一的数字,表示自1970-01-01 00:00:00 UTC以来经过的毫秒数,而不考虑跳跃秒。日期对象本身不存储时区或字符串格式。当使用Date对象的各种功能时,计算机的本地时区应用于内部表示。如果函数生成一个字符串,那么可以考虑计算机的区域设置信息来确定如何生成该字符串。每个函数的详细信息各不相同,有些是特定于实现的。

图书馆

幸运的是,有一些库可以用来处理时区。虽然它们仍然不能使Date对象的行为有任何不同,但它们通常实现标准的olson/iana时区数据库,并提供在javascript中使用它的函数。如果您在Web浏览器中运行,有些会产生开销,因为如果您想要整个过程,数据库可能会变得有点大。幸运的是,这些库中的许多都允许您有选择地选择要支持的区域,从而使数据大小更易于使用。还有一些使用现代功能从IntlAPI获取时区数据,而不必自己发送。

我知道有几个图书馆:

  • 卢克森(Moment.js的继承人)
  • 矩时区(moment.js的扩展)
  • JS JoDA(Java JS端口)
  • 日期Fns TZ/日期Fns时区(日期Fns的延长)
  • BigEasy/时区
  • Walltime JS(已停产)
  • TimeZunNeJS
  • TZ.JS

Luxon可能是所有现代应用中最安全的赌注,并且是最轻的重量,因为它使用IntlAPI作为时区数据。

Moment TimeZone是Moment.js的扩展,并带来自己的时区数据。

JS JoDA是JordaTime API(来自Java)的JavaScript实现,并通过单独的模块包括时区支持。

日期Fns Tz是日期Fns 2.x的扩展。日期Fns时区是日期Fns 1.x的扩展。

BigEasy/TimeZone似乎也在正确的轨道上。

WalltimeJS已经到了生命的尽头,所有者正在迁移到Moment时区。

Timezonejs是最长的,但已知有一些长期存在的错误,特别是在夏时制转换附近。希望在将来的某个时候可以解决这些问题。

TZ.JS也存在一段时间了,但是没有很好的记录,imho。

您应该评估这些库,看看哪一个可以满足您的需求。如果不确定,请选择"时刻/时刻"时区。

现代浏览器中的本机支持

如果您可以将您的使用限制在现代Web浏览器上,那么现在您可以在不使用任何特殊库的情况下执行以下操作:

1
new Date().toLocaleString("en-US", {timeZone:"America/New_York"})

这不是一个全面的解决方案,但它适用于许多只需要输出转换的场景(从UTC或本地时间到特定时区,但不适用于其他方向)。这是ECMAScript国际化API(ECMA-402)的一部分。详情请参阅本帖。此兼容性表跟踪支持哪些版本。这是上面提到的IntlAPI,某些库现在正在内部使用。

未来的建议

TC39临时提案旨在提供一组新的标准对象,用于使用JavaScript语言本身的日期和时间。这将包括对时区感知对象的支持。


您可以在new Date()上指定时区偏移,例如:

1
new Date('Feb 28 2013 19:00:00 EST')

1
new Date('Feb 28 2013 19:00:00 GMT-0500')

由于Date存储UTC时间(即getTime返回UTC),javascript将把时间转换成UTC,当你调用toString之类的东西时,javascript将把UTC时间转换成浏览器的本地时区,并在本地时区返回字符串,即如果我使用UTC+8时:

1
2
> new Date('Feb 28 2013 19:00:00 GMT-0500').toString()
<"Fri Mar 01 2013 08:00:00 GMT+0800 (CST)"

也可以使用普通的getHours/Minute/Second方法:

1
2
> new Date('Feb 28 2013 19:00:00 GMT-0500').getHours()
< 8

(此8表示在时间转换成我的本地时间UTC+8后,小时数为8。)


正如马特约翰逊所说

If you can limit your usage to modern web browsers, you can now do the
following without any special libraries:

new Date().toLocaleString("en-US", {timeZone:"America/New_York"})

This isn't a comprehensive solution, but it works for many scenarios
that require only output conversion (from UTC or local time to a
specific time zone, but not the other direction).

因此,尽管浏览器在创建日期时无法读取IANA时区,或者有任何方法可以更改现有日期对象上的时区,但似乎有一个漏洞:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
  function changeTimezone(date,ianatz) {

     // suppose the date is 12:00 UTC
     var invdate = new Date(date.toLocaleString('en-US', {
        timeZone: ianatz
     }));

     // then invdate will be 07:00 in Toronto
     // and the diff is 5 hours
     var diff = date.getTime()-invdate.getTime();

     // so 12:00 in Toronto is 17:00 UTC
     return new Date(date.getTime()+diff);

   }

用法

1
2
var there = new Date(when);
var here = changeTimezone(there,"America/Toronto");


我在单元测试中遇到了类似的问题(特别是在jest中,当单元测试在本地运行以创建快照,然后CI服务器在(可能的)不同时区运行时,导致快照比较失败)。我嘲笑了我们的Date和一些支持方法,比如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
describe('...', () => {
  let originalDate;

  beforeEach(() => {
    originalDate = Date;
    Date = jest.fn(
      (d) => {
        let newD;
        if (d) {
          newD = (new originalDate(d));
        } else {
          newD = (new originalDate('2017-05-29T10:00:00z'));
        }
        newD.toLocaleString = () => {
          return (new originalDate(newD.valueOf())).toLocaleString("en-US", {timeZone:"America/New_York"});
        };
        newD.toLocaleDateString = () => {
          return (new originalDate(newD.valueOf())).toLocaleDateString("en-US", {timeZone:"America/New_York"});
        };
        newD.toLocaleTimeString = () => {
          return (new originalDate(newD.valueOf())).toLocaleTimeString("en-US", {timeZone:"America/New_York"});
        };
        return newD;
      }
    );
    Date.now = () => { return (Date()); };
  });

  afterEach(() => {
    Date = originalDate;
  });

});

我知道已经晚了3年了,但也许它可以帮助其他人,因为我没有发现类似的东西,除了时刻时区库,这和他在这里要求的不完全一样。

我为德国时区做了类似的事情,这有点复杂,因为夏令时和闰年有366天。

它可能需要一些"isDaylightSavingTimeingerMany"功能的工作,而不同的时区在不同的时间变化夏令时。

无论如何,请查看此页面:https://github.com/zerkotin/german-timezone-converter/wiki

主要方法有:将本地日期转换为时区将德国日期转换为本地时区

我努力把它记录下来,这样它就不会那么混乱了。


我发现在不担心第三方库的情况下,最受支持的方法是使用getTimezoneOffset计算适当的时间戳,或者更新时间,然后使用常规方法获取必要的日期和时间。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
var mydate = new Date();
mydate.setFullYear(2013);
mydate.setMonth(02);
mydate.setDate(28);
mydate.setHours(7);
mydate.setMinutes(00);

// ET timezone offset in hours.
var timezone = -5;
// Timezone offset in minutes + the desired offset in minutes, converted to ms.
// This offset should be the same for ALL date calculations, so you should only need to calculate it once.
var offset = (mydate.getTimezoneOffset() + (timezone * 60)) * 60 * 1000;

// Use the timestamp and offset as necessary to calculate min/sec etc, i.e. for countdowns.
var timestamp = mydate.getTime() + offset,
    seconds = Math.floor(timestamp / 1000) % 60,
    minutes = Math.floor(timestamp / 1000 / 60) % 60,
    hours   = Math.floor(timestamp / 1000 / 60 / 60);

// Or update the timestamp to reflect the timezone offset.
mydate.setTime(mydate.getTime() + offset);
// Then Output dates and times using the normal methods.
var date = mydate.getDate(),
    hour = mydate.getHours();

编辑

我以前在执行日期转换时使用UTC方法,这是不正确的。通过将偏移量添加到时间中,使用本地get函数将返回所需的结果。


尝试使用NPM的CTOC。https://www.npmjs.com/package/ctoc_时区

它有简单的功能来改变时区(大多数时区在400左右)和所有你想要它显示的自定义格式。


尝试:从时区开始,在本地可用的Intl.DateTimeFormat的帮助下解决预期日期。

我在我的一个项目中使用了这种方法已经有几年了,但现在我决定把它作为一个小型操作系统项目来发布:)


面对同样的问题,用了这个

console.log(date.parse("2018年6月13日10:50:39 GMT+1"));

它将返回毫秒,你可以检查到有+100时区初始化英国时间。希望它有帮助!!