Unix 时间、闰秒以及将 Unix 时间转换为日期

Unix 时间、闰秒以及将 Unix 时间转换为日期

我读过很多很多关于 Unix 时间的帖子,很多人说一天是 86400 秒。

但是页面就像谈论闰秒。这让我很困惑。我读到Unix时间是基于UTC的,我所能理解的是UTC和TAI之间的区别是闰秒,所以Unix必须支持闰秒,否则为什么不说基于TAI而不是UTC呢?

为了让自己更加困惑,我尝试看看Windows做了什么,我发现的答案是它不计算闰秒,但是它说基于 UTC,又令人困惑,如果不计算闰秒为什么要说 UTC?

不管怎样,我想问一下在 Unix 中如何将整数秒转换为精确日期?

答案1

我读过很多很多关于 Unix 时间的帖子,很多人说一天是 86400 秒。

这就是定义,是的。

但像这样的页面谈论的是闰秒。这让我很困惑。

你并不是唯一的一个。这是一个搞笑的混乱。

我读到 Unix 时间是基于 UTC

是的,见下文。

我能理解的是UTC和TAI之间的区别是闰秒

是的。

那么Unix必须支持leap

事实并非如此。它实际上闭上了眼睛,拒绝听任何关于这种尴尬和不切实际的技术问题的讨论。

POSIX 定义“自纪元以来的秒数”基于细分的 UTC 时间,明确假设所有天的长度均为 86400 秒:

自纪元以来 4.15 秒

一个近似自纪元以来经过的秒数的值。协调世界时[世界标准时间]名称(以秒为单位指定(tm_秒), 分钟 (最小时间), 小时 (tm_小时),自当年 1 月 1 日起的天数 (tm_yday),日历年减 1900 (tm_year)) 与自纪元以来以秒表示的时间相关,根据下面的表达式。

如果年份 <1970 或值为负,则关系未定义。如果年份 >=1970 并且该值非负,则根据 C 语言表达式,该值与协调世界时名称相关,其中tm_秒,最小时间,tm_小时,tm_yday, 和 tm_year都是整数类型:

tm_sec + tm_min*60 + tm_hour*3600 + tm_yday*86400 +
    (tm_year-70)*31536000 + ((tm_year-69)/4)*86400 -
    ((tm_year-1)/100)*86400 + ((tm_year+299)/400)*86400

一天中的实际时间与自纪元以来的当前秒数之间的关系未指定。

如何对自纪元以来的秒值进行任何更改以与当前实际时间保持所需的关系是由实现定义的。按照自纪元以来的秒数表示,每一天应精确计算 86400 秒。

( 的奇数处理tm_year是为了考虑闰日;除法是截断整数除法。)

基本原理部分直接承认忽略了闰秒:

协调世界时 (UTC) 包括闰秒。然而,在 POSIX 时间(自纪元以来的秒数)中,闰秒被忽略(未应用)提供一种简单且兼容的计算时间差的方法。因此,尽管其外观如此,分解后的 POSIX 时间不一定是 UTC。


上面的表达式意味着当插入闰秒时,“自纪元以来的秒数”会重复一整秒:

UTC 1972-06-30 23:59:59 = 78796799 seconds since epoch
UTC 1972-06-30 23:59:60 = 78796800 seconds since epoch
UTC 1972-07-01 00:00:00 = 78796800 seconds since epoch
UTC 1972-07-01 00:00:01 = 78796801 seconds since epoch

这意味着要么自纪元以来的那些“秒”的长度(物理上)并不相同,要么纪元本身实际上“移动”。随你挑选。

  • 如上所述,一秒是两(物理,SI / UTC)秒长,所以显然它们的长度并不相同。

  • 如果您采用“所有天都是 86400 秒”的定义,假设长度恒定的 SI 秒并对此进行计算,则纪元“移动”如下所示。 (也就是说,由于 UTC 1972-07-01 00:00:00 是 UTC 1970-00-01 00:00:01 之后的 78796800 SI 秒,因此它必须是当时的有效纪元。)

好吧,他们确实用“大约”来对冲。

此外,这是不存在简单但精确的解决方案的情况之一。你可以到从三件好事中选两件并且必须忍受

  • a) 可变长度的天数(以秒为单位);
  • b) 本身长度不同的秒;或者
  • c) 最终与太阳不同步的日期系统。

为了简化日期计算,POSIX 拒绝 (a),但在其他方面则匹配 UTC 以匹配民用时间,因此拒绝 (c) 并选择接受缺点 (b)。基本原理中明确提到了计算的简单性:

自纪元以来的秒数是否应考虑闰秒的话题已经在很多场合进行了争论,每次都达成共识(每次都承认不同意见):对大多数用户来说,最好对所有日期进行相同的处理。 (也就是说,大多数应用程序被判定为假设所有天都采用单一长度(以自纪元以​​来的秒为单位进行测量)。因此,闰秒不适用于自纪元以来的秒数。)

(“一天中的实际时间与自纪元以来的当前秒数之间的关系未指定。”这句话本身很奇怪,因为几乎所有实际目的 UTC 都是“一天中的实际时间”,并且定义上面依赖于 UTC。我不确定它指的是什么,但我认为这只是说系统时钟不需要准确。而且,如果没有外部同步,大多数计算机时钟都不需要。

同样,“因此,尽管出现了 POSIX 时间,但其具体时间并不一定是 UTC。”基本原理部分看起来很奇怪,但适用于闰秒。否则,给定的等价似乎意味着分解的 POSIX 时间世界标准时间。)


但不要指望我能把它弄对,因为聪明的人已经写了足够多的文章了。


不管怎样,我想问一下在 Unix 中如何将整数秒转换为精确日期?

取决于具体是哪几秒......

如果是 Unix 时间,除以 86400 即可得到天数,然后遵循有关可变长度月份、闰日和时区的通常规则。

或者更确切地说,打电话localtime()gmtime()为您做这件事。

如果是其他时间,请使用一些可以处理您所拥有的内容的库。

答案2

您混淆了多个概念。 UNIX 时间(从足够的高度来看)是一个秒计数器,这只是一个本地概念。最后,有人拿着一个时钟给你说“根据这个漂亮的时钟,现在距离 1970 年 1 月 1 日已经过去了多少秒”,你就说“好吧,那么现在距离 1970 年 1 月 1 日已经过去了多少秒” ”并继续数秒。这对于许多“家庭”计时用途来说是可以的,例如,存储您上次更改日记文档的时间。 TAI也只是一个秒计数器,但它是一个协调的计数器,这意味着它不仅仅是本地的时间计数,而是许多原子钟的平均值。

UTC 是完全不同的东西:它定义什么时候它是——而不仅仅是某个常见事件发生后的秒数。

例如,在许多情况下,“自纪元以来已有一千四百二十万二十六秒”不如说“这里是早上 9 点 12 分,今天是六月六号”有用。


需要明确的是:“UNIX 时间”只是自特定事件(纪元)以来经过的秒数。如果您有一个每秒滴答一次的完美节拍器,并且您计算了自该日期以来的滴答声,那就是 UNIX 时间。 (如果不是因为这个计数器实际上是从 UTC 派生出来的,有效地每当 UTC 插入闰秒时,该纪元就会向前移动一秒。)

现在,您的困惑源于这样一个事实:例如 1697300527(这是几秒钟前的 UNIX 时间)对于人类来说并不是一个非常有用的时间。您需要将第二个计数转换为人类可读的日期时间(例如,2023 年 10 月 14 日星期六 18:22:01)。

时间转换当然必须考虑闰年、闰秒、时区和其他修正。这意味着从 UNIX 时间到 UTC 时间或任何其他时间表示形式的转换不一定是平滑、连续的,甚至是明确的。 (例如:可能有多个 UNIX 时间戳对应于您所在时区夏令时结束当天的凌晨 3:15。)

就是这样。虽然你总是可以“轻松而明确地”说“现在距离纪元已经过去了这么多秒”,简单地通过计算从那时起的秒数(这就是 UNIX 时间),用于人类目的的本地时间(或协调时间)表示的转换可以是非单调的。

不管怎样,我想问一下在 Unix 中如何将整数秒转换为精确日期?

通过使用您的平台拥有的时区信息。 Chris Davies 建议使用date,我同意。任何合理的编程语言 都可以访问 tzinfo 数据库。


1 C:ctime(…)、C++:、std::chronoPython:python -c 'import datetime;print(datetime.datetime.now())'、Perl、Haskell,...)

答案3

如果您有合适的版本,则date可以使用其内置功能:

date
Sat Oct 14 17:17:59 BST 2023

date +'%s'
1697300279

date --date '@1697300279'
Sat Oct 14 17:17:59 BST 2023

date --date '@1697300279' +'%Y-%m-%d %H:%M:%S'
2023-10-14 17:17:59

相关内容