64位Unix时间戳转换

12

有没有适用于32位系统的64位Unix时间戳转换的C++实现? 我需要将 struct tm 转换为64位整数,反之亦然,包括闰年,时区和协调世界时。而且我需要它能够跨平台运行,至少能在GNU / Linux和Windows上使用。


那个时间戳应该代表什么?如果它只是自纪元以来的秒数,那么您可以使用32位转换函数并将结果复制到64位整数中(只要在2038年之前)。 - PlasmaHH
大多数系统上的time_t不是已经64位了吗?Windows也有大多数函数的64位变体。只需在Google上搜索即可。 - RedX
整数无法表示时区,因为它是相对于任意纪元(在UNIX中为1970年1月1日)的偏移量,但时区未定义。如果要表示这些元素,您必须将它们分解为“结构体”。另外,没有人会“表示”闰年;他们会怎么做呢?持有一个毫无意义的计数或其他什么东西吗? - trojanfoe
1
PlasmaHH: 没错,就是在2038年之前不会发生。 - Adam Trhon
RedX: 我相信只有在64位系统上才可以。如果我错了,请纠正我。 - Adam Trhon
trojanfoe:你说得对,整数无法表示其中任何一个。但是在转换为“struct tm”时必须考虑它们。 - Adam Trhon
5个回答

14
您需要:
typedef long long time64_t; 
time64_t mktime64(struct tm *t); 
struct tm* localtime64_r(const time64_t* t, struct tm* p);

最初(2011年),这个答案包含指向2038bug.com的链接,可以下载包含所述函数的小型pivotal_gmtime_r库。那个库已经从2038bug.com中删除,链接已经失效并被管理员从答案中删除。现在似乎可以在此处找到pivotal_gmtime_r代码:

https://github.com/franklin373/mortage/tree/master/time_pivotal

此外,我还发现另一个更近期的库,称为y2038,也实现了mktime64和localtime64_r:

https://github.com/evalEmpire/y2038


这对我来说看起来是一个完美的解决方案。在我的理解中,mktime64函数期望其参数为UTC时间。通过使用localtime64_r将time_t转换为struct tm,再使用mktime64进行反向转换并不能得到原始时间,而通过使用gmtime64_r将time_t转换为struct tm,再使用mktime64进行反向转换则可以。您能否确认一下? - Adam Trhon
是的,确认!在这个线程中提到了localtime这么多次,我混淆了。 :) 当然你需要gmtime64_r - Alexei Khlebnikov
但据我所知,“mktime”应该期望本地时间,不是吗? - Adam Trhon
是的,看起来像是不一致的地方。标准的 mktime 期望本地时间,并且会从您的本地设置中获取时区并将其转换为 UTC。但是这个 mktime64 是“时区无关的”。也就是说,如果您提供了输入 struct tmUTC 中,它将以 UTC 的形式给出输出 time64_t,如果您提供了 UTC+10 的输入,则输出为 UTC+10 等。 - Alexei Khlebnikov
那些函数在哪里?最近的Linux上运行ack mktime64 /usr/include/没有返回结果。 - dargaud
@dargaud,我已经更新了答案,并附上了链接和一些解释。 - Alexei Khlebnikov

7
struct tm*转换为time_t的函数是mktime。你可以在多个实现中找到它,例如在Glibc和libvxc的mktime.c文件中。你可以采用这些代码(假设它们对你合法,请尊重许可证),将time_t更改为像int64_t这样的64位整数。
time_t 转换为 struct tm* 的其他功能是 localtimegmtime,你也可以采用类似的方法进行更改。
然而,您可能存在更基本的问题:在2040年运行的32位机器应该有一种适当地以64位变体的 time_t 形式提供当前时间(与time系统调用相同),这要困难得多(这取决于内核和硬件)。

有没有一种实现方式可以以可移植的方式处理 localtime - Adam Trhon
1
我不明白这个问题。你所说的“便携式”是什么意思?time_t 可以是 32 位或 64 位整数类型(因此机器代码必须不同)。 - Basile Starynkevitch
它是完全可移植的,但他们不关心UTC和本地时间之间的差异。 - Adam Trhon
你所链接的mktime()版本非常幼稚。例如,将其与minix中的mktime()进行比较。 - jfs
+1,对于我的目的来说,libvxc相当不错。顺便说一句,修复本地->UTC转换问题只需要一行代码,所以这并不是一个问题。 - Agnius Vasiliauskas
显示剩余2条评论

6
您似乎假设在32位系统上,time_t是32位的,事实可能并非如此。
从Visual Studio 2005开始,在Windows上,即使您为32位Windows编译,time_t的大小也为64位。
不幸的是,glibc将其定义为long int,这在32位系统上是一个32位的整数。这意味着基于gcc/glibc(如Cygwin)的32位Linux和其他32位平台将无法使用64位时间戳。
如果您的应用程序必须在32位glibc上运行,则应使用自己的转换函数,这些函数可以是重新编译以使用64位时间戳的C库中的相同函数。
如果您需要具有宽松许可(BSD)的源代码,则可以查看minix3中的这些函数。 这里是localtime。源代码已经超链接,所以您可以轻松找到其他函数。

0

32位Linux上的64位时间支持是在5.1内核首次引入的,新增了新的*time64系统调用(因为更改旧系统调用的返回类型会破坏旧应用程序)。查看这个表格,你会发现这些系统调用仅适用于32位平台。

但这只是从内核方面提供的支持。您可以直接调用clock_gettime64(通过内联汇编或使用{{link1:syscall()}}函数)来获取当前时间,但是您需要使用特定于Linux的代码,因为目前还没有glibc的支持。要获得完整的用户空间支持,您必须在Linux 5.6或更高版本以及musl 1.2+或glibc 2.32+上运行。只需重新构建您的代码,time_t将变为64位长。现在使用time_t的代码将变得完全可移植。
所有用户空间必须使用64位的time_t进行编译,这将在即将发布的musl-1.2和glibc-2.32版本中得到支持,同时还需要安装来自linux-5.6或更高版本的内核头文件。
直接使用系统调用接口的应用程序需要移植为使用在linux-5.1中添加的time64系统调用来替换现有的系统调用。这会影响大多数futex()seccomp()的用户,以及那些基于libc之外的运行时环境的编程语言。
欲了解更多信息,请参阅https://lkml.org/lkml/2020/1/29/355?anz=web

-1

如果你是Windows的粉丝,可以使用stuct tm *_localtime64 ( const __time64_t *timer);


完美的解决方案适用于Windows,但正如我在问题中提到的那样,我需要同时支持Windows和Linux。 - Adam Trhon
有一个叫做“ifndef”的东西..使用它。 - Alex Force
真的,没错。但我的经验法则是,除非绝对必要,否则不要考虑条件编译。 - Adam Trhon
"_localtime64_s" 更安全,顺便说一下。 - Guy L

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接