我正在寻找一种 JavaScript 算法来将本地日历日期时间转换为自 Unix 纪元以来的 UTC 毫秒(或Date
表示相同的对象)。对某些相对日历日期执行此操作的典型且通常有用的方法YYYY-MM-DD hh:mm:ss.zzz
是:
# Close, but not quite monotonic
(new Date(YYYY, MM-1, DD, hh, mm, ss, zzz)).getTime()
JavaScript 实现有其处理 DST 的方法(阅读:夏令时、夏令时、必要时您自己的区域设置等效项),不幸的是它们不适用于手头的应用程序,而是需要单调的本地时间(但如有必要可能会失真)。
转换必须是单调的,因为对于两个相对的日历日期(即不知道时区、不知道 DST)A 和 B,转换函数 C 是这样的
If A is strictly earlier than B then C(A) ≤ C(B)
If A is simultaneous to B then C(A) = C(B)
If A is strictly later than B then C(A) ≥ C(B)
(这并不是指对时间获取函数的连续调用严格不递减的意义上的单调性——这个应用程序没有当前时间的概念,也不需要类似的东西。)
我已经开始着手自己的实现,但它可能会变得复杂,我认为也许其他人有更好的想法。
问题是:
- 这已经实施了吗?
- 如果没有,是否有比我在下面概述的更明智的方法来实现这一点?
- 不连续性发现启发式方法是否适用于全球所有已知的 DST?
JavaScriptDate
的行为
以下是 2012 年美国 DST 的极端情况。这些值是来自 Firefox 的实验结果。在这里,让函数 C 表示使用给定的本地日期和时间创建Date
对象的结果,然后使用该getTime()
方法检索 UTC 毫秒。
- 跳过时间:2012 年 3 月 11 日,夏令时开始日期,当地时间 02:00 开始的时间被跳过:01:59 之后的分钟为 03:00。在 Firefox 中,较晚的输入时间可能会导致较早的解析时间;例如 01:45 < 02:15,但 C(01:45) > C(02:15),所以音阶不是单调的。
- 双倍小时:在 2012 年 11 月 4 日,夏令时结束日期,从 01:00 开始的小时在当地时间出现两次:白天 01:59 之后的分钟是标准时间 01:00,然后是 01:59 之后的分钟标准时间是标准时间 02:00。在 Firefox 中,C(01:30) 对应于后面的重复,标准时间 01:30。
- 只要保证解析行为有利于以后的时间,这不会破坏单调性。(我没有这个保证的文档,但它可能来自 ECMA-262 中的某种语言。)
必需/首选行为
在这里,让函数 C 表示具有所需行为的转换。
- 跳过的小时:跳过的一分钟的日期应解析为接下来的第一个未跳过的分钟。例如,在 DST 开始日期,02:15、02:30 和 02:45 将解析为下一个未跳过的分钟 03:00;换句话说,C(02:15) = C(02:30) = C(02:45) = C(03:00)。
- 未跳过的时间将保持不变:将 01:45 < 02:15 < 02:45 < 03:15 与 C(01:45) < C(02:15) = C(02:45) < C(03:15) 进行比较.
- 双倍小时:一分钟多次出现的日期应仅解析为第一次出现;例如,DST 结束日期的 01:30 将被解析为 01:30 日光时间而不是标准时间,因为这是两者中较早的时间。
- 和以前一样,只是保证了更早的时间。
这些规则大致基于 Vixie cron 的规则。但是,此应用程序不处理当前时间的概念,因此不具备监视时钟以了解时间变化所需的状态。它需要一些其他方法来确定是否以及何时将跳过或加倍时间。
顺便说一句,作为一个附加要求,实现不能假定它在美国语言环境中运行;它需要在国际范围内工作,并尽可能使用检测而不是配置。
实施思路
我认为可能用于检测日期是否不连续的一件事是测试本地日期跨度的宽度±1个日历日。如果两个日期的 UTC 时间之间的差异小于或大于 48 小时,则分别意味着某些时间已被跳过或加倍。
- 如果跳过,我们可能会进一步确定是否跳过给定时间本身,如果在转换为 UTC 并返回之后,
hh:mm:ss.zzz
读取不同。如果是这样,则将时间解析为中断后的第一分钟。 - 如果加倍,我们可能会在以后的重复中确定所有时间的范围。如果给定时间落在后面的重复中,则恢复到较早的时间;否则,它会被单独留下。
这两者都可能需要不连续点的确切位置,例如,这可以通过以 ±1 日期为界的二分搜索来完成。
这种启发式方法可能因多种原因而失败;虽然我的印象是它们不太可能,但夏季时间规则在世界范围内是奇怪且不一致的:
- 如果在同一 3 个日历日内,任一方向上可能出现多个中断。(在美国,每年有两次,相隔几个月。我怀疑任何地方的调整量都超过了,比如说,四个小时。)
- 如果不连续性是互补的,那么它们可能一开始就无法检测到。
- 在任何情况下,简单的搜索都会假设在该范围内只有一个不连续点。
- 如果单个不连续性可能导致(近)3 个日历日的持续时间。(在美国,每个不连续时间占一小时。我相当肯定夏季时间调整永远不会在任何地方都以天为单位。)