As:
The Unix time number is zero at the Unix epoch, and increases by exactly 86400
per day since the epoch. So it cannot represent leap seconds. The OS will slow
down the clock to accomodate for this.
那么,如果我在数据库中存储Unix epoch(例如ts((毫秒精度(,如何处理以下情况?
- 如何确保ts总是在增加而不是向后
- 如何从考虑闰秒的db中准确选择100秒的间隔
例如
SELECT * FROM events WHERE ts >= T1 and ts < T1 + 100
上述SQL将返回发生在T1、T1+1、T1+2、。。直到T1+99,但由于闰秒的原因,将闰秒包括在内的结果可能是错误的,如何考虑这一点?
我首先要说的是,我在现实生活中从未遇到过这样的问题,所以我只是猜测,但这将是一个有根据的猜测。根据http://en.wikipedia.org/wiki/Unix_time#Encoding_time_as_a_number当插入闰秒时,问题是两次(例如1998-12-31T23:59:60.00和1999-01-01T00:00:00.00(具有相同的Unix时间(915.148.800.000(。当删除闰秒时应该没有任何问题。
根据维基百科同一页面上的注2,闰秒是不可预测的,这给你留下了两个选择:一个通用解决方案,它(假设你有由这些时间戳索引的表(总是可以插入条目,并且当一个条目出现在最后插入的条目之前(可能在闰秒内(,你可以开始一个"涂抹"过程,基本上是在条目上添加一些毫秒,以确保它超出闰秒的范围。当插入的条目再次具有比先前插入的条目更大的值时,该过程可以停止。我称之为"抹黑",因为它在某种程度上受到了谷歌"Leap smear"技术的启发(尽管不完全相同(:http://googleblog.blogspot.in/2011/09/time-technology-and-leaping-seconds.html不过,在我看来,这可能会给数据库带来一些压力,而插入查询几乎是我见过的最复杂的查询之一(如果仅在SQL中是可能的话(。
另一种解决方案可以是(我假设您使用的是Java(手动检查时间戳是否在闰秒之内。如果是,只需阻止对数据库的任何访问,并将条目插入队列中。当闰秒结束时,只需以FIFO的方式将队列插入数据库,以保证您所关心的顺序(类似于上面的解决方案,但完全是Java的,所以在它接触DB层之前(。您可以通过消除队列并直接插入数据库来优化这一点——只需像上面那样在一秒钟内"涂抹"条目即可。
当然,也有一个缺点,那就是你在闰秒中牺牲了一点准确性(考虑到闰秒如此罕见,这不是一个很大的牺牲(,但好处是它很简单,你的顺序也有保证。
如果你或其他人找到了更好的解决方案,请在这里分享,这个话题很有趣:(
更新:我已经为第三个解决方案编写了伪代码(完全在SQL查询中(,它依赖于闰秒的硬编码检查(比通用解决方案更快(。它可能会被优化很多,但只是为了证明我的观点:
if (newTime is in a leap second){
read smearCount from db;
if (smearCount <= 0) {
smearCount = 1000; // making sure we land outside the leap second
update smearCount in db;
}
newTime += smearCount;
insert newTime into db;
} else { // gradually reducing smearCount by 1 millisecond over the following 1000 insertions
read smearCount from db;
if (smearCount > 0){
smearCount -= 1;
update smearCount in db;
newTime += smearCount;
}
insert newTime into db;
}
来自Joda Time常见问题解答:
是否支持闰秒
Joda Time不支持闰秒。闰秒可以通过编写新的,专门的年表,或通过对现有
ZonedChronology
进行一些增强班无论哪种情况,Joda Time的未来版本默认情况下都不会启用闰秒。大多数应用程序都不需要它,而且它可能会带来额外的性能成本。
从IANA/Olson TZDB文件闰秒:
尽管定义中也包括掉秒的可能性("负"闰秒(,这从来没有做过,也不太可能是必要的在可预见的未来。
你的第一个问题:
如何确保ts总是在增加而不是向后?
一个负闰秒会让你处于相同的时间戳(一个值代表两个经过的秒(,所以如果没有两个负闰秒,你就无法真正倒退。由于看起来不太可能出现负闰秒,我想说这是一个你永远不会真正遇到的问题。
更新:我能想象时间戳倒退的唯一方法是,如果你使用毫秒精度并遇到下面的行为#3。
您的第二个问题:
如何从考虑闰秒的db中准确选择100秒的间隔?
由于您正在以UTC记录时间,因此您的值已经包含闰秒。我知道这听起来可能与直觉相悖,因为正如你所描述的,按照这个比例,一天中正好有86400秒(86400000毫秒(。但他们确实在那里。如果他们没有,那么我们将与TAI同步,而不是UTC。那怎么可能呢?好吧,当闰秒发生时,会发生一些不同的事情:
-
如果操作系统和应用程序代码都支持闰秒,那么它确实可以将显示秒显示为
:60
或:61
。但这几乎没有真正的实现,因为编程语言通常只允许几秒钟进入:59
。 -
操作系统可能会"冻结"一秒钟,在整整一秒钟内给出相同的值。
-
操作系统可能前进到
:59.999
,然后跳回:59.000
以重复闰秒所覆盖的周期。(感谢@Teo( -
操作系统可能会"漂移"或"拖尾"一段时间,每次慢慢地在系统时钟上增加几毫秒,直到它完全赶上额外的一秒。
-
操作系统可能会跳过它,什么也不做。您的时钟将不同步,直到下次通过NTP同步为止。如果它恰好在闰秒的那一刻同步,它可能只会将时间设置为
:59
或:00
,然后再次失去同步一段时间。
让我们考虑一个真实的例子。值1341100800000
表示2012年7月1日UTC的午夜。(您可以在本网站上查看,也可以在Java或Joda Time代码中查看以进行验证。(如果除以86400000,我们将得到自1970年1月1日UTC以来的15522天。
该值包括35闰秒的,包括2012年6月30日当天结束前一秒发生的闰秒。就好像闰秒从来没有发生过一样。
所以大多数时候,你不需要担心闰秒。假装它们不存在。让你的操作系统以任何方式处理它们。
如果你需要超精确的时间测量,也许是在科学的背景下,那么你无论如何都不应该使用计算机的系统时钟。除了闰秒可以保持、拉伸或忽略这一事实之外,它的设计并不像计时器那样精确。相反,您可能应该使用一些非常专业的计时硬件,例如该供应商提供的硬件。
更新:如果您正在快速记录事件(每秒许多事件(,并且您的操作系统具有上面#3中描述的行为,则可能需要处理闰秒。在这种情况下,我的建议是不按时间戳排序,而是考虑保留一个单独的单调递增的序列号,并按时间戳进行排序。
例如,您的数据库中可能已经有一个自动递增的整数ID。您仍然可以在where子句中按时间戳筛选,以获取特定日期的数据,但随后您将按ID排序,这样即使时间戳不是,事件也会按顺序排列。
有关其他建议,请参阅Teo的回答。