使用标准JS库(ECMA5),不使用momentjs或外部库,如何在给定时区字符串(例如"欧洲/罗马"或"美国/Los_Angeles")的情况下计算UTC偏移量?
UTC 偏移量可能取决于它是否为 DST,因此如果解决方案需要将本地客户端日期转换为指定的时区字符串,这将是有意义的。目标只是知道 UTC 的偏移量。
function getUtcOffset(timezone) {
// return int value.
// > 0 if +GMT
// < 0 if -GMT.
}
ECMAScript (ECMA-262) 中没有函数可以执行您请求的操作。 这仅仅是因为标准 ECMAScript 除了本地计算机和 UTC 的时区之外,对时区一无所知。
但是,在支持 ECMAScript 国际化 API (ECMA-402) 并完全支持 IANA 时区数据库标识符的浏览器中,您可以像这样组合一个函数:
function getTimezoneOffset(d, tz) {
const a = d.toLocaleString("ja", {timeZone: tz}).split(/[/s:]/);
a[1]--;
const t1 = Date.UTC.apply(null, a);
const t2 = new Date(d).setMilliseconds(0);
return (t2 - t1) / 60 / 1000;
}
这将适用于当前版本的Chrome,也许在其他一些地方。 但它肯定不能保证在任何地方都有效。 特别是,它不适用于任何版本的Internet Explorer浏览器。
用法示例(在 Chrome 中):
getTimezoneOffset(new Date(2016, 0, 1), "America/New_York") // 300
getTimezoneOffset(new Date(2016, 6, 1), "America/New_York") // 240
getTimezoneOffset(new Date(2016, 0, 1), "Europe/Paris") // -60
getTimezoneOffset(new Date(2016, 6, 1), "Europe/Paris") // -120
关于此特定函数需要注意的几点:
就像我提到的,它不会在任何地方都有效。 最终,随着所有浏览器都赶上现代标准,它会,但目前不会。
您传入的日期确实会影响结果。 这是由于夏令时和其他时区异常造成的。 您可以只使用
new Date()
传递当前日期,但结果将根据您调用函数的时间而变化。 请参阅时区标签维基中的"时区!=偏移量"。此函数的结果与
Date.getTimezoneOffset
相同 - 以分钟为单位,正值为 UTC 以西。 如果您正在使用ISO8601偏移量,则需要转换为小时并反转符号。该函数依赖于
toLocaleString
函数的时区格式化功能。 我选择了'ja'
区域性,因为数组的日期部分已经按正确的顺序排列。 这确实是一个黑客。 理想情况下,将有一个 API 允许您访问时区信息,而无需在格式化时将其绑定到区域设置。 不幸的是,这个特定 API 的设计者犯了将时区与区域设置相关联的错误。 这是在来自各种语言的其他一些 API 中犯的错误,不幸的是,它被带到了 JavaScript 中。明确重申:ECMA-402 中唯一的时区功能是在格式化字符串时应用时区,恕我直言,这是一个设计缺陷。
在上面的示例用法部分中有一个错误,它说明了为什么这个 API 是一个错误。 具体而言,创建
Date
对象时无法指定时区。 我传入的 1 月 1 日和 7 月 1 日日期是在本地时区创建的,而不是指定的时区。 因此,输出可能不完全是您在过渡附近的预期。 这可以被黑客入侵以解决此问题,但我会将其作为练习留给您。
再次 - 尽管此答案满足要求的标准,但由于不涉及外部库,我强烈建议不要在任何生产代码中使用它。 如果你打算用这个做任何重要的事情,我会使用我在这里列出的一个库。
你检查过时刻时区吗?
moment.tz("America/Los_Angeles").utcOffset();
选择的答案并不能真正回答这个问题。作者想要的是一个可以输入时区名称然后返回偏移量的函数。
经过一些调查和挖掘 icu4c 的源代码,事实证明以下代码片段可以满足您的需求:
const getUtcOffset = (timeZone) => {
const timeZoneName = Intl.DateTimeFormat("ia", {
timeZoneName: "short",
timeZone,
})
.formatToParts()
.find((i) => i.type === "timeZoneName").value;
const offset = timeZoneName.slice(3);
if (!offset) return 0;
const matchData = offset.match(/([+-])(d+)(?::(d+))?/);
if (!matchData) throw `cannot parse timezone name: ${timeZoneName}`;
const [, sign, hour, minute] = matchData;
let result = parseInt(hour) * 60;
if (sign === "-") result *= -1;
if (minute) result + parseInt(minute);
return result;
};
console.log(getUtcOffset("US/Eastern"));
console.log(getUtcOffset("Atlantic/Reykjavik"));
console.log(getUtcOffset("Asia/Tokyo"));
请注意,此处使用的区域设置ia
是国际语言。原因是根据 icu4c 的源代码,时区名称因语言环境而异。即使您使用相同的区域设置,时区名称的格式仍可能因不同的时区而异。
使用国际语(ia
),格式总是与GMT+NN:NN
相同的模式,可以很容易地解析。
这有点烦躁,但在我自己的产品中效果很好。
希望它对您有所帮助:)
你必须:
- 获取当前日期时间
- (将为您提供当前设备当前时区的日期时间)
- 获取当前时区偏移量
- 将日期时间转换为新的/必需的时区
- 获取当前日期时间和转换后的日期时间之间的差异
- 将所述差异添加到当前时区偏移量
例如,以小时为单位返回时区:
function getTimezoneOffset(tz, hereDate) {
hereDate = new Date(hereDate || Date.now());
hereDate.setMilliseconds(0); // for nice rounding
const
hereOffsetHrs = hereDate.getTimezoneOffset() / 60 * -1,
thereLocaleStr = hereDate.toLocaleString('en-US', {timeZone: tz}),
thereDate = new Date(thereLocaleStr),
diffHrs = (thereDate.getTime() - hereDate.getTime()) / 1000 / 60 / 60,
thereOffsetHrs = hereOffsetHrs + diffHrs;
console.log(tz, thereDate, 'UTC'+(thereOffsetHrs < 0 ? '' : '+')+thereOffsetHrs);
return thereOffsetHrs;
}
getTimezoneOffset('America/New_York', new Date(2016, 0, 1));
getTimezoneOffset('America/New_York', new Date(2016, 6, 1));
getTimezoneOffset('Europe/Paris', new Date(2016, 0, 1));
getTimezoneOffset('Europe/Paris', new Date(2016, 6, 1));
getTimezoneOffset('Australia/Sydney', new Date(2016, 0, 1));
getTimezoneOffset('Australia/Sydney', new Date(2016, 6, 1));
getTimezoneOffset('Australia/Sydney');
getTimezoneOffset('Australia/Adelaide');
哪些输出像
America/New_York 2015-12-30T22:00:00.000Z UTC-5
America/New_York 2016-06-30T01:00:00.000Z UTC-4
Europe/Paris 2015-12-31T04:00:00.000Z UTC+1
Europe/Paris 2016-06-30T07:00:00.000Z UTC+2
Australia/Sydney 2015-12-31T14:00:00.000Z UTC+11
Australia/Sydney 2016-06-30T15:00:00.000Z UTC+10
Australia/Sydney 2019-08-14T03:04:21.000Z UTC+10
Australia/Adelaide 2019-08-14T02:34:21.000Z UTC+9.5
对于可能遇到这个问题的其他人,JavaScript 中的标准 Date 函数上有一种方法可用于获取 UTC 的偏移量,称为 getTimezoneOffset。
用法很简单(例如):new Date().getTimezoneOffset()。这将返回当前时区从 UTC 开始的分钟数。注意:如果当前时区早于 UTC,则返回负数。例如,如果用户的时区为 UTC+1,则返回 -60。