t为纯ISO C的时代?
除了C标准库(普通ISO C,没有POSIX,因此没有假设time_t
是在“纪元以来的秒数”中表示的),什么是最简单的方法来获得time_t
值对应于1970年1月1日00:00 :00 UTC ?
UTC部分是关键; 否则,只需在正确初始化的struct tm
上使用mktime
就可以平息问题。
或者(这实际上是问题的“要点”),如何可移植地确定给定time_t
值(例如,通过time(0)
获得的当前时间time(0)
与历元之间的POSIX秒数? “POSIX秒”是指POSIX的“秒以来的秒数”中使用的定义,它不使用闰秒。 如果这听起来太复杂了,就回到第一段所述的问题,并假设时间在time_t
可以表示。
这里有一个方法可以做到这一点,如果没有人打败它,“最简单”
02 Jan 1970 00:00:00
在struct tm
上调用mktime
31 Dec 1969 00:00:00
上调用struct tm
上的mktime
。 这可以合理返回-1,在这种情况下将其视为0。 time_t
,当传递给gmtime
,结果为01 Jan 1970 00:00:00
假设本地时间与UTC的时差不超过24小时,我敢肯定这是事实。 如有必要,我们可以扩大边界,在极端情况下我们可以在0
和time(0)
之间进行搜索。
二进制搜索可以通过例如插值来改进。 但是谁知道,也许一些疯狂的时区(或tzinfo数据破碎)可能导致12月/ 1月的夏令时变化。 我怀疑是否发生了任何实时区域。 但是C标准并没有禁止它,只有通过常识。
如果不是这样,我想我们可以根据gmtime(mktime(01 Jan))
(计算时区)和01 Jan 1970 00:00:00
与01 Jan 1970 00:00:01
(以获得time_t
的精度)。
您的问题相当重要:ISO C在时区上几乎完全支持,只需提供mktime()
和localtime()
和gmtime()
转换,并为夏令时提供挂钩。 (他们执行,你决定。)
所以似乎只有两件事你可以做:
time_t
是UTC时代以来的秒数,并使用gmtime()
来验证,并且如果它失败,则会发出恐慌或警报; 要么 步骤1:选择任何time_t
(当前时间将工作得很好)作为参考点; 称它为t0
。
步骤2:在t0
上调用gmtime
,并以分解的struct tm
形式计算结果与历元之间的差异。
步骤3:调用localtime
上t0
并从步骤2到所得的应用破旧差struct tm
。 然后调用mktime
来获取time_t
。
结果应该是表示时代的time_t
。
当本地时间偏移量不是一直保持不变时,例如在已添加或放弃夏令时或从观察一个区域切换到另一个区域的地方,我第一次尝试实现此功能存在问题。 这似乎是因为时区信息所基于的struct tm
的数据发生更改。 这是最初的实施,其问题在于:
time_t get_epoch(time_t t0)
{
struct tm gmt = *gmtime(&t0);
struct tm tmp = *localtime(&t0);
tmp.tm_sec -= gmt.tm_sec;
tmp.tm_min -= gmt.tm_min;
tmp.tm_hour -= gmt.tm_hour;
tmp.tm_mday -= gmt.tm_mday-1;
tmp.tm_mon -= gmt.tm_mon;
tmp.tm_year -= gmt.tm_year-70;
return mktime(&tmp);
}
和一个改进版本,其中posix_time
是一个函数,用于使用POSIX公式(http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap04.html#tag_04_15)计算给定struct tm
的纪元后的秒数,还有额外的工作要处理1970年之前的几年,如果需要的话:
time_t get_epoch(time_t t0)
{
struct tm gmt = *gmtime(&t0);
struct tm tmp = *localtime(&t0);
long long offset = posix_time(&gmt);
while (offset > INT_MAX) {
offset -= INT_MAX;
tmp.tm_sec -= INT_MAX;
mktime(&tmp);
}
while (offset < -INT_MAX+61) {
offset -= -INT_MAX+61;
tmp.tm_sec -= -INT_MAX+61;
mktime(&tmp);
}
tmp.tm_sec -= offset;
return mktime(&tmp);
}
对于C89的兼容性, long long
必须被丢弃,并且所需的mktime
调用的数量将显着增加; offset
不能作为单个值计算,但每年需要循环多次调用mktime
。