6

我正在创建应用程序,我想知道应该/可以如何处理闰秒。我将尝试用一个(希望如此)简单的示例情况来描述这个问题。在这种简单的情况下,您可以很容易地争辩说,每 +- 1.5 年的额外等待时间可能无关紧要,但我仍然会睡得更好,因为知道它在“所有”情况下都能正常工作/我想要的方式:)


情况


(我们不考虑任何延迟,因此当用户按下按钮时,它会立即“发生”)

您有一个游戏,您可以在其中创建士兵。

  • 每个士兵需要 110 秒来创建。
  • 用户按下按钮在day 1 - 23:58:30
  • 然后,用户期望110s稍后创建士兵。通过正常的“日期时间计算”,您的应用程序最终会得到day 2 - 00:00:20

day 1现在和day 2-->之间恰好有一个闰秒23:59:60。按照这种方法,用户实际上会等待111s他的士兵。

我更喜欢使用 Unix 时间戳。这样,您只需添加110s到当前时间(以秒为单位)。但据我所知,这也没有考虑闰秒。你最终还是会111s在实际时间等待。


问题


我应该怎么做才能确保用户或程序只等待它应该等待的时间?

是否有一个常用的时间戳确实考虑了闰秒?

我是否应该始终检查是否发生了闰秒? (可能会造成很多cpu功耗的“腰”?)

编辑:我主要在 Javascript (Node.js) 中工作,但 C、php 或 Python 中的示例也可以正常工作!

4

4 回答 4

5

UTC 时间与原子时间的不同之处正是那些闰秒。如果没有对原子时间的引用,您无法仅从 UTC 检测到何时插入了闰秒。这使得 UTC 中的时间“几乎”连续,因为这些超过 0.5 秒和不到 1.0 秒的小跳跃恰好扰乱了时间,当它们发生时。UTC 的定义是为了节省日历时间并将其与地球运动相匹配,但尽管如此,只需忽略这些闰秒,就可以认为它是连续的。

当插入闰秒时,您只需在时钟中注意到任何内容,因为只有原子时钟与 UTC 时钟的差异已得到纠正。只有在您计算行星或卫星轨道的情况下,您必须更正原子钟,否则您将在计算中插入整个闰秒(并使您的轨道发生变化)实际效果是UTC已经提前一秒被一个幻影。这只会影响秒数,在原子时间和 UTC 之间的差异中再损失一秒。但是您注意到您的机器中没有发生任何事情。恰好与闰秒发生重叠的时间戳的实时差异不受影响,因为闰秒插入仅影响原子秒数和UTC秒数的差异。

如果您要考虑与闰秒重叠的时间(例如,在某些外星飞行器的轨道参数计算中),那么您必须多花一秒(在原子钟中没有计算在内,是的UTC 时间)并将其添加到间隔中,否则您的微积分将不正确。但是天体物理学总是使用正确的时间尺度来做他们的微积分,所以不要犹豫太空垃圾会错误地落在你的头上。

如果您使用时钟同步程序(例如 ntpd),那么闰秒的插入可以通过多种方式发生:

  • 在插入闰时钟时,通过在系统时钟上加一来插入秒。这使得奇怪的事情发生,因为这些时间的超时延迟受到时钟调整的严重影响。

  • 时钟在闰秒前的某个时间(比如说两个小时前)快速调整,并在闰秒后的某个时间再次正常调整。您将有一个连续的时间尺度,持续时间的秒数略小于标准原子时间来调整时间。

  • 让时钟运行。时钟循环突然“看到”参考(“新”utc 刻度)与其内部时钟之间的一秒偏移,因此它开始以正常方式纠正它(调整时钟速度)。这与前一个相同,但会导致更多的偏移差异(前一点的一整秒对一半)

目前我不知道您的时钟同步应用程序是否遵循第一种或第二种方法。至少在linux中我认为使用的是第二个或第三个,通常你不会注意到它。

笔记

在您举的例子中,假设最坏的情况(时间通过在时钟中进行步进调整来同步)您将在 111 秒而不是 110 秒中创建士兵,但这没什么好担心的,因为一切在那次失误中发生在 111 秒而不是 110 秒。你会得到1.0%更多的时间来得到你的士兵,但1.0%在同一时期一切都发生得更慢,而且你的士兵没有受到其他士兵的实际惩罚或闰秒之后。

最后,如果您不使用时间同步程序,您的时钟将受到其实际偏移量(您的时钟时间与实际时间的差异)的影响,而不是对其进行步进调整的漂移。

于 2016-06-23T06:16:34.437 回答
2

你不能在 javascript 中做到这一点。javascript中没有闰秒。ECMAScript 5,第 15.9.1.1 节说:

自 1970 年 1 月 1 日 UTC 以来,时间在 ECMAScript 中以毫秒为单位进行测量。在时间值中,闰秒被忽略。假设每天正好有 86,400,000 毫秒

时间值不能保证是单调的,当闰秒发生时,它们很可能不会是单调的。在 POSIX 中,您可以使用clock_gettimea clk_idof CLOCK_MONOTONIC(如果_POSIX_MONOTONIC_CLOCK已定义)。

于 2016-06-22T17:19:38.183 回答
2

你真的应该在这里使用时间戳。

时间戳只是从预定义日期(称为“纪元”)经过的秒数(所有秒数,无论是否跳跃)。这只是一个柜台。

它不受闰秒、夏季/冬季时区变化的影响,甚至不受每年更改时区边界的疯狂政府的影响。

使用时间戳,您始终可以计算 UTC、GMT 和欧洲/莫斯科现在的时间(有闰秒和没有闰秒,这取决于您的 tz_data 配置)。有时无法进行反向操作。

于 2016-06-22T17:00:31.437 回答
1

一些 RTL 支持闰秒。例如,在 Ada 2005 中,Ada.Calendar.Time 支持闰秒,虽然不明显,但只能通过子包Ada.Calendar.Formatting 和 Ada.Calendar.Arithmetic观察到。此外,Ada.Calendar.Formatting.Image 为闰秒写入“:59”,而 Ada.Calendar.Formatting.Value 不接受“:60”。

在没有原生闰秒支持的 GNAT目标中,需要“-y”绑定键来打开它。使用这把钥匙,即使在 Windows 上,我也设法使用闰秒时间。我制作了一个小型库,用于从 UNIX time_t 转换有或没有闰秒,以及支持闰秒的格式化/解析函数,以充分展示 Ada 标准的这一特性。当然,在 Windows 上,秒的不一致是不可避免的,但是矩之间的差异可以正确计算,并且可以对从其他地方获得的值进行操作。

应使用单调时间以避免过程中的冲击。无论如何都应该使用它,因为用户可以更改时钟时间,或者可以通过同步来调整时间,这比闰秒发生得更频繁,更难以预测。

如果你真的需要这个,你可以使用单调时间来推断平民:

with Ada.Calendar;
with Ada.Real_Time;

-- ...

------------------------
-- Extrapolated_Clock --
------------------------

type Extrapolated_Clock is tagged record
   C_Clock : Ada.Calendar.Time := Ada.Calendar.Clock;
   RT_Clock : Ada.Real_Time.Time := Ada.Real_Time.Clock;
end record;

use all type Ada.Calendar.Time;
use all type Ada.Real_Time.Time;
use all type Ada.Real_Time.Time_Span;

procedure Synchronize (Object : in out Extrapolated_Clock);
function Extrapolate (Object : Extrapolated_Clock) return Ada.Calendar.Time is
  (Object.C_Clock + To_Duration (Ada.Real_Time.Clock - Object.RT_Clock));

-- ...

------------------------------------
-- Extrapolated_Clock.Synchronize --
------------------------------------

procedure Synchronize (Object : in out Extrapolated_Clock) is
begin
   Object.C_Clock := Ada.Calendar.Clock;
   Object.RT_Clock := Ada.Real_Time.Clock;
end Synchronize;

您可能还需要确保捕获的时间时刻不能是闰秒(捕获后的第二个不是闰秒)。

于 2016-12-08T18:40:47.090 回答