如何在我的应用程序中正确处理闰秒

How can I handle a leap second correctly in my application

我正在创建应用程序,我想知道我如何 should/can 处理闰秒。我将尝试用(希望如此)简单的示例情况来描述问题。在这种简单的情况下,您可以很容易地争辩说,每 +- 1.5 年多等待一秒可能无关紧要,但我仍然会睡得更好,因为我知道它有效 correctly/the 我在 'all' 情况下也希望如此:)


情况


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

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

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

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


问题


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

是否有考虑闰秒的常用时间戳?

我应该经常检查是否出现闰秒吗? (可能会导致很多 "waist" 的 cpu 功率? )

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

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

时间戳只是从预定义日期(称为 "epoch")过去的秒数(所有秒,无关紧要)。这只是一个计数器。

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

使用时间戳,您始终可以计算 UTC、GMT 和 Europe/Moskow 中的现在时间(有闰秒和没有闰秒,这取决于您的 tz_data 配置)。逆向操作有时做不到。

您不能在 javascript 中这样做。 javascript 中没有闰秒。 ECMAScript 5,第 15.9.1.1 节说:

Time is measured in ECMAScript in milliseconds since 01 January, 1970 UTC. In time values leap seconds are ignored. It is assumed that there are exactly 86,400,000 milliseconds per day

不能保证时间值是单调的,并且当出现闰秒时,它们很可能不会。在 POSIX 中,您可以将 clock_gettimeclk_idCLOCK_MONOTONIC 一起使用(如果定义了 _POSIX_MONOTONIC_CLOCK)。

UTC 时间与原子时间的区别恰恰在于那些闰秒。如果不参考原子时间,则无法仅从 UTC 检测到何时插入了闰秒。这使得 UTC "almost" 中的时间连续,因为这些超过 0.5s 和小于 1.0s 的小跳跃碰巧会扰乱时间,当它们发生时。 UTC 的定义是为了保存日历时间并将其与地球运动相匹配,但尽管如此,只需忽略这些闰秒就可以认为它是连续的。

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

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

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

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

  • 时钟在闰秒之前的某个时间(比方说两个小时之前)调整得快,在闰秒之后的某个时间再次调整正常。你将有一个连续的时间尺度,持续时间的秒数比标准原子时间少一点来调整时间。

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

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

注意

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

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

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

the GNAT targets not having native leap seconds support need "-y" binder key to turn it on. With this key I managed to work with leap seconds time even on Windows. I've made a tiny library for conversion from UNIX time_t with or without leap seconds, and also formatting/parsing functions supporting leap seconds中充分展示Ada标准的这个特性。当然,在Windows上,秒的不一致在所难免,但可以适当计算矩差,对从别处得到的值进行运算。

过程中应使用

Monotonic time以避免震荡。无论如何都应该使用它,因为用户可以更改时钟时间,或者可以通过同步来调整时间,与闰秒相比,这种事情发生得更频繁,更难以预测。

如果真的需要这个,可以用单调时间外推民用时间:

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;

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