在JavaScript中获取另一个时区的时区偏移量,而不使用字符串

9

我想计算从“用户时间”到“WET/WEST”的偏移量。

我用 new Date().getTimezoneOffset() 来获取用户的偏移量。

但是,我该如何获得WET/WEST的偏移量,以便我可以计算两者的组合偏移量?

例如,如果用户在中欧时间(CET/CEST)区域,在冬季,组合偏移量将是-60(CET)+ 0(WET)= -60。在夏季,它将是-120(CEST)+ 60(WEST)= -60。在这种情况下,总是-60,但用户的时区也可能没有DST(日光节约时间)。

能否在不将其格式化为字符串并从该字符串中读取新时区的情况下完成此操作?


由于日期对象本质上是UTC时间,因此您可以忽略本地时区,并将任何偏移量应用于UTC部分,然后读取新的UTC值。您无法可靠地从Date.prototype.toString的结果中读取时区,因为它取决于实现。时区可能会丢失、模糊或难以辨认。 - RobG
但是我从哪里获取WET的偏移量呢?我正在寻找类似于new Timezone("WET").getOffset这样的东西。 - Jens
1
为此,您需要构建自己的实用程序,可能基于广泛使用的IANA时区数据库。我不认为需要构造函数,时区是与UTC偏移量相对简单的事情,因此可以使用myDateLib.getTimezoneOffset('WET')myDateLib.getTimezoneOffset('LHDT')myDateLib.getTimezoneOffset('Australia/Lord_Howe') - RobG
4个回答

9
您不能可靠地使用时区缩写进行输入,因为它们可以被解释为许多不同的方式。例如,CST在北美可能是“中央标准时间”,而在中国则是“中国标准时间”,或者是“古巴标准时间”。虽然一些缩写诸如WETWEST是唯一的,但许多缩写是含糊的。请参阅此处的时区缩写列表
相反,您需要知道IANA时区标识符。一个使用WET/WEST的地点是葡萄牙,其IANA标识符为"Europe/Lisbon"。您可以在此处找到标识符列表。选择正确的标识符非常重要,因为时区会随着时间而改变。每个标识符反映了每个地区的特定历史。
一旦您知道了IANA时区标识符,那么您就有了使用它的选项:
  1. In some modern browsers that fully support the time zone features of the ECMAScript Internationalization API (ECMA-402), you can do the following:

    var d = new Date();
    var s = d.toLocaleString(undefined, { timeZone: "Europe/Lisbon" })
    

    This will convert the provided date and time to the correct time zone during formatting. Passing undefined in the first parameter will use the current locale for formatting.

    There are a few downsides to this approach, as it is not yet implemented in all browsers, and there is no API for just retrieving the raw time zone offset of a particular point in time.

  2. You can consider using a library that implements this functionality. I list several of them here. My personal preference is for moment.js with the moment-timezone addon, which you can do the following:

    var m = moment.tz("Europe/Lisbon");
    var s = m.format();
    

    You can pass parameters to the format method to display the output however you like. You can also convert an existing time, such as:

    var m = moment.utc("2016-01-01T00:00:00").tz("Europe/Lisbon");
    var s = m.format();
    

    You can also get the offset for a particular moment in time like so:

    var m = moment.utc("2016-01-01T00:00:00").tz("Europe/Lisbon");
    var offsetInMinutes = m.utcOffset();
    var offsetAsString = m.format("Z");
    
  3. You can write your own code for handling a particular time zone. Though this can be error prone and I don't generally recommend it. Updates can be particularly difficult if you go down this route.

请注意,特定时区的偏移量将根据所处日期和时间而变化。 因此,表示“现在”的new Date()可能并不总是正确的输入,具体取决于您的情况。

2
关于#1。好的,我可以将给定日期更改为特定时区。 但是我(和op)想要计算用户当前时区与任意时区之间的时区差异。因此,在您的示例中:ds之间的差异(以小时,秒或其他方式)是多少? - macwier

3

function getTimezoneOffset(atTime, timeZone) {
    const localizedTime = new Date(atTime.toLocaleString("en-US", {timeZone}));
    const utcTime = new Date(atTime.toLocaleString("en-US", {timeZone: "UTC"}));
    return Math.round((localizedTime.getTime() - utcTime.getTime()) / (60 * 1000));
}

// returns -360 or -420 depending on the time of year 
console.log(getTimezoneOffset(new Date(), "America/Denver"));

// returns -300 or -360 depending on the time of year 
console.log(getTimezoneOffset(new Date(), "America/Chicago"));


非常好的简单解决方案。我希望你不介意我将其转换为分钟偏移,因为某些时区会偏移30或45分钟。 - Pawel

2

我也希望不使用库,但即使使用moment.js也只有固定数量的时区数据,如果不持续更新,将在30年后失效,这让我感到有些恼火。虽然这个方法使用字符串,但并没有比解析数字字符串更复杂的内容。

function getTimezoneOffset(dt, timezone) {

     let getItem = function(format) {
         format.timeZone = timezone;
         return parseInt(dt.toLocaleString(
            'en-US', format));
     };
    
     let adjDate = new Date(
        getItem({year: 'numeric'}),
        getItem({month: 'numeric'}) - 1, // months are zero based
        getItem({day: 'numeric'}),
        getItem({hour: 'numeric',hour12: false}),
        getItem({minute: 'numeric'}));
     let noSecs = new Date(dt.getTime());
     noSecs.setSeconds(0, 0);
     let diff = Math.round((adjDate.getTime() - noSecs.getTime()) / 60000);
    
     return dt.getTimezoneOffset() - diff;
    
}

0
我想不使用库来完成这个任务,因此我编写了这个函数,可以返回给定时区与UTC之间的时区偏移量:
function getTimezoneOffsetFrom(otherTimezone) {
    if (otherTimezone === void 0) { otherTimezone = "Europe/Amsterdam"; }
    var date = new Date();
    function objFromStr(str) {
        var array = str.replace(":", " ").split(" ");
        return {
            day: parseInt(array[0]),
            hour: parseInt(array[1]),
            minute: parseInt(array[2])
        };
    }
    var str = date.toLocaleString(['nl-NL'], { timeZone: otherTimezone, day: 'numeric', hour: 'numeric', minute: 'numeric', hour12: false });
    var other = objFromStr(str);
    str = date.toLocaleString(['nl-NL'], { day: 'numeric', hour: 'numeric', minute: 'numeric', hour12: false });
    var myLocale = objFromStr(str);
    var amsterdamOffset = (other.day * 24 * 60) + (other.hour * 60) + (other.minute);
    var myLocaleOffset = (myLocale.day * 24 * 60) + (myLocale.hour * 60) + (myLocale.minute);
    return myLocaleOffset - amsterdamOffset + date.getTimezoneOffset();
}

也许可以使用 en-US 作为区域设置字符串进行批准,然后 str.replace 可能需要过滤逗号或其他字符。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接