数据库中的闰秒处理

时间:2013-11-03 08:27:51

标签: unix timezone jodatime unix-timestamp

如:

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. 

那么,如果我在DB中存储Unix纪元(例如ts)(毫秒精度),如何处理以下情况?

  1. 如何确保ts总是在增加而不是向后?
  2. 如何从db中精确选择100s间隔,考虑到闰秒?
  3. e.g。

    SELECT * FROM events WHERE ts >= T1 and ts < T1 + 100
    

    上述SQL将返回发生在T1,T1 + 1,T1 + 2,..上升到T1 + 99的事件,但由于闰秒,结果可能是错误的,包括1s的跳跃时间,如何考虑到这个?

2 个答案:

答案 0 :(得分:4)

我首先要说的是我在现实生活中没有遇到过这样的问题所以我只会猜测,但这将是一个有根据的猜测。根据{{​​3}}插入闰秒时问题是2次(例如1998-12-31T23:59:60.00和1999-01-01T00:00:00.00)具有相同的Unix时间(915.148.800.000) 。当删除闰秒时,应该没有任何问题。

根据同一维基百科页面上的注释#2闰秒是不可预测的,这留下了两个选择:一个通用的解决方案(假设您有这些时间戳索引的表)可以始终插入条目和一个条目的时刻在最后插入的条目之前(可能在闰秒内),您可以开始“涂抹”过程,该过程基本上为条目添加几毫秒,以确保它超出闰秒的范围。当插入的条目将再次具有比先前插入的条目更大的值时,该过程可以停止。我称之为'涂抹',因为它的某种灵感来自谷歌的“Leap Smear”技术(虽然不完全相同):http://en.wikipedia.org/wiki/Unix_time#Encoding_time_as_a_number 我看到它的方式虽然这可能会给你的数据库带来一些压力,插入查询只是我见过的最复杂的查询之一(如果它甚至可以在SQL中使用)。

另一个解决方案是(我假设你正在使用Java),你手动检查时间戳是否落在闰秒之内。如果是,则阻止对数据库的任何访问并将条目插入队列。当闰秒结束时,只需将FIFO方式的队列插入数据库,以保证您关注的顺序(类似于上面的解决方案,但完全是Java,所以在它甚至触及数据库层之前)。您可以通过消除队列并直接插入数据库来优化这一点 - 只需像上面那样“涂抹”一秒钟内的条目。

当然有一个缺点是你在闰秒中牺牲了一点精度(考虑到闰秒是如此罕见,这不是一个很大的牺牲)但是加分是它很简单,你的订单也是有保障的。

如果您或其他任何人发现更好的解决方案,请在此处分享,此主题非常有趣:)

更新:我已经编写了第三个解决方案的伪代码(完全在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;
}

答案 1 :(得分:4)

来自Joda Time FAQ

  

是否支持闰秒?

     

Joda-Time不支持闰秒。写一个新的,可以支持闰秒,   专业年表,或对现有ZonedChronology进行一些改进   类。在任何一种情况下,Joda-Time的未来版本默认情况下都不会启用闰秒。   大多数应用程序都不需要它,并且可能会产生额外的性能成本。

来自IANA / Olson TZDB file on leap seconds

  

虽然定义还包括掉秒的可能性   (“负”闰秒),这从未做过,也不太可能是必要的   在可预见的未来。

你的第一个问题:

  

如何确保ts总是在增加而不是向后?

一个负闰秒会让你处于相同的时间戳(一个值为两个经过的秒数),所以如果没有两个负闰秒,你就不能真正向后退。由于看起来不会出现负闰秒,我会说这是一个你永远不会遇到的问题。

更新:我可以设想倒退时间戳的唯一方法是使用毫秒精度并遇到下面的行为#3。

你的第二个问题:

  

如何从db中精确选择100s间隔,将其考虑到闰秒?

由于您的录制时间是UTC,因此您的值已包含闰秒。我知道这听起来可能违反直觉,因为正如您所描述的那样,按照这种规模,一天只有86400秒(86400000毫秒)。但他们确实在那里。如果他们不是 - 那么我们将与TAI同步,而不是UTC。那怎么可能呢?好吧,当闰秒发生时会发生一些不同的事情:

  1. 如果操作系统和应用程序代码都支持闰秒,那么它确实可以将显示秒数显示为:60:61。但是几乎没有真正的实现,因为编程语言通常只允许秒:59

  2. 操作系统可能会“冻结”一秒钟,给出相同的值一整秒。

  3. 操作系统可能会前进到:59.999,然后跳回:59.000以重复闰秒所涵盖的时间段。 (感谢@Teo)

  4. 操作系统可能会“漂移”或“涂抹”一段时间,一次慢慢地向系统时钟添加几毫秒,直到它完全赶上额外的秒钟。

  5. 操作系统可能会跳过它并且什么都不做。在下次通过NTP同步之前,您的时钟将不同步。如果它恰好在闰秒时刻正确同步,则可能只将时间设置为:59:00并再次暂时不同步。

  6. 让我们考虑一个真实的例子。值1341100800000代表2012年7月1日恰好在UTC午夜。 (您可以在this web site上或在您的Java或Joda时间码中查看。验证。)如果我们除以86400000,我们将完全 <155>自1970年1月1日UTC以来已过去。

    此值为35闰秒的包含,包括2012年6月30日结束前一秒钟发生的值。就像闰秒从未发生过一样。

    所以大多数时候,你不需要担心闰秒。假装他们不存在。让您的操作系统以任何方式处理它们。

    如果您需要超精确的时间测量,也许是在科学背景下,那么您无论如何都不应该使用计算机的系统时钟。除了闰秒可以被保持,拉伸或忽略之外,它的设计并不是精确的定时器。相反,您应该使用一些非常专业的计时硬件,例如this vendor提供的硬件。

    更新:如果您要快速记录事件(每秒许多事件),并且您的操作系统具有上述#3中描述的行为,则可能需要处理闰秒的位置。在这种情况下,我的建议是不按时间戳排序,而是考虑保留一个单独的单调增加的序列号,并按此排序。

    例如,您的数据库中可能已经有一个自动递增的整数ID。您仍然可以通过where子句中的timestamp 过滤来获取特定日期的数据,但是您可以按ID排序,以便顺序排列事件,即使时间戳不是。

    有关其他建议,请参阅Teo's answer