rrule.js - 重复序列在夏令时后更改时区中的时间

fen*_*dow 7 javascript icalendar datetime rrule dst

我有一个看起来非常常见的用例:我想要一个在特定时区每天同一时间发生的重复事件(在下面的示例中,美国/丹佛时区的上午 6:00)。我希望在夏令时更改后像以前一样在一天中的同一时间重复出现这种情况。目前,它在夏令时后一小时发生变化,这似乎表明在生成循环日期时间时未考虑夏令时。

我已经尝试了 rrule 的各种配置,如此此处的文档中所示。它说夏令时期间的时间应该相同,但这不是我所看到的。

代码示例

const rrule = new RRule({
  freq: RRule.DAILY,
  dtstart: new Date(Date.UTC(2022, 7, 18, 12, 0, 0)),
  // tzid: 'America/Denver', // output is the same whether this is included or not
})
const datetimes = rrule.between(
  new Date('2022-10-31'),
  new Date('2022-11-10')
)
Run Code Online (Sandbox Code Playgroud)

尝试一下CodeSandbox。只要您位于实行夏令时的时区并且between范围包括夏令时的变化,就应该得到类似的结果。

预期产出

夏令时后,美国/丹佛时区的时间不应改变(即重复应考虑夏令时):

Mon Oct 31 2022 06:00:00 GMT-0600 (Mountain Daylight Time)
Tue Nov 01 2022 06:00:00 GMT-0600 (Mountain Daylight Time)
Wed Nov 02 2022 06:00:00 GMT-0600 (Mountain Daylight Time)
Thu Nov 03 2022 06:00:00 GMT-0600 (Mountain Daylight Time)
Fri Nov 04 2022 06:00:00 GMT-0600 (Mountain Daylight Time)
Sat Nov 05 2022 06:00:00 GMT-0600 (Mountain Daylight Time)
Sun Nov 06 2022 06:00:00 GMT-0700 (Mountain Standard Time) <-- Daylight savings change
Mon Nov 07 2022 06:00:00 GMT-0700 (Mountain Standard Time)
Tue Nov 08 2022 06:00:00 GMT-0700 (Mountain Standard Time)
Wed Nov 09 2022 06:00:00 GMT-0700 (Mountain Standard Time)
                ^^
Run Code Online (Sandbox Code Playgroud)

实际产量

美洲/丹佛 时区 每天的时间从 6:00 更改为 5:00:

Mon Oct 31 2022 06:00:00 GMT-0600 (Mountain Daylight Time)
Tue Nov 01 2022 06:00:00 GMT-0600 (Mountain Daylight Time)
Wed Nov 02 2022 06:00:00 GMT-0600 (Mountain Daylight Time)
Thu Nov 03 2022 06:00:00 GMT-0600 (Mountain Daylight Time)
Fri Nov 04 2022 06:00:00 GMT-0600 (Mountain Daylight Time)
Sat Nov 05 2022 06:00:00 GMT-0600 (Mountain Daylight Time)
Sun Nov 06 2022 05:00:00 GMT-0700 (Mountain Standard Time) <-- Daylight savings change
Mon Nov 07 2022 05:00:00 GMT-0700 (Mountain Standard Time)
Tue Nov 08 2022 05:00:00 GMT-0700 (Mountain Standard Time)
Wed Nov 09 2022 05:00:00 GMT-0700 (Mountain Standard Time)
                ^^
Run Code Online (Sandbox Code Playgroud)

我已经在 GitHub 上为此打开了一个问题,但我想知道我是否遗漏了一些东西。这似乎是一个常见的用例,所以我想我能够找到一些关于它的东西。我确实在这里这里发现了一些关于它的问题,但我已经在应用建议的解决方案。

这是 rrule 中的实际错误还是我只是错过了一些东西?

Mic*_*fer 2

我必须深入挖掘,令人惊讶的是,似乎没有错误。如果您将 CodeSandbox 更改为以下内容,那么它应该可以工作:

import { RRule } from "rrule";
import "./styles.css";

const rrule = new RRule({
  freq: RRule.DAILY,
  dtstart: new Date(Date.UTC(2022, 7 - 1, 18, 12, 0)), // See note 1
  tzid: 'America/Denver',
})
const datetimes = rrule.between(
  new Date('2022-10-31'),
  new Date('2022-11-10')
)
const output = datetimes.map((d) => new Date( // See note 2
  d.getUTCFullYear(),
  d.getUTCMonth(),
  d.getUTCDate(),
  d.getUTCHours(),
  d.getUTCMinutes(),
)).join('<br/>')
document.getElementById("app").innerHTML = output;
Run Code Online (Sandbox Code Playgroud)

笔记:

  1. https://github.com/jakubroztocil/rrule#timezone-support上的文档指出,使用new Date(2022, 7, ...)将产生意外的时区偏移。而是使用 rrule 的datetime函数。由于此功能目前无法正常工作(请参阅https://github.com/jakubroztocil/rrule/issues/551),我们必须使用new Date(Date.UTC(2022, 7 - 1, ...))(这正是datetime函数正在执行的操作)。
  2. 结果将是 UTC 日期,您必须将其解释为您在 中指定的时区中的日期tzid这看起来很奇怪,但在https://github.com/jakubroztocil/rrule#important-use-utc-dates 的文档中指出:即使给定的偏移量是Z(UTC),这些都是当地时间,而不是 UTC次。只是为了强调这一点,我的代码示例通过创建使用值的新实例将UTC日期转换为日期(仅当您的操作系统的系统时区为 时才有效)。America/DenverDateUTCAmerica/Denver

  • 最后,我明白它应该如何工作!文档中有一些零碎的内容,但他们从未真正将它们放在一起。这有点奇怪,但至少我现在可以让它工作了。谢谢你! (2认同)