这是一个奇怪的情况。查看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日到2030年12月31日)。

但是,如果1943年处于日期间隔的中间,则似乎失去了一天。第二个例子:1/19/1930-20/12/31。

经过大量测试,我发现问题出在Juny / 1943。第三个示例:1/6/1943-30/6/1943,返回28天而不是29天。

更确切地说,似乎26日和27日是同一天。第四个示例:26/6/1943-27/6/1943,返回0天。

我的机器是使用gcc(g++)4.8.2,Linux 3.13.0-52通用x86_64的Ubuntu 14.02.2 LTS。

问题出在哪里? GNU libc实现的某种错误?

最佳答案

这是一个时区问题。在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之外,还有其他问题,例如leap秒)。

您要尝试做的是计算(丰满的)公历中两个日期之间的天数。您应该自己实现此目的,例如,将它们转换为儒略日数。

关于c++ - C++和mktime:26/6/1943和27/6/1943是同一天,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/30191700/

10-12 02:57