5

作为:

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 纪元(例如 ts)存储在 DB 中(毫秒精度),如何处理以下情况?

  1. 如何确保 ts 总是在增加而不是向后?
  2. 如何从考虑到闰秒的 db 中准确选择 100 秒间隔?

例如

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

上面的 SQL 会返回发生在 T1, T1+1, T1+2, .. 直到 T1+99 的事件,但是由于闰秒,包含 1s 的闰时间,结果可能是错误的,如何取考虑到这一点?

4

2 回答 2

4

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

根据同一维基百科页面上的注释 #2,闰秒是不可预测的,这给您留下了 2 个选择:一个通用解决方案(假设您有这些时间戳索引的表)总是可以插入条目,并且在一个条目出现之前最后插入的条目(可能在闰秒内)您可以开始一个“拖尾”过程,该过程基本上是在条目中添加一些毫秒,以确保它超出闰秒的范围。当插入的条目再次具有比先前插入的条目更大的值时,该过程可以停止。我称其为“涂片”,因为它在某种程度上受到了 Google 的“Leap Smear”技术的启发(尽管不完全相同):http: //googleblog.blogspot.in/2011/09/time-technology-and-leaping-seconds.html 我看到它的方式虽然这会给您的数据库带来一些压力,并且插入查询几乎是我见过的最复杂的查询之一(如果它甚至可以单独在 SQL 中使用)。

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

当然,缺点是您在闰秒中牺牲了一点准确性(考虑到闰秒非常罕见,牺牲并不大),但好处是它很简单并且您的订单得到保证。

如果您或其他人找到更好的解决方案,请在这里分享,这个话题很有趣:)

更新:我已经为第 3 个解决方案(完全在 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;
}
于 2013-11-03T18:07:39.063 回答
4

来自Joda Time 常见问题解答

是否支持闰秒?

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

来自闰秒的 IANA/Olson TZDB 文件

尽管该定义还包括减少秒数(“负”闰秒)的可能性,但这从未发生过,并且在可预见的将来不太可能是必需的。

你的第一个问题:

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

一个负闰秒将使您处于相同的时间戳(两个经过秒的一个值),因此如果没有两个负闰秒,您就无法真正向后退。由于似乎不太可能出现负闰秒,我会说这是你永远不会真正遇到的问题。

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

你的第二个问题:

如何从考虑到闰秒的 db 中准确选择 100 秒间隔?

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

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

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

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

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

  5. 操作系统可能会跳过它并且什么也不做。您的时钟将不同步,直到下一次通过 NTP 同步。如果它恰好在闰秒的那一刻同步,它可能只是将时间设置为:59:00再次不同步一段时间。

让我们考虑一个真实的例子。该值1341100800000代表 2012 年 7 月 1 日,恰好在 UTC 午夜。(您可以在此网站上查看它,或者在您的 Java 或 Joda 时间代码中进行验证。)如果我们除以 86400000,我们将得到自 1970 年 1 月 1日以来经过的 15522 天 UTC

该值包括35 个闰秒,包括 2012 年 6 月 30 日结束前一秒发生的闰秒。就好像闰秒根本没有发生。

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

如果您需要超精确的时间测量,也许是在科学环境中,那么您无论如何都不应该使用计算机的系统时钟。除了可以保持、延长或忽略闰秒这一事实之外,它的设计并不是为了像计时器那样精确。相反,您可能应该使用一些非常专业的计时硬件,例如该供应商提供的那些。

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

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

有关其他建议,请参阅Teo 的回答

于 2013-11-04T01:02:58.727 回答