gmtime() 函数是不是考虑闰年?
Posted
技术标签:
【中文标题】gmtime() 函数是不是考虑闰年?【英文标题】:Does gmtime() function take into account leap years?gmtime() 函数是否考虑闰年? 【发布时间】:2017-08-15 07:10:12 【问题描述】:我正在研究嵌入式 linux,自 1970 年 1 月 1 日以来,系统/控制器时间以毫秒为单位。我正在尝试使用 gmtime 但无法获得准确的结果。任何将此时间(毫秒)转换为实时时:分:秒:日:月的示例都会有所帮助。
【问题讨论】:
当然应该。你说你得到的不准确结果的一个例子,以及产生它们的minimal reproducible example,会很有帮助——事实上,如果你想回答你的问题,这是必不可少的。 (你知道tm_mday
是从1开始的,tm_mon
是从0开始的,tm_year
是从1900开始的,对吧?)
【参考方案1】:
您可以使用 civil_from_days
,它是在 Howard Hinnant 的 chrono-Compatible Low-Level Date Algorithms 中派生和详细解释的。此函数计算自 1970 年 1 月 1 日以来的天数,并将其转换为 y, m, d
字段。完成此操作后,您只需处理从毫秒时间戳中减去天数以获得自午夜以来的毫秒数,然后将其分解为h:M:s.ms
。
完整代码如下:
#include <iostream>
#include <iomanip>
#include <cstdint>
int
main()
using namespace std;
int64_t t = 1490285505123; // milliseconds since epoch
int32_t z = (t >= 0 ? t : t - (1000*86400-1))/(1000*86400); // days since epoch
t -= z * (1000LL * 86400); // milliseconds since midnight
z += 719468;
int32_t era = (z >= 0 ? z : z - 146096) / 146097;
int32_t doe = z - era * 146097;
int32_t yoe = (doe - doe/1460 + doe/36524 - doe/146096) / 365;
int32_t y = yoe + era * 400;
int32_t doy = doe - (365*yoe + yoe/4 - yoe/100);
int32_t m = (5*doy + 2)/153;
int32_t d = doy - (153*m + 2)/5 + 1; // day
m += m < 10 ? 3 : -9; // month
y += m <= 2; // year
int32_t h = t / (1000 * 3600); // hour
t -= h * (1000 * 3600);
int32_t M = t / (1000 * 60); // minute
t -= M * (1000 * 60);
int32_t s = t / 1000; // second
int32_t ms = t - s * 1000; // ms
cout.fill('0');
cout << setw(4) << y << '-' << setw(2) << m << '-' << setw(2) << d
<< ' ' << setw(2) << h << ':' << setw(2) << M
<< ':' << setw(2) << s << '.' << setw(3) << ms << '\n';
作为一个例子,我用1490285505123ms
作为输入,输出是:
2017-03-23 16:11:45.123
这需要考虑闰年。它不考虑闰秒。您的嵌入式 linux 系统/控制器不太可能这样做,因此尝试这样做是不正确的。
上述算法对t
的有效范围非常大:
-5877641-06-23 00:00:00.000 <= t <= 5880010-09-09 23:59:59.999
(+/- 580 万年)
如果您不介意将t
的下限限制为0000-03-01 00:00:00.000
,那么您可以将era
的计算简化为:
int32_t era = z / 146097;
如果可以将t
的下限限制为1970-01-01 00:00:00.000
,那么z
的计算可以简化为:
int32_t z = t / (1000 * 86400); // days since epoch
最后,如果您愿意将t
限制在这 400 年的范围内:
2000-03-01 00:00:00.000 <= t <= 2400-02-29 23:59:59.999
那么era
可以变成简单的:
int32_t const era = 5;
Fwiw,这是一个利用 C++11/14 <chrono>
库的high-level date/time library,它执行完全相同的计算,只是语法更简洁。您的 std::lib 必须支持 <chrono>
才能使用此库:
#include "date.h"
#include <iostream>
int
main()
using namespace date;
using namespace std;
using namespace std::chrono;
cout << sys_time<milliseconds>(1490285505123ms) << '\n';
【讨论】:
【参考方案2】:gmtime()
不仅支持闰年,它还占闰秒,这就是为什么 tm_sec
字段的范围定义为 [0..60] (含)。
【讨论】:
根据C
标准,gmtime()
不需要考虑闰秒,但允许这样做。此特性未指定。根据 POSIX,gmtime()
不 考虑闰秒,因为 time_t
从 1970-01-01T00:00:00 UTC 开始不将它们包括在其计数中。随后,gmtime()
的实现很少(如果有的话)会考虑闰秒(我不知道)。闰年,是的。闰秒,不。尽管您对tm_sec
的范围是正确的。在这方面,struct tm
可以代表闰秒,而 time_t
不能。
@HowardHinnant time_t
在 Linux 和 FreeBSD(可能还有其他)上是否包括闰秒是可配置的。当它包含闰秒时,它们的gmtime()
可以返回tm_sec
值60
。见coliru.stacked-crooked.com/a/622da23fd57dabca
@hvd:很好的演示 putenv("TZ=right/UTC")
,谢谢。以上是关于gmtime() 函数是不是考虑闰年?的主要内容,如果未能解决你的问题,请参考以下文章