首页 > 解决方案 > JavaScript:如何在指定时区获取日期时间

问题描述

有没有办法在 JavaScript 中获取指定时区的本地时间?基本上,我正在寻找一种方式来说明,纽约下午 2 点的 ISO 时间字符串是什么?

我有一个技巧,其中date是可解析的日期字符串,并且tz是时区标识符,例如America/New_York.

function getDateInTZ(date, tz) {
  const formatter = new Intl.DateTimeFormat([], {
    year: "numeric",
    month: "numeric",
    day: "numeric",
    hour: "numeric",
    minute: "numeric",
    second: "numeric",
    fractionalSecondDigits: 3,
    timeZone: tz,
  });
  const localDate = new Date(date);
  const localDateAtTZ = new Date(formatter.format(localDate));
  const tzOffset = localDate.getTime() - localDateAtTZ.getTime();
  return new Date(localDate.getTime() + tzOffset);
}

它具有以下行为

getDateInTz("2021-07-01 20:05", "America/Chicago").toISOString(); // 2021-07-02T01:05:00.000Z
getDateInTz(new Date("2021-12-05 20:05"), "America/Chicago").toISOString(); // 2021-12-06T02:05:00.000Z

getDateInTz("2021-12-06T02:05:00.000Z", "America/New_York").toISOString(); // 2021-12-06T02:05:00.000Z if local time is NY
getDateInTz("2021-12-06T02:05:00.000Z", "America/New_York").toISOString(); // 2021-12-06T07:05:00.000Z if local time is UTC

虽然上述解决方案适用于 Chrome,但它不适用于 Firefox,因为 FF 无法Date.parseformatter.format(). 这让我认为这不是一个正确的解决方案。

有没有人遇到过这个要求,并且有一个好的解决方案?

标签: javascriptdatefirefoxtimezone

解决方案


我们在这里受到 JavaScript 原生 Date 对象的限制。

Date 对象的内部状态是自 1970 年 1 月 1 日 00:00 UTC 以来经过的毫秒数,并且没有时区存储在 Date 对象中。

因此,我们可以创建与在另一个时区创建的日期有点等价的日期,但它们的行为不会完全相同:

  • Date.toString() 的时区偏移量将显示本地客户端时区(例如 GMT+0000),而不是所需时区的偏移量。

  • DST 规则可能无法按预期工作。我们可以得到相同的日期,比如 America/New_York,但 DST 转换将遵守当地时区规则,而不是纽约规则。

话虽如此,您使用的方法的一种变体将在所需的时区给出我所说的等效日期。

我们如何做到这一点:

  1. 首先使用Date.toLocaleString()在所需时区格式化 ISO 时间戳。我们使用 hack 来做到这一点,将“sv”语言环境传递给函数。这将创建一个 ISO 时间戳,例如 yyyy-MM-dd HH:mm:ss。

  2. 将此时间戳传递给 Date() 构造函数。

TLDR:真的做不到。但是对于某些上下文和用例,我们可以近似期望的行为。为此,我建议使用像luxon这样的库。

下面的例子:

function getDateInTimezone(date, timeZone) {
    // Using a locale of 'sv' formats as an ISO date, e.g. yyyy-MM-dd HH:mm.
    const timeInTimeZone = date.toLocaleString('sv', { timeZone } );
    // Pass this to the Date constructor
    return new Date(timeInTimeZone);
}

const localTime = new Date();
const timeZoneList = ['Asia/Tokyo', 'Europe/Berlin','America/Los_Angeles'];

console.log(`Local Time: ${localTime.toLocaleTimeString()}`);
for(let timeZone of timeZoneList) {
    const dt = getDateInTimezone(localTime, timeZone);
    console.log(`Time (${timeZone}): ${dt.toLocaleTimeString()}`);
}
.as-console-wrapper { max-height: 100% !important; top: 0; }

luxon中的时区更容易处理,当我们在 Luxon DateTime 上调用 .toString() 时,我们会得到正确的 UTC 偏移量。

const { DateTime } = luxon;
const localTime = DateTime.now();
const timeZoneList = ['Asia/Tokyo', 'Europe/Berlin','America/Los_Angeles'];

console.log(`Local Time: ${localTime.toFormat('HH:mm:ssZZ')}`);
for(let zone of timeZoneList) {
    const dt = DateTime.fromMillis(Date.now(), { zone });
    console.log(`Time (${zone}): ${dt.toFormat(' HH:mm:ssZZ')}`);
}
.as-console-wrapper { max-height: 100% !important; top: 0; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/luxon/2.0.2/luxon.min.js" integrity="sha512-frUCURIeB0OKMPgmDEwT3rC4NH2a4gn06N3Iw6T1z0WfrQZd7gNfJFbHrNsZP38PVXOp6nUiFtBqVvmCj+ARhw==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>


推荐阅读