timezone – 使用moment.js将一系列跨越Fall DST边界的非UTC时间戳转换为UTC时间戳的正确方法?

背景

我正在调查一个项目的moment.js的不同用例,但我对在秋季结束的夏令时问题感到困惑.在提出我的问题之前,既然我想明确并为有其他类似问题的人提供背景,那就让我解释一下我在做什么,以及春天夏令时发现了什么.

首先,我正在使用UTC时间戳和America / New_York时间戳.在美国,2017年的夏令时从3月12日凌晨2点开始(从凌晨2:00:00到凌晨3:00:00跳过),并于11月5日凌晨2点结束(从凌晨2:00:00恢复到凌晨1:00:00 ).由于我也总是知道我需要转换的目标时区(America / New_York),因此我不会依赖moment.js来检测我的本地时区,而是明确指定我想要的时区.

在夏令时实施夏令时,观察夏令时的时区,如America / New_York,向前跳了一个小时. moment.js处理这个很好.

例如,如果我将moment.js的UTC时间戳传递给美国/纽约的夏令时生效一秒,那么它看起来像这样:

moment('2017-03-12T06:59:59Z').tz('America/New_York').format('YYYY/MM/DD hh:mm:ss a z')

由于我的时间戳上的Z,上面的输入被视为UTC,我明确地设置目标时区为.tz(‘America / New_York’),因此它不使用本地系统时间.

或者使用moment-timezone我可以明确地将输入时区设置为UTC并将输出设置为America / New_York.

moment.tz('2017-03-12T06:59:59', 'UTC').tz('America/New_York').format('YYYY/MM/DD hh:mm:ss a z')

无论哪种方式,结果是2017/03/12美国东部时间上午01:59:59.

然后,我在一秒钟后运行相同的命令片刻.我将使用上面第一个示例中给出的格式,我将时间指定为UTC,然后将其转换为America / New_York时间:

moment('2017-03-12T07:00:00Z').tz('America/New_York').format('YYYY/MM/DD hh:mm:ss a z')

我的结果是正确的预期:2017/03/12 03:00:00美国东部时间 – 由于夏令时,时间提前一小时.

然后我可以使用moment-timezone通过传入America / New_York时间戳并将其转换为UTC来返回其他方式.

moment.tz('2017-03-12T01:59:59', 'America/New_York').utc().format('YYYY/MM/DD hh:mm:ss a z')

这给了我2017/03/12 06:59:59 UTC

而且,因为夏令时发挥作用,在America / New_York时区的下一刻是03:00:00,所以我将其转换为UTC …

    moment.tz('2017-03-12T03:00:00', 'America/New_York').utc().format('YYYY/MM/DD hh:mm:ss a z')

…并获得2017/03/12 07:00:00 UTC看起来正确.
在America / New_York时间跳过了一小时(“丢失”),但是时刻可以检测到并将其转换为UTC.

总而言之,对于美国的春季夏令时变化,我可以将UTC时间戳传递到moment.js或moment-timezone,并在另一个时区中获取时间戳,同时应用正确的夏令时偏移.然后,我还可以将America / New_York时间戳传递给片刻,并获取正确转换的UTC时间戳.

我的问题

太棒了,所以我希望在夏季白天节省结束时做同样的事情当然不是那么简单.我的假设是,由于夏令时有效地导致一小时“重复”,因此暂时无法知道UTC的正确时间.换句话说,当夏令时开始时有一小时的间隙,现在我们有一小时的重叠.

问题(第1部分):有没有办法将相对时间戳和时区传递给片刻并恢复正确的UTC时间?当我在下面的例子中尝试这个时,UTC时刻会跳过一个小时.

moment.tz('2017-11-05T01:00:00', 'America/Denver').tz('UTC').format('YYYY/MM/DD hh:mm:ss a z') => "2017/11/05 07:00:00 am UTC"
moment.tz('2017-11-05T01:59:59', 'America/Denver').tz('UTC').format('YYYY/MM/DD hh:mm:ss a z') => "2017/11/05 07:59:59 am UTC"
moment.tz('2017-11-05T02:00:00', 'America/Denver').tz('UTC').format('YYYY/MM/DD hh:mm:ss a z') => "2017/11/05 09:00:00 am UTC"
moment.tz('2017-11-05T02:59:59', 'America/Denver').tz('UTC').format('YYYY/MM/DD hh:mm:ss a z') => "2017/11/05 09:59:59 am UTC"

我假设它是因为时间按时间顺序发生,如下例所示.因此我没有给出UTC偏移上下文,因此我假设当前不能区分以星号开头的America / New_York时间戳:

*2017/11/05 01:00:00 am America/New_York => 2017/11/05 07:00:00 am UTC
*2017/11/05 01:59:59 am America/New_York => 2017/11/05 07:59:59 am UTC
*2017/11/05 01:00:00 am America/New_York => ???
*2017/11/05 01:59:59 am America/New_York => ???
2017/11/05 02:00:00 am America/New_York => 2017/11/05 09:00:00 am UTC
2017/11/05 02:59:59 am America/New_York => 2017/11/05 09:59:59 am UTC

同样,我想知道的是,如果有办法解决这个问题?目前,我所拥有的数据中的时间戳不包含UTC偏移量.

问题(第2部分):如果我在America / New_York时区提交数据,那么认为我基本上有两小时的数据点全部塞进(看似)从01:00开始的单个1小时是正确的: 2017年11月5日00:01:59:59?

相关话题

关于SO的一些其他主题与此相关,但我找不到任何主题或回答同样的问题.我将在此处链接一些以供参考:

> Moment.js Convert Local time to UTC time does work
> Initialize a Moment with the timezone offset that I created it with

最佳答案
看起来你已经考虑过问题并做了一些基础研究.谢谢!

您所描述的是covered in the moment-timezone docs here.如果数据在输入中不能作为UTC偏移量,则无法区分出第一次或第二次出现模糊的本地时间.瞬间选择第一次,因为时间向前移动,因此这通常是大多数情况下最明智的选择.

问题是模糊不清.即使只是一个人,如果我说“2017年11月5日凌晨1点在纽约”,你不知道我在描述的两个时间点中的哪一个.

也就是说,有时您拥有可以提供帮助的外部知识.例如,如果您有一组有序的时间戳,其中包含向后跳过的时间,那么您就知道遇到了后退转换.假设我在当地时间间隔15分钟记录数据:

00:45
01:00
01:15
01:30
01:45
01:00  <---  this one comes next sequentially, but appears backwards, so infer transition
01:15
01:30
01:45
02:00

您必须编写自己的检测逻辑,以便将该场景的下一个值与下一个值进行比较.另请注意,如果您没有任何时间似乎不按顺序,那么您无法确定正在描述的事件.在某些情况下,“心跳”信号可以帮助解决这个问题.

现在,如何在不事先知道偏移的情况下选择Moment中的第二次出现?像这样:

首先,获取hasAmbiguousWallTime函数from here.

然后定义另一个函数:

function adjustToLaterWhenAmbiguous(m) {
    if (hasAmbiguousWallTime(m)) {
        m.utcOffset(moment(m).add(1, 'hour').utcOffset(), true);
    }
}

现在你可以这样做:

// start with the first occurrence
var m = moment.tz("2017-11-05T01:00:00", "America/New_York");
m.format(); // "2017-11-05T01:00:00-04:00"

// now shift it to the second occurrence
if (... your logic, such as wall time going backwards in sequence, etc. ...) {
    adjustToLaterWhenAmbiguous(m);
    m.format(); // "2017-11-05T01:00:00-05:00"
}

这两个函数可能应该加强并添加到时刻时区,但它们应该足以满足您描述的场景.

其他几个小问题:

>而不是moment.tz(s,’UTC’),考虑使用moment.utc(s)
>而不是moment.tz(s,’America / New_York’).tz(‘UTC’),考虑一下
使用moment.tz(s,’America / New_York’).utc()
>您可能需要查看DST tag wiki以查看问题空间.
>在你的问题的第二部分,是的 – 你最终将两小时的数据填入可能被视为一小时空间的数据中.人们一直有图表和图表的问题.他们在当地时间绘制一个具有恒定值的东西,然后在春天看到归零效果,在秋天看到倍增效应.即使你告诉时间使用后来的事件,除非你实际以UTC而不是本地时间显示图形,否则你不会避免这种情况.

转载注明原文:timezone – 使用moment.js将一系列跨越Fall DST边界的非UTC时间戳转换为UTC时间戳的正确方法? - 代码日志