如何将JavaScript Date初始化为特定的时区

31 浏览
0 Comments

如何将JavaScript Date初始化为特定的时区

我有一个表示特定时区的日期时间字符串,想将其转换为本地时间。但是我不知道如何在 Date 对象中设置时区。

例如,我有一个 Feb 28 2013 7:00 PM ET,我可以

var mydate = new Date();
mydate.setFullYear(2013);
mydate.setMonth(02);
mydate.setDate(28);
mydate.setHours(7);
mydate.setMinutes(00);  

据我所知,我可以设置 UTC 时间或本地时间。但是,我如何设置其他时区的时间?

我尝试使用从 UTC 中添加/减去偏移量,但是我不知道如何解决夏令时。我不确定我是否朝着正确的方向走。

如何在 JavaScript 中将来自不同时区的时间转换为本地时间?

admin 更改状态以发布 2023年5月23日
0
0 Comments

正如Matt Johnson所说的:

如果你能将你的使用限制在现代的Web浏览器中,你现在可以使用以下方法而无需任何特殊库:

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

这不是一个全面的解决方案,但它适用于许多情况,其需要仅进行输出转换(从UTC或本地时间到特定时区,但不是反向的情况)。

所以,尽管浏览器不能在创建日期时读取IANA时区,或者没有任何方法在现有的Date对象上更改时区,但似乎有一个变通的方法:

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); // needs to substract
}
// E.g.
var here = new Date();
var there = changeTimezone(here, "America/Toronto");
console.log(`Here: ${here.toString()}\nToronto: ${there.toString()}`);

0
0 Comments

背景

JavaScript的Date对象在内部跟踪的是UTC时间,但通常接受输入和产生输出则是根据计算机运行的本地时间。它几乎没有处理其他时区时间的工具。

Date对象的内部表示是一个数字,表示从1970-01-01 00:00:00 UTC以来经过的毫秒数,不考虑闰秒。

Date对象本身没有存储任何时区或字符串格式。

当运用Date对象的各种函数时,计算机的本地时区将应用于内部表示。如果函数产生字符串,则计算机的本地信息可能被考虑在内,以确定如何产生该字符串。详细信息因函数而异,并且有些是特定于实现的。

Date对象可以处理非本地时区的唯一操作是:

  • 它可以解析包含来自任何时区的数值UTC偏移量的字符串。它使用此信息来调整被解析的值,并存储UTC等效值。在生成的Date对象中,原始的本地时间和偏移量不会保留。例如:

      var d = new Date("2020-04-13T00:00:00.000+08:00");
      d.toISOString()  //=> "2020-04-12T16:00:00.000Z"
      d.valueOf()      //=> 1586707200000  (this is what is actually stored in the object)
    

  • 在实现了ECMA国际化API(又名“Intl”)的环境中,Date对象可以产生针对给定时区标识符调整的地方特定字符串。这是通过toLocaleString及其变体的timeZone选项实现的。大多数实现将支持IANA时区标识符,如'America/New_York'。例如:

      var d = new Date("2020-04-13T00:00:00.000+08:00");
      d.toLocaleString('en-US', { timeZone: 'America/New_York' })
      //=> "4/12/2020, 12:00:00 PM"
      // (midnight in China on April 13th is noon in New York on April 12th)
    

    大多数现代环境都支持完整的IANA时区标识符集合(可以在这里查看兼容性表)。但要注意,Intl要求支持的唯一标识符是'UTC',因此如果需要支持旧版本浏览器或非典型环境(例如轻量级IoT设备),请仔细检查。

有几个库可用于处理时区。虽然它们仍然无法使Date对象表现不同,但它们通常实现了标准的IANA时区数据库,并提供了在JavaScript中使用它的函数。现代库使用Intl API提供的时区数据,但旧的库通常有开销,特别是如果您在Web浏览器中运行,因为数据库可能会变得有点大。这些库中的一些还允许您有选择地减少数据集,无论是根据支持的时区还是可用的日期范围。

以下是需要考虑的库:

基于Intl的库

新开发应从以下实现之一中选择,它们依赖于Intl API来获取它们的时区数据:

非Intl的库

这些库是维护的,但是需要打包自己的时区数据,这可能会非常大。

*虽然以前建议使用Moment和Moment-Timezone,但现在Moment团队更喜欢用户选择新开发用Luxon。

停用的库

这些库已正式停用,不应再使用。

未来提议

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

常见错误

经常尝试的几种方法是错误的,通常应该避免使用。

重新解析

new Date(new Date().toLocaleString('en', {timeZone: 'America/New_York'}))

上述方法正确地使用Intl API在特定时区创建字符串,但然后错误地将该字符串传递回Date构造函数。 在这种情况下,解析将是特定于实现的,并且可能完全失败。 如果成功,计算机的本地时区将在解析期间应用,结果可能是Date 对象现在表示错误的时间点。

时间位移

var d = new Date();
d.setTime(d.getTime() + someOffset * 60000);

上面的方法试图通过将Unix时间戳位移来操作Date对象的时区。然而,由于Date对象仅以UTC跟踪时间,它实际上只是使Date对象代表不同的时间点。

有时会直接在构造函数上使用相同的方法,但这也是无效的。

时间位移有时在日期库内部被用作快捷方式,以避免编写日历日算法。在这样做时,必须避免访问非UTC属性。例如,一旦位移,调用getUTCHours是可以接受的,但调用getHours是无效的,因为它使用本地时区。

它被称为“时间位移”,因为正确使用时,Unix纪元(1970-01-01T00:00:00.000Z)已不再与时间戳0相关联,而是通过偏移量的量移动到不同的时间戳。

如果您不是编写日期库,您不应该进行时间位移。

有关更多关于时间位移的详情,请观看Greg Miller在CppCon 2015的此视频。该视频是关于C++中的time_t,但解释和问题是相同的。(对于JavaScript的开发者来说,每当您听到Greg提到time_t,只需想到“Date对象”。)

尝试创造一个“UTC日期”

var d = new Date();
var utcDate = new Date(Date.UTC(d.getUTCFullYear(), d.getUTCMonth(), d.getUTCDate(), d.getUTCHours(), d.getUTCMinutes(), d.getUTCSeconds(), d.getUTCMilliseconds()));

在这个例子中,dutcDate是相同的。构建utcDate的工作是多余的,因为d已经以UTC为单位。检查toISOStringgetTimevalueOf函数的输出将显示两个变量的相同值。

另一种类似的方法是:

var d = new Date();
var utcDate = new Date(d.getUTCFullYear(), d.getUTCMonth(), d.getUTCDate(), d.getUTCHours(), d.getUTCMinutes(), d.getUTCSeconds(), d.getUTCMilliseconds());

这种方法将UTC值传递到期望本地时间值的Date构造函数中。生成的Date对象现在表示完全不同的时间点。这本质上是之前描述的时间位移相同的结果,因此应该避免使用。

获得基于UTC的Date对象的正确方法是使用new Date()。如果需要一个基于UTC的字符串表示形式,则使用new Date().toISOString()

0