在JavaScript中计算给定时区字符串的UTC偏移量

在JavaScript中计算给定时区字符串的UTC偏移量,javascript,datetime,timezone,locale,offset,Javascript,Datetime,Timezone,Locale,Offset,使用标准JS库(ECMA5),在不使用momentjs或外部LIB的情况下,如何计算给定时区字符串(如“欧洲/罗马”或“美国/洛杉矶”)的UTC偏移量 UTC偏移量可能取决于它是否为DST,因此,如果解决方案需要将本地客户端日期转换为指定的时区字符串,那么它是有意义的。目标只是知道UTC的偏移量 function getUtcOffset(timezone) { // return int value. // > 0 if +GMT // < 0 if -GMT. }

使用标准JS库(ECMA5),在不使用momentjs或外部LIB的情况下,如何计算给定时区字符串(如“欧洲/罗马”或“美国/洛杉矶”)的UTC偏移量

UTC偏移量可能取决于它是否为DST,因此,如果解决方案需要将本地客户端日期转换为指定的时区字符串,那么它是有意义的。目标只是知道UTC的偏移量

function getUtcOffset(timezone) {
  // return int value. 
  // > 0 if +GMT 
  // < 0 if -GMT.
}
函数getUtcOffset(时区){ //返回int值。 //如果+GMT,则大于0 //<0如果-GMT。 }
你检查过时刻时区了吗

moment.tz("America/Los_Angeles").utcOffset();

ECMAScript()中没有可以执行您请求的操作的函数。这仅仅是因为标准ECMAScript除了本地计算机和UTC之外,对时区一无所知

但是,在支持ECMAScript国际化API()并完全支持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中):

关于此特定函数,需要注意以下几点:

  • 就像我提到的,它不会在任何地方都起作用。最终,随着所有浏览器赶上现代标准,它会的,但目前不会

  • 你输入的日期确实会影响结果。这是由于夏令时和其他时区异常。只需使用
    new date()
    ,即可传递当前日期,但结果将根据调用函数的时间而变化。请参见中的“时区!=偏移”

  • 此函数的结果与
    Date.getTimezoneOffset
    相同,以分钟为单位,正值位于UTC以西。如果使用ISO8601偏移,则需要转换为小时并反转符号

  • 该函数依赖于
    toLocaleString
    函数的时区格式化功能。我选择了
    'ja'
    区域性,因为日期部分已经按照数组的正确顺序排列。这确实是一种黑客行为。理想情况下,应该有一个API,允许您在格式化时访问时区信息,而无需将其绑定到区域设置。不幸的是,这个特定API的设计者犯了将时区与区域设置相关联的错误。这是来自不同语言的一些其他API中所犯的错误,不幸的是,它被带入了JavaScript中

    简单重申:ECMA-402中唯一的时区功能是在格式化字符串时应用时区,这是一个设计缺陷,IMHO。

  • 在我上面的示例使用部分中有一个bug,它举例说明了为什么这个API是一个错误。具体来说,在创建
    Date
    对象时,无法指定时区。我传入的1月1日和7月1日日期是在本地时区中创建的,而不是在指定的时区中创建的。因此,在转换附近,输出可能与您期望的不完全相同。为了解决这个问题,可以对它进行更多的黑客攻击,但我将把它作为练习留给你们


同样-尽管这个答案满足要求的标准,因为没有涉及外部库,但我强烈建议不要在任何生产代码中使用它。如果您打算对此做一些重要的事情,我会使用。

对于其他可能涉及此问题的人,JavaScript中的标准日期函数中提供了一种方法,用于获取UTC的偏移量,称为getTimezoneOffset

用法很简单(例如):new Date().getTimezoneOffset()。返回当前时区距UTC的分钟数。注意:如果当前时区早于UTC,则返回负数。例如,如果用户的时区为UTC+1,则返回-60。

您必须:

  • 获取当前日期时间(将提供当前设备当前时区中的日期时间)
  • 获取当前时区偏移量
  • 将日期时间转换为新的/所需的时区
  • 获取当前日期时间和转换的日期时间之间的差异
  • 将所述差异添加到当前时区偏移
例如返回时区(小时):

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

选择的答案并不能真正回答问题。作者想要的是一个可以输入时区名称然后返回偏移量的函数

经过一些调查和分析,以下代码片段可以满足您的需要:

const getUtcOffset=(时区)=>{
常量timeZoneName=Intl.DateTimeFormat(“ia”{
时区名称:“短”,
时区,
})
.formatToParts()
.find((i)=>i.type==“timeZoneName”).value;
常量偏移=时区名称切片(3);
如果(!offset)返回0;
常量matchData=offset.match(/([+-])(\d+(:(\d+)))/;
如果(!matchData)throw`无法分析时区名称:${timeZoneName}`;
常数[,符号,小时,分钟]=匹配数据;
设结果=parseInt(小时)*60;
如果(符号==“-”)结果*=-1;
if(分钟)结果+parseInt(分钟);
返回结果;
};
控制台日志(getUtcOffset(“美国/东部”);
console.log(getUtcOffset(“大西洋/雷克雅未克”);

console.log(getUtcOffset(“亚洲/东京”).tz
数据在插件中单独提供。谢谢@MattJohnson。但是,我不希望添加时刻时区。我不完全拥有这个软件,所以更改libs不是一件容易的事。谢谢,但是有
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