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

IT技术 javascript timezone
2020-12-16 15:58:11

我将特定时区中的日期时间作为字符串,我想将其转换为本地时间。但是,我不知道如何在 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中将时间从不同的时区转换为本地时间?

6个回答

背景

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)
    
  • 在已实现ECMASCRipt 国际化 API(又名“Intl”)的环境中,Date对象可以生成调整为给定时区标识符的特定于语言环境的字符串。这是通过timeZone选项 totoLocaleString及其变体来实现的。大多数实现将支持 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 Apring 13th is noon in New York on April 12th)
    

    大多数现代环境都支持全套 IANA 时区标识符(请参阅此处的兼容性表)。但是,请记住Intl需要支持的唯一标识符'UTC',因此您应该仔细检查是否需要支持旧浏览器或非典型环境(例如,轻量级 IoT 设备)。

图书馆

有几个库可用于处理时区。尽管他们仍然无法使Date对象表现出任何不同,但他们通常实现标准的 IANA 时区数据库并提供在 JavaScript 中使用它的函数。现代库使用 Intl API 提供的时区数据,但较旧的库通常有开销,尤其是在 Web 浏览器中运行时,因为数据库可能会变得有点大。其中一些库还允许您有选择地减少数据集,或者根据支持的时区和/或您可以使用的日期范围。

以下是要考虑的库:

基于国际的图书馆

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

非国际图书馆

这些库得到维护,但承担着打包它们自己的时区数据的负担,这可能非常大。

* 虽然之前推荐了 Moment 和 Moment-Timezone,但 Moment 团队现在更喜欢用户选择 Luxon 进行新开发。

停产的图书馆

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

未来的提案

TC39颞议案旨在提供一套新的标准对象的日期和时间的JavaScript语言本身的工作。这将包括对时区感知对象的支持。

当你这样做的Firebug控制台: var date_time = new Date(),valuedate_time为(在AEST对我来说),Date {Sun Aug 21 2016 22:18:47 GMT+1000 (AEST)}等于是现在看来,这已经存储在时区。我如何确保date_time纯粹是 UTC 时间,而不包括任何时区?
2021-02-11 15:58:11
@redcalx - 这是 Rhino 特定的实现,但即便如此,如果您滚动到最底部,您会看到这private double date是唯一的实例字段。其他一切都是静态的,主要用于缓存环境提供的值。它不是按实例跟踪的内容,也不是您可以轻松修改的内容。
2021-02-16 15:58:11
@user1063287—— toString方法使用主机时区偏移量来生成“本地”时区中的日期和时间。日期对象本身具有一个与 1970-01-01T00:00:00Z 的偏移量的时间值,因此有效地为 UTC。要查看 UTC 日期和时间,请使用toISOString
2021-02-27 15:58:11
Date.UTC函数是根据 UTC 输入计算时间戳。的输出.toString()将是等效的本地时间,并将显示本地时区偏移量、名称或缩写。
2021-02-27 15:58:11
嗯,根据这个答案(stackoverflow.com/a/8047885/1063287),是这样的: new Date().getTime();
2021-03-05 15:58:11

正如马特约翰逊所说

如果您可以将使用限制为现代 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()}`);

@logidelic 这是一篇很新的帖子。老实说,当我想到你可以用它toLocaleString来达到相反的效果时,我很惊讶,是的,这应该是常识!去写一篇关于它的文章并提到我:-)
2021-02-13 15:58:11
“……为什么这个答案没有得到更多的爱?” - 因为Date它返回对象是一个谎言。即使是显示的示例,字符串输出也包括本地机器的时区。在我的电脑上,两个结果显示“GMT-0700(太平洋夏令时间)”,其中只有第一个是正确的(多伦多实际上是东部时间。)除了字符串输出之外,Date对象中保存的时间戳是转移到不同的时间点。可能是一种有用的技术,但有很多警告 - 主要是Date对象函数不会有正常输出。
2021-02-25 15:58:11
浏览器不需要解析toLocaleString的输出,所以它基于错误的前提。
2021-02-26 15:58:11
我想知道为什么这个答案没有得到更多的爱。就我而言,这绝对是最好的解决方案。在我的应用程序中,所有日期都是 UTC,但它们需要在 UI 的显式 IANA 时区中输入或输出。为此,我使用了这个解决方案,它工作得很好。简单、有效、标准。
2021-03-03 15:58:11
对于已经不在 GMT 的节点环境,上面的代码需要从 var invdate = new Date(date.toLocaleString('en-US', { timeZone: ianatz })); 进入 var invdate = new Date(${date.toLocaleString('en-US', { timeZone: ianatz })} GMT`);
2021-03-08 15:58:11

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

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

或者

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

由于Date存储 UTC 时间(即getTime以 UTC 返回),javascript 会将时间转换为 UTC,并且当您调用诸如toStringjavascript 之类的东西时,会将 UTC 时间转换为浏览器的本地时区并返回本地时区中的字符串,即如果我正在使用UTC+8

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

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

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

(这8意味着将时间转换为我的本地时间 - 后UTC+8,小时数为8. )

此评论中的示例在 Internet Explorer 中通常不起作用。正如之前对这篇文章的评论中提到的,ISO 8601 很重要。我通过阅读 ECMA-262(Javascript 5th)版本语言规范来确认。
2021-03-02 15:58:11
解析除 ISO 8601 扩展格式之外的任何格式都取决于实现,不应依赖。时区缩写没有标准,例如“EST”可能代表 3 个不同区域中的任何一个。
2021-03-09 15:58:11

这应该可以解决您的问题,请随时提供修复。此方法还将考虑给定日期的夏令时。

dateWithTimeZone = (timeZone, year, month, day, hour, minute, second) => {
  let date = new Date(Date.UTC(year, month, day, hour, minute, second));

  let utcDate = new Date(date.toLocaleString('en-US', { timeZone: "UTC" }));
  let tzDate = new Date(date.toLocaleString('en-US', { timeZone: timeZone }));
  let offset = utcDate.getTime() - tzDate.getTime();

  date.setTime( date.getTime() + offset );

  return date;
};

如何使用时区和本地时间:

dateWithTimeZone("America/Los_Angeles",2019,8,8,0,0,0)
非常好的解决方案,对我来说只是返回 utcDate 就是我所需要的
2021-02-13 15:58:11
这是一个坏主意,因为没有指定toLocaleString的格式,并且不需要内置解析器来解析它,尤其是在包含选项时。此外,它可能会在观察夏令时的区域产生不可靠的结果,其中要解析的日期和时间不存在(进入 DST)或存在两次(离开 DST),其中实现必须选择如何解决这些问题。
2021-02-14 15:58:11
为什么不直接返回tzDate呢?
2021-02-17 15:58:11
这个解决方案适合我的需要。我不确定我如何准确地解释夏令时。
2021-02-21 15:58:11
在 Node js 服务器中完美运行!(解析云代码)
2021-02-28 15:58:11

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

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函数将返回所需的结果。

你在哪里计算的timezone_offset
2021-02-16 15:58:11
糟糕,我错误地标记了我的一个变量。timezone_offset应该是offset,反之亦然。我已经编辑了我的答案以显示正确的变量名称。
2021-02-21 15:58:11