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

评论

此答案可能会帮助您
日期对象没有时区,它们是UTC。

值得阅读这篇文章medium.com/@toastui/…,结论是您无法使用JavaScript,但可以使用Moment Timezone之类的库

日期对象肯定具有时区。这就是为什么他们有两组不同的getter和setter。一个用于UTC,一个用于本地。

@JohnLord不,本地获取器只是将存储的UTC时间转换为主机时区。例如,在控制台中先执行d = new Date()然后d.getHours(),将计算机的时区更改一个小时,然后再次尝试d.getHours()。 d中没有存储本地时区信息。

#1 楼

背景

JavaScript的Date对象在内部跟踪UTC时间,但通常在运行它的计算机的本地时间接受输入并产生输出。它几乎没有其他时间区域的时间设施。

Date对象的内部表示形式是单个数字,表示自1970-01-01 00:00:00 UTC以来经过的毫秒数,而不考虑leap秒。 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对象可以生成特定于语言环境的字符串调整为给定的时区标识符。这是通过timeZonetoLocaleString选项及其变体来完成的。大多数实现将支持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的库

新开发应从以下实现之一中进行选择,这些实现依赖于Intl API的时区数据:



Luxon(Moment.js的前身)

date-fns-tz(date-fns的扩展名)

非国际图书馆

这些库得到了维护,但承担了打包自己的时区数据的负担,这可能会很大。



js-joda / timezone( js-joda的扩展名)

moment-timezone *(Moment.js的扩展名)

date-fns-timezone(date-fns的较旧1.x的扩展名)
BigEasy / TimeZone

*以前曾建议使用Moment和Moment-Timezone,但Moment团队现在更喜欢用户选择Luxon进行新开发。
< b r />已停产的库

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


WallTime-js
TimeZoneJS

未来的建议

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

评论


由于所代表的时间戳与时区无关,因此请将“代表”更改为“输出/解析”

–贝尔吉
13年4月4日在18:39

@Bergi-我对此进行了重新思考,并同意您的意见。相应地更新了我的答案。

–马特·约翰逊·品特(Matt Johnson-Pint)
2013年6月17日14:37

在Firebug控制台中执行此操作时:var date_time = new Date(),date_time的值是(对我来说是AEST)Date {Sun Aug 21 2016 22:18:47 GMT + 1000(AEST)},因此,它是似乎它已经存储了一个时区。如何确保date_time纯粹是UTC时间,不包含任何时区?

–user1063287
16年8月21日在12:22

@ user1063287-toString方法使用主机时区偏移量来生成“本地”时区中的日期和时间。日期对象本身具有一个时间值,该时间值与1970-01-01T00:00:00Z之间存在偏移,因此实际上是UTC。要查看UTC日期和时间,请使用toISOString。

–RobG
16年8月23日在22:47



Date.UTC函数正在根据所有UTC输入来计算时间戳。 .toString()的输出将是本地时间等效值,并显示本地时区偏移量,名称或缩写。

–马特·约翰逊·品特(Matt Johnson-Pint)
16-09-20在22:53



#2 楼

正如马特·约翰逊(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()}`); 




评论


我想知道为什么这个答案没有得到更多的爱。对我来说,这绝对是最好的解决方案。在我的应用中,所有日期均为UTC,但需要在UI中的明确IANA时区中输入或输出。为此,我使用了此解决方案,并且效果很好。简单,有效,标准。

–逻辑
19年2月11日在13:55

@logidelic这是一个很新的帖子。老实说,当我震惊时,您可以使用toLocaleString达到相反的效果,这让我感到惊讶,是的,这应该是常识!去写一篇关于它的文章并提我:-)

–commonpike
19年2月11日14:36



对于不在GMT中的节点环境,上述代码需要从var invdate = new Date(date.toLocaleString('en-US',{timeZone:ianatz}))更改。放入var invdate = new Date($ {date.toLocaleString('en-US',{timeZone:ianatz})} GMT`);

– Mike P.
19年5月29日在20:28



浏览器不需要解析toLocaleString的输出,因此它基于错误的前提。

–RobG
19年9月8日在6:41

“……为什么这个答案没有得到更多的爱?” -因为它返回的Date对象是一个谎言。即使显示的示例,字符串输出也包含本地计算机的时区。在我的计算机上,两个结果都显示为“ GMT-0700(太平洋夏令时)”,这仅适用于第一个(多伦多实际上是东部时间。)除了字符串输出之外,Date对象中保存的时间戳还具有被转移到另一个时间点。这可能是一种有用的技术,但有很多警告-主要是Date对象函数将不会有正常的输出。

–马特·约翰逊·品特(Matt Johnson-Pint)
4月13日20:48

#3 楼

您可以在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,当您调用toString之类的东西时,javascript将把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。)

评论


解析除ISO 8601扩展格式以外的任何格式均取决于实现,因此不应依赖。没有时区缩写的标准,例如“ EST”可能代表3个不同区域中的任何一个。

–RobG
2016年12月6日4:36



此注释中的示例通常在Internet Explorer中不起作用。如对此帖子的先前评论所述,ISO 8601非常重要。通过阅读ECMA-262(Javascript第5版)语言规范已确认。

– Dakusan
17年9月7日在23:24

#4 楼

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

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)


评论


此解决方案适合我的需要。我不确定我该如何准确地解释夏令时。

–侯赛因·阿敏(Hossein Amin)
19-10-7在9:08

为什么不直接返回tzDate?

– levi
1月15日22:57

在Node js服务器中可以正常工作! (解析云代码)

–华金·佩雷拉(Joaquin Pereira)
10月7日,下午2:23

#5 楼

我发现最受支持的方法而无需担心第三方库,是使用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?

– Anand Somani
19 Mar 12 '19 at 0:02

糟糕,我给我的变量之一贴错了标签。 timezone_offset应该已被抵消,反之亦然。我已经编辑了答案以显示正确的变量名。

– Shaun Cockerill
19年4月11日,1:11

#6 楼

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

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;
  });

});


#7 楼

尝试从npm使用ctoc。
https://www.npmjs.com/package/ctoc_timezone

它具有更改时区(大多数时区大约为400)和所有自定义格式的简单功能希望它显示。

#8 楼

在上面的答案的基础上,我正在使用本机的一个衬纸将较长的时区字符串转换为三个字母的字符串:

var longTz = 'America/Los_Angeles';
var shortTz = new Date().
    toLocaleString("en", {timeZoneName: "short", timeZone: longTz}).
    split(' ').
    pop();


提供的日期。在我的特定用例中,在Salesforce(Aura / Lightning)上进行开发,我们可以从后端以长格式获取用户时区。

评论


仅仅因为我喜欢不时地玩学究,“ America / Los_Angeles”不是时区,而是特定偏移量和夏令时规则以及这些更改的历史记录的“代表位置”(根据IANA时区数据库) 。 ;-)

–RobG
19-09-23在13:20



哈哈,您必须在这里对多个答案发表评论:P

– Shane
19-09-23在14:15

#9 楼

对于Ionic用户而言,我很烦,因为必须将.toISOString()与html模板一起使用。

这将获取当前日期,但是当然可以将其添加到选定日期的先前答案中。 br />
我使用以下方法修复了它:




 date = new Date();
public currentDate: any = new Date(this.date.getTime() - this.date.getTimezoneOffset()*60000).toISOString(); 





* 60000表示UTC -6是CST,因此无论需要什么TimeZone,都可以更改数字和差异。

评论


这不是发问者要寻找的答案。他希望将日期设为特定的时区。

–理查德·韦吉斯(Richard Vergis)
4月29日6:25



@RichardVergis该问题明确指出用户想要更改为本地时间,但没有声明他们所在的时区。我以我的时区为例,用户可以根据我的示例清楚地阅读内容,他们可以输入他们的时区偏移量。

–斯蒂芬·罗梅罗(Stephen Romero)
4月29日下午13:07



#10 楼

尝试:date-from-time-timezone,它借助本地可用的Intl.DateTimeFormat解析预期的日期。

我已经在我的一个项目中使用该方法几年了,但是现在我决定将其发布为小型OS项目:)

#11 楼

我知道它的3年为时已晚,但也许可以帮助其他人,因为除了矩时区库(除了他在这里要求的内容不完全相同)之外,我没有找到其他类似的东西。

我在德国时区做了类似的事情,
由于夏令时和and年有366天,所以这有点复杂。

它可能需要一些工作使用“ isDaylightSavingTimeInGermany”功能,而不同时区在夏令时的不同时间发生变化。

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

主要方法是:
convertLocalDateToGermanTimezone
convertGermanDateToLocalTimezone

我已尽力对其进行记录,因此不会如此令人困惑。

评论


这应该是评论,而不是答案。

–RobG
19年11月14日在20:40

#12 楼

也许这会对您有帮助




 /**
 * Shift any Date timezone.
 * @param {Date} date - Date to update.
 * @param {string} timezone - Timezone as `-03:00`.
 */
function timezoneShifter(date, timezone) {
  let isBehindGTM = false;
  if (timezone.startsWith("-")) {
    timezone = timezone.substr(1);
    isBehindGTM = true;
  }

  const [hDiff, mDiff] = timezone.split(":").map((t) => parseInt(t));
  const diff = hDiff * 60 + mDiff * (isBehindGTM ? 1 : -1);
  const currentDiff = new Date().getTimezoneOffset();

  return new Date(date.valueOf() + (currentDiff - diff) * 60 * 1000);
}



const _now = new Date()
console.log(
  [
    "Here: " + _now.toLocaleString(),
    "Greenwich: " + timezoneShifter(_now, "00:00").toLocaleString(),
    "New York: " + timezoneShifter(_now, "-04:00").toLocaleString(),
    "Tokyo: " + timezoneShifter(_now, "+09:00").toLocaleString(),
    "Buenos Aires: " + timezoneShifter(_now, "-03:00").toLocaleString(),
  ].join('\n')
); 




评论


运行代码片段,对于东京来说,这是错误的一天。从UTC + 2运行此代码,我得到2020-08-05 23:32:15和格林威治:2020-08-05 21:32:15,但随后显示东京:2020-08-05 12:32:15据我所知,应该说-08-06。当我在格林威治午夜之后强制日期(使用Date.UTC())时,纽约正确显示了以前的日期,因此可以正常工作。 –此外,这并未考虑夏季时间的不同使用。

– Liggliluff
8月5日21:42

不幸的是,像这样的硬编码很少起作用。时间真的很复杂;使用内置方法来这样做,而不是自己发明。

–vy32
10月22日12:09

#13 楼

面临相同的问题,使用了这个

Console.log(Date.parse(“ Jun 13,2018 10:50:39 GMT + 1”));

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

评论


(此帖子似乎无法为问题提供高质量的答案。请编辑您的答案,或仅将其发布为对问题的评论)。

–sɐunıɔןɐqɐp
18年6月14日在6:28