将UTC转换为新西兰日期(考虑夏令时)的最简单方法是什么?

6
我有以下函数,用于将msSinceEpoch转换为新西兰日期(兼容IE11)...

const MAGICNUMBER = 13;
const toNewZealand = (msSinceEpoch) => {
    const [day, month, year, time] = new Date(msSinceEpoch).toLocaleString("en-NZ", {
      hour12: false, timeZone: "UTC"
    }).split(/[/,]/); // timeZone UTC is the only format support on IE11
    const [hours, minutes, seconds] = time.trim().split(":");
    return new Date(~~year, ~~month - 1, ~~day, ~~hours + MAGICNUMBER, ~~minutes, ~~seconds)
  };

// usage....
console.log(
  toNewZealand(new Date().getTime())
)

然而,这个数字并不与新西兰的夏令时(+12或+13)有关。因此,如何获得相对于新西兰夏令时的正确数字变得复杂起来(+12或+13)。我的初步尝试只是计算它是否在9月的最后一个星期日或4月的第一个星期日之间,但我意识到,只要在代码中的任何地方使用new Date()构造函数,它就会创建一个相对于他们系统时间的日期并打破数学计算。简而言之,将自纪元以来的UTC毫秒转换为适用于新西兰夏令时设置的新西兰时间。编辑:由于捆绑大小成本,也不想使用Moment或任何其他库来解决此问题。

1
通常使用支持时区的库(例如Luxon)来完成这个任务,除非您想保留自己的历史日期数据库以供新西兰夏令时转换使用。日期对象是UTC,因此只要将所有东西解析回UTC,"本地"日期就不会出现问题。 - RobG
请查看 https://momentjs.com/。另外,休息一下——现在是午餐时间 :-) - Dacre Denny
PS. 只是为了展示支持历史时区并不容易,即使仅限于夏令时,这里有一篇关于新西兰过去一个世纪的变化的文章:新西兰夏令时历史 - RobG
午饭休息了哈哈 :) 我回来了,不能像帖子中所述那样使用库; 我想找出库是如何做到的,然后按照那种方式去做,但他们的代码就像意大利肉酱面一样混乱。 99% 的这些库只是臃肿,我只对这个单一用例的代码感兴趣。 - Shanon Jackson
“编辑:由于捆绑大小成本,也不想使用Moment或任何其他库来解决此问题。” 很遗憾,因为某些浏览器会错误地处理某些时间。日期API无法涵盖用时间制造的每个漏洞。时间是相对的。如果可以的话,您可以通过Ajax请求到服务器(许多服务器端语言具有更强大的日期时间API)来获取魔术数字。 - KarelG
2个回答

0
TL;DR 将自纪元以来的UTC毫秒转换为新西兰时间...
我认为OP对时间转换有误解。自纪元以来的毫秒始终处于UTC中。时间转换更改时间的显示格式,不应更改msSinceEpoch
您可以使用toLocalString()并传递所需的时区(Pacific/Auckland)来转换DateTime的显示格式:

const date = new Date();
console.log(
  date.toLocaleString('en-NZ', {
    timeZone: 'Pacific/Auckland',
  }),
);


-1

问题已解决,尽可能保持易读性。基本上,根据当前的UTC日期时间,尝试添加12或13,如果通过添加12或13我们进入下一个夏令时期间,则添加备用项... 例如,如果通过添加12我们进入+13领域...则添加+13。 例如,如果通过添加+13我们进入+12领域...则添加+12。

新西兰的夏令时在9月的最后一个星期日和4月的第一个星期日更改。

这就是解决方案...

const UTCFromMS = (ms) => {
  return new Date(new Date(ms).toUTCString().replace(" GMT", ""))
};

const addHours = (dte, hrs) => {
        return new Date(
            dte.getFullYear(),
            dte.getMonth(),
            dte.getDate(),
            dte.getHours() + hrs,
            dte.getMinutes(),
            dte.getMilliseconds()
        );
};

const toNewZealand = (ms) => {
        return addNewZealandDaylightSavings(UTCFromMS(ms));
};

const getPreviousSunday = (dte) => {
    return new Date(
        dte.getFullYear(),
        dte.getMonth(),
        dte.getDate() - dte.getDay(),
        1,
        0,
        0
    );
};

const getNextSunday = (dte) => {
    return new Date(
        dte.getFullYear(),
        dte.getMonth(),
        dte.getDay() === 0 ? dte.getDate() : dte.getDate() + (7 - dte.getDay()),
        1,
        0,
        0
    )
};

const standardHours = 12;
const daylightHours = 13;
const addNewZealandDaylightSavings = (dte) => {
    const lastSundaySeptember = getPreviousSunday(
        new Date(dte.getFullYear(), 8, 30)
    );

    const firstSundayApril = getNextSunday(
            new Date(dte.getFullYear(), 3, 1)
    );

    // If its before firstSundayApril, add 13, if we went over 1am, add 12.
    if(dte <= firstSundayApril) {
        const daylightNz = addHours(dte, daylightHours);
        if(daylightNz >= firstSundayApril) {
            return addHours(dte, standardHours);
        }
        return daylightNz
    }

    // if its before lastSundaySeptember, add 12 if we went over 1am add 13.
    if(dte <= lastSundaySeptember) {
        const standardNz = addHours(dte, standardHours);
        if(standardNz >= lastSundaySeptember) {
            return addHours(dte, daylightHours);
        }
        return standardNz;
    }
    return addHours(dte, daylightHours);
};

console.log(toNewZealand(new Date().getTime()).toString());
// the above line should always output the current DateTime in New Zealand, replace the argument with any epoch milliseconds and it should still always give you the correct time.

===编辑====

由于现在有一些成为Web标准的方法,因此上面的答案已经不太相关了。

Date.prototype.toLocaleString和timeZone: "Pacific/Auckland"是今天转换日期最简单的方法之一,您可以在MDN上阅读更多信息:

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toLocaleString


你不应该需要重新发明轮子。 - Hooman Bahreini
如果没有人重新发明轮子,我们仍然会开着马车四处奔波,这个比喻在软件工程领域中更加适用。当这篇文章写作时,MomentJS是一个100KB-150KB的依赖项,但现在已经被弃用了,所以这对任何人都不是好建议。自从这篇文章写出来后,现在有一种现代的Web标准可以将时区转换为toLocaleString,并将其转换为另一个日期构造函数,我将更新我4年前写的答案,解决我自己的问题。 - Shanon Jackson
toLocalString() 自 Chrome 1 版本以来就存在了,实际上您在问题中使用了它。这个答案是错误的。时间转换不应该改变 msSinceEpoch。时间转换只应该改变 DateTime 的显示格式。 - Hooman Bahreini
是 timeZone 选项出了问题,这个链接可以帮助解决:https://caniuse.com/mdn-javascript_builtins_date_tolocalestring_iana_time_zone_names 与此相比,这个链接也可能有所帮助:https://caniuse.com/?search=tolocaleString - Shanon Jackson

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