这是一个奇怪的情况。查看this Coliru code:
#include <iostream>
#include <utility>
#include <ctime>
using namespace std;
#define SEGS 60
#define MINS 60
#define HOURS 24
int days(tm* date1, tm* date2)
{ return (mktime(date1) - mktime(date2)) / SEGS / MINS / HOURS; }
tm mkdate(int day, int mon, int year)
{
tm date = {0, 0, 0};
date.tm_mday = day;
date.tm_mon = mon - 1;
date.tm_year = year - 1900;
return date;
}
int main()
{
tm date1 = mkdate(31, 12, 2030);
tm date2 = mkdate(1, 1, 2000);
cout << days(&date1, &date2) << endl;
// 11322... OK 30 * 365 (1/1/2000 - 1/1/2030)
// + 8 (leap years) + 364 (from 1/1/2030 - 31/12/2030).
date1 = mkdate(31, 12, 2030);
date2 = mkdate(1, 1, 1930);
cout << days(&date1, &date2) << endl;
// 36889... OK; but in my machine, it returns 36888.
date1 = mkdate(31, 12, 1943);
date2 = mkdate(1, 1, 1943);
cout << days(&date1, &date2) << endl;
// 364... OK: but in my machine, it returns 363.
date1 = mkdate(30, 6, 1943);
date2 = mkdate(1, 6, 1943);
cout << days(&date1, &date2) << endl;
// 29... OK; but in my machine, it returns 28.
date1 = mkdate(27, 6, 1943);
date2 = mkdate(26, 6, 1943);
cout << days(&date1, &date2) << endl;
// 1... OK; but in my machine, it returns 0.
return 0;
}
每个例子之后的评论都来自Coliru和我的笔记本电脑。 Coliru输出是纠正的,但我的机器打印错误的数字。
如果您阅读了代码,则会正确计算天数之间的差异(第一个示例,从2000年1月1日到2010年12月31日)。
但如果1943年处于日期间隔的中间,似乎有一天会失去。第二个例子:1/1/1930 - 31/12/2030。
经过大量测试,我发现问题出现在Juny / 1943。第三个例子:1943年1月6日 - 1943年3月30日,返回28天而不是29天。
更具体地说,似乎第26天和第27天是同一天。第四个例子:19/6/1943 - 27/6/1943,返回0天。
我的机器是Ubuntu 14.02.2 LTS,Linux 3.13.0-52-generic x86_64,使用gcc(g ++)4.8.2。
问题是什么来的? GNU libc实现的某种bug?
答案 0 :(得分:3)
这是一个时区问题。在 main():
的开头添加它// set the timezone to UTC
setenv("TZ", "", 1);
tzset();
此外,您的 days()函数应如下所示:
double days (tm *date1, tm *date2) {
return difftime (mktime(date1), mktime(date2)) / 86400;
}
根本原因是time_t表示自UTC时期以来的秒数,而mktime()将其参数解释为本地时区中的日期。因此,它必须将其转换为UTC以生成time_t。因此,如果在本地时区的两个日期之间存在相对于UTC的不连续性,则结果可能是错误的。
真正的解决方案不是使用mktime()。它不适用于您的应用程序(除了DST之外还有其他问题,如闰秒)。
您要做的是计算(预感)格里高利历中两个日期之间的天数。您应该自己实现,例如将它们转换为朱利安日数。
答案 1 :(得分:1)
这是不时区问题,即使问题没有显示在UTC上。
此程序存在一些问题,但关键的一个问题是tm_isdst
字段未初始化。此字段会影响mktime
的行为,这会在tm_hour
字段中引入调整。
根据这个特定日期(19/6/1943,27 / 6/1943),时区(欧洲/马德里)和0作为tm_isdst
两个字段的内容,您将获得23小时的差异(82800) s)两个日期之间而不是24小时(86400秒)。然后,在函数days
中,82800被86400除以整数除法。结果为0,休息日完全丢失。
请注意,只需添加:
date.tm_isdst = -1;
到你的mkdate
函数以避免这种情况,通过告诉mktime
在计算自纪元以来的秒数之前检查DST时区的日期。
答案 2 :(得分:0)
在Ubuntu 15.04,32位,我得
edd@don:/tmp$ ./mkdate
11322
-12821
364
29
1
edd@don:/tmp$
在Ubuntu 15.04上,64位,我得
edd@max:/tmp$ ./mkdate
11322
36889
364
29
1
edd@max:/tmp$
在这两种情况下都是g ++ 4.9.2。差异是由于time_t
分别为4和8个字节,因此代码中的日期差异计算会出现溢出。但我不知道你的旧版本中可能存在库错误。