459

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

4

17 回答 17

633

背景

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 语言本身中处理日期和时间。这将包括对时区感知对象的支持。

于 2013-03-02T05:17:45.367 回答
165

正如马特约翰逊所说

如果您可以限制对现代 Web 浏览器的使用,您现在可以在没有任何特殊库的情况下执行以下操作:

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

这不是一个全面的解决方案,但它适用于许多只需要输出转换的场景(从 UTC 或本地时间到特定时区,而不是另一个方向)。

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

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

于 2018-12-06T13:05:07.763 回答
48

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

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)
于 2019-09-08T13:06:30.280 回答
44

您可以在 上指定时区偏移量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。)

于 2016-02-04T09:33:00.020 回答
9

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

于 2018-12-06T03:49:27.103 回答
4

对于 Ionic 用户,我对此感到很痛苦,因为.toISOString()必须与 html 模板一起使用。

这将获取当前日期,但当然可以添加到选定日期的先前答案中。

我用这个修复了它:

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

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

于 2019-10-22T17:38:50.763 回答
3

我在单元测试中遇到了类似的问题(特别是当单元测试在本地运行以创建快照,然后 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;
  });

});
于 2017-10-06T15:56:16.227 回答
2

基于上面的答案,我正在使用这个原生的 one 班轮将长时区字符串转换为三个字母的字符串:

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

这将根据提供的日期提供 PDT 或 PST。在我的特定用例中,在 Salesforce(Aura/Lightning)上开发,我们能够从后端获取长格式的用户时区。

于 2019-06-30T13:48:41.503 回答
2

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

它具有更改时区的简单功能(大多数时区在 400 左右)以及您希望它显示的所有自定义格式。

于 2018-11-01T06:13:47.553 回答
1

我有同样的问题,但我们可以使用我们想要使用的时
.toLocaleDateString()

例如:

var day=new Date();
const options= {day:'numeric', month:'long', year:"numeric", timeZone:"Asia/Kolkata"};
const today=day.toLocaleDateString("en-IN", options);
console.log(today);

于 2021-01-03T23:30:55.330 回答
1

尝试:date-from-timezone,它在 natively available 的帮助下解析预期日期Intl.DateTimeFormat

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

于 2017-06-02T07:32:34.100 回答
1

我在运行 GCP 云功能时遇到了这个问题。当然它可以在本地机器上运行,但是在云中运行会使操作系统默认(本地)变得 new Date()无关紧要。就我而言,来自云端的 api 调用需要东部标准时间,采用 ISO 格式(不带“Z”),偏移量为“-0500”或“-0400”,具体取决于 DST,例如:

2021-12-01T00:00:00.000-0500

同样,这不是浏览器格式问题,因此我被迫采用这种格式以使 api 调用正常工作。

使用@chickens 代码作为开始,这是有效的:

var date = new Date(); 
var now_utc =  Date.UTC(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate(),
date.getUTCHours(), date.getUTCMinutes(), date.getUTCSeconds());

var dt = new Date(now_utc);

let utcDate = new Date(dt.toLocaleString('en-US', { timeZone: "UTC" }));
let tzDate = new Date(dt.toLocaleString('en-US', { timeZone: "America/New_York" }));
let offset1 = utcDate.getTime() - tzDate.getTime();
let offset2 = offset1/60000;
let o1 = Math.abs(offset2);
console.log(offset2)
var offsetValue1 =  (offset2 < 0 ? "+" : "-") + ("00" + Math.floor(o1 / 60)).slice(-2) + ("00" + (o1 % 60)).slice(-2);
console.log(offsetValue1)
dt.setTime(dt.getTime() - offset1);

console.log(dt.toISOString());
console.log(dt.toISOString().slice(0,-1)+offsetValue1);
于 2021-12-03T15:35:16.197 回答
0

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

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

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

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

主要方法有: convertLocalDateToGermanTimezone convertGermanDateToLocalTimezone

我已经努力记录它,所以它不会那么混乱。

于 2016-04-14T13:18:49.560 回答
0

试试这样的,

public static getTimezoneOffset(timeZone: string, date = new Date()): number {
    const localDate = date.toLocaleString('fr', { timeZone, timeZoneName: 'long' });
    const tz = localDate.split(' ');
    const TZ = localDate.replace(tz[0], '').replace(tz[1], '').replace(' ', '');
    const dateString = date.toString();
    const offset = (Date.parse(`${dateString} UTC`) - Date.parse(`${dateString}${TZ}`)) / (3600 * 1000);
    return offset;
}
于 2022-02-02T11:32:48.950 回答
0

感谢@commonpike 的回答,我编写了一个函数,它接受一个 ISO 字符串日期(例如2020-10-10T08:00:00.000输入)并发送一个包含 2 个主要属性的对象。

第一个是fromUtc与作为参数输入的时区相对应的日期。

第二个是toUtc允许您格式化源自fromUtc.

const timeZoneTransformer = (stringDate, timeZone = "Europe/Paris") => {
  const now = new Date();
  const serverDate = new Date(stringDate);
  const utcDate = new Date(
    Date.UTC(
      serverDate.getFullYear(),
      serverDate.getMonth(),
      serverDate.getDate(),
      serverDate.getHours(),
      serverDate.getMinutes(),
      serverDate.getSeconds()
    )
  );
  const invdate = new Date(
    serverDate.toLocaleString("en-US", {
      timeZone,
    })
  );
  const diff = now.getTime() - invdate.getTime();
  const adjustedDate = new Date(now.getTime() - diff);
  return {
    toUtc: utcDate,
    fromUtc: adjustedDate,
  };
};
const fromUtc = timeZoneTransformer("2020-10-10T08:00:00.000").fromUtc;
console.log(fromUtc);
const toUtc = timeZoneTransformer(fromUtc).toUtc;
console.log(toUtc);

于 2021-09-11T17:53:10.633 回答
-1
//For Mumbai time difference is 5.5 hrs so
city_time_diff=5.5; //change according to your city

let time_now = Date.now();
time_now = time_now + (3600000 * city_time_diff); //Add our city time (in msec);
let new_date = new Date(time_now);
console.log("My city time is: ", new_date);
于 2021-01-31T19:05:53.740 回答
-4

遇到同样的问题,用过这个

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

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

于 2018-06-14T06:04:47.373 回答