4

我正在寻找一种比 O(n) 更好的算法来确定未来的某个日期是否会应用夏令时(以及多少)。给定年、月、日、小时、分钟和时区(以及奥尔森时区数据库的副本),如何有效地确定该日期是否在 DST 中?我正在寻找算法,而不是要调用的库函数。

谢谢你。

进一步说明:当您创建具有未来日期和时区的对象时,我使用的日期库非常慢。事实证明,它进行了线性计算来计算日期是否处于夏令时。不仅如此,它还在对象创建时执行此操作。显然它可以等到被问到,但它也应该更有效率。

当然,DST 规则会发生变化,日期库无法预测未来,但另一种选择是对本地化日期设置任意上限。

4

4 回答 4

1

每个人都已经对不断变化的 DST 的问题发表了评论。但我可以接受我们只是假装目前已知的规则将永远适用的前提。

要获取您的 DST 信息,首先要做的是计算您未来日期的年/月/日(如果尚未采用该格式)。然后您查看您的时区并提取与 UTC、DST 开/关规则和偏移量的变化。取决于哪一年,可能会有几种不同的规则,您要确保为您的“目标”年份选择正确的规则。由于下面解释的原因,了解上一年的规则可能会很方便。

开/关规则会有一个有趣的规范,比如“Oct lastSun”:这意味着切换发生在 10 月的最后一个星期日的晚上。

您需要做的是收集所有这些格式简洁的“规则”,并为每个规则开发一些代码以确定该规则指示的最后日期。目前是 12 月,因此考虑到我所在时区的“Mar lastSun”和“Oct lastSun”等一些规则,这些日期将是 2009 年 3 月 29 日和 2009 年 10 月 25 日。这些日期中的哪一个更近?十月。十月与“关闭”相关联,因此我们目前必须没有 DST。

无论目标日期是在这些日期之前还是之后,您都可以计算当前(即目标)年份的 DST 开/关日期;如果开/关日期在您的目标日期的未来,那么只需对上一年再次进行规则计算。请注意,规则可能在间隔期间发生了变化,因此请务必为您正在查看的年份应用正确的规则。

此计算的最坏情况是,您必须重复上一年的两个规则计算。但是没有其他搜索,所以严格来说是 O(1)。

我在这里找到了一个 Local/DST/Tz 计算器:http: //home-4.tiscali.nl/~t876506/WhatDay.html,因为它是一个 JavaScript 小程序,您应该能够简单地编写代码。但是,它不能处理所有规则,因此您需要为其余规则添加一些代码。


更新:我刚刚注意到你的时间也有一小时和一分钟。这使事情稍微复杂了一点。如果您的日期不在“切换”日期,那么我上面给出的说明会很好。否则,您需要考虑时间。我想最干净的做法是将时间包括在您确定“最近”的时间中。即如果您的目标时间是 00:30 UTC 并且给定区域的切换时间是 01:00,那么目标年份的切换时间仍然是将来的,您必须使用上一年的切换时间。出于实际目的,这将意味着“其他”切换时间是最近的,并且它的开/关状态适用。

于 2009-12-18T11:24:02.983 回答
0

据我所知,已知的 DST 更改每年在固定的一天开始和结束(4 月的第一个周末,10 月的最后一个周末,诸如此类)。因此,您可以使用世界末日算法来查找给定年份的星期几并从中计算转换日期。然后您可以确定 DST 在源和/或目标区域设置中是否有效。转换本身只是增加和/或减少一个小时以补偿 DST,然后考虑时区差异的问题。

于 2009-12-16T09:50:16.110 回答
0

您的第一个问题是地方当局制定的夏令时规则。后者可以随时通过几乎任何法律,因此以您无法预测的方式更改规则。

于 2009-12-16T07:42:08.450 回答
0

嗯,正如我所见,问题在于确定某一天的工作日,在很远的将来。

为此,我建议这样的事情:

  • 每 400 年,整个系统转一圈,所以先将年数除以 400,取整数部分。400年有99个闰年,301个闰年。如果任意一天是星期一,那么 400 年后的同一天将是 301+2x99 = 499 (mod 7) ---> Monday+2 ---> Wednesday。所以你必须这样说:

wday = (ref_day + 2 * (int)((target_year - ref_year) / 400)) mod 7

  • 那么你可以做进一步的优化,但我想你可以逐年进行,这样就可以了。最多你做 399 个简单的操作, if (leap_year) then ++ else +=2, mod 7。

在获得当年 1 月 1 日的工作日之后,您可以计算 DST 转换日期,正如 Carl Smotricz 所写的那样。

于 2009-12-18T13:51:19.380 回答