我是否使用了 tm/mktime 错误,如果没有,是否有解决方法?
Am I using tm/mktime wrong, and if not is there a workaround?
我认为下面的程序应该输出从公元 1 年到 1970 年的每一年的第一天到 1970 年的秒数,前面是 time_t
在它编译的系统上的大小(CHAR_BIT
是一个宏,所以我认为你不能只是复制编译后的可执行文件并假设它是正确的,尽管实际上现在所有东西都使用 8 位 char
s)。
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
void do_time(int year)
{
time_t utc;
struct tm tp;
memset(&tp, 0, sizeof(tp));
tp.tm_sec = 0;
tp.tm_min = 0;
tp.tm_hour = 0;
tp.tm_mday = 1;
tp.tm_mon = 0;
tp.tm_year = year - 1900;
tp.tm_wday = 1;
tp.tm_yday = 0;
tp.tm_isdst = -1;
printf("%d %ld\n",year, mktime(&tp));
}
int main(){
printf("time_t is %lu bits\n",sizeof(time_t)*CHAR_BIT);
for (int i = 1; i<1971; i++)
do_time(i);
exit(0);
}
但是在 OS X (10.11.3 15D21) 上这只适用于 >= 1902 年,尽管 time_t
是 64 位签名的。我可能会理解 Apple 的程序员是否懒惰并且不支持 1970 年之前的任何年份,但是回到 1902 年然后停止的正确行为看起来更像是我的错误。
在 UNIX OS 中,通常有启用 64 位版本的时间函数。 OS X 可能有类似的东西,虽然我通过快速搜索找不到它。有关详细信息,请参阅 64 bit unix timestamp conversion。
编辑:
我找到了一个 Mac 来测试它,不幸的是它似乎没有 mktime64 功能。我确实发现这个 this library 可以作为一种变通方法,但我还没有亲自测试它。
查阅C标准:
The range and precision of times representable in clock_t
and time_t
are
implementation-defined. [..]
[N1570 §7.27.1/4] (emphasis mine)
再往下,关于mktime
:
The mktime
function returns the specified calendar time encoded as a value of type
time_t
. If the calendar time cannot be represented, the function returns the value
(time_t)(-1)
.
[N1570 §7.27.2.3/3]
因此,只要 mktime
的 return 值在它不起作用的年份是 (time_t)(-1)
......你就靠你自己了。
实际上,IMO,标准对这一切有点沉默:
[..] int tm_year; // years since 1900
[..]
[N1570 §7.27.1/4]
这可能意味着自 1900 年以来的(正)年,但为什么要使用有符号整数。
附带说明:在我的系统 (Linux 3.14.40 x86_64 glibc-2.21) 上,我得到 ...
time_t is 64 bits
1 -62135600008
...
1969 -31539600
1970 -3600
考虑变通部分:您当然可以查看正在执行您想要的操作的 libc 实现,并尝试使用他们的代码(如果这对于您需要遵守的任何许可而言是可能的)。 Here's 我的系统使用的那个。
我认为下面的程序应该输出从公元 1 年到 1970 年的每一年的第一天到 1970 年的秒数,前面是 time_t
在它编译的系统上的大小(CHAR_BIT
是一个宏,所以我认为你不能只是复制编译后的可执行文件并假设它是正确的,尽管实际上现在所有东西都使用 8 位 char
s)。
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
void do_time(int year)
{
time_t utc;
struct tm tp;
memset(&tp, 0, sizeof(tp));
tp.tm_sec = 0;
tp.tm_min = 0;
tp.tm_hour = 0;
tp.tm_mday = 1;
tp.tm_mon = 0;
tp.tm_year = year - 1900;
tp.tm_wday = 1;
tp.tm_yday = 0;
tp.tm_isdst = -1;
printf("%d %ld\n",year, mktime(&tp));
}
int main(){
printf("time_t is %lu bits\n",sizeof(time_t)*CHAR_BIT);
for (int i = 1; i<1971; i++)
do_time(i);
exit(0);
}
但是在 OS X (10.11.3 15D21) 上这只适用于 >= 1902 年,尽管 time_t
是 64 位签名的。我可能会理解 Apple 的程序员是否懒惰并且不支持 1970 年之前的任何年份,但是回到 1902 年然后停止的正确行为看起来更像是我的错误。
在 UNIX OS 中,通常有启用 64 位版本的时间函数。 OS X 可能有类似的东西,虽然我通过快速搜索找不到它。有关详细信息,请参阅 64 bit unix timestamp conversion。
编辑: 我找到了一个 Mac 来测试它,不幸的是它似乎没有 mktime64 功能。我确实发现这个 this library 可以作为一种变通方法,但我还没有亲自测试它。
查阅C标准:
The range and precision of times representable in
clock_t
andtime_t
are implementation-defined. [..][N1570 §7.27.1/4] (emphasis mine)
再往下,关于mktime
:
The
mktime
function returns the specified calendar time encoded as a value of typetime_t
. If the calendar time cannot be represented, the function returns the value(time_t)(-1)
.[N1570 §7.27.2.3/3]
因此,只要 mktime
的 return 值在它不起作用的年份是 (time_t)(-1)
......你就靠你自己了。
实际上,IMO,标准对这一切有点沉默:
[..]
int tm_year; // years since 1900
[..][N1570 §7.27.1/4]
这可能意味着自 1900 年以来的(正)年,但为什么要使用有符号整数。
附带说明:在我的系统 (Linux 3.14.40 x86_64 glibc-2.21) 上,我得到 ...
time_t is 64 bits
1 -62135600008
...
1969 -31539600
1970 -3600
考虑变通部分:您当然可以查看正在执行您想要的操作的 libc 实现,并尝试使用他们的代码(如果这对于您需要遵守的任何许可而言是可能的)。 Here's 我的系统使用的那个。