如何用ep_之前的time_t计算C ++中的时差?

时间:2015-07-17 11:11:25

标签: c++ mktime time-t difftime

我想用简单程序做的是计算两个日期之间的秒数差异。

time_t referenceDate;
time_t dateNow = time(0);
struct tm referenceDateComponent = {0};
referenceDateComponent.tm_hour = 0;
referenceDateComponent.tm_min = 0;
referenceDateComponent.tm_sec = 0;
referenceDateComponent.tm_year = 89;
referenceDateComponent.tm_mon = 11;
referenceDateComponent.tm_mday = 31;
referenceDate = mktime(&referenceDateComponent);  
long seconds = difftime(dateNow, referenceDate);

使应用程序上方的代码正常工作,但如果尝试设置tm.year为负(在1900之前构建日期),则mktime()函数返回-1

我知道time_t类型根据文档仅管理自1970年1月1日UTC开始的日期:

  

由于历史原因,它通常被实现为表示自UTC时间1970年1月1日00:00(即,unix时间戳)以来经过的秒数的整数值。尽管库可以使用替代时间表示来实现此类型。

我知道还有Boost库,但对我来说不是一个有用的解决方案。

所以我的问题是,从1970年以前的日期开始,有什么办法可以在几秒钟之内得到改变?

3 个答案:

答案 0 :(得分:8)

我建议使用C ++ 11 std::chrono命名空间和<chrono>标准标头以及它们内部的标准函数和类。

您可能还会考虑C标准中的difftimelocaltime&amp; mktime

至少要升级到C++11 还有很多其他理由(如果可以的话,还是C++14)。最近几个好的免费软件编译器GCCClang/LLVM支持该标准(如果你想要GNU扩展和C ++ 14,可以用-std=c++11-std=gnu++14编译)

顺便说一下,你的问题比你想象的要复杂得多。日历已经改变。 Julian / Gregorian日历过渡发生在俄罗斯的XX th 世纪。我已故的母亲出生于1919年,在移民和内战期间,在一个俄罗斯村庄,其法律制度当时有争议(俄罗斯革命没有立即发生)。她有一些文件提到1919年12月13日,其他论文提到1919年12月26日,指的是两个不同日历中的同一天。你的软件将如何处理?我甚至不确定时区是否足够!

BTW,我不确定 Boost 或C ++ 11 <chrono>是否可以可靠地处理此类日历问题。

评论中提到的

nwp非常好computerphile video: Problem with Time & Timezones

答案 1 :(得分:1)

您已经回答了这个问题。 time_t表示自UNIX纪元以来的秒数,而不是自此之前的任意时间以来的秒数。从根本上说你要做的事情毫无意义。

如果您坚持使用C ++ 03,不管您对可以使用和不能使用的内容的含糊不清,都必须使用Boost.DateTime

否则,标准库在<chrono>中有一些很好的现代计时功能。

答案 2 :(得分:1)

当您尝试使用time_t进行日历算法时,您自然不得不担心time_t的类型和表示,这当然是实现定义的。它几乎总是一个有符号的整体类型。在Unix和现代MacOS系统上,它自1970年以来只有几秒钟,而且为了兼容性,我认为它也可以在Windows上以这种方式使用。它往往是32位。总而言之,它通常代表1901年12月13日至2038年1月18日之间的日期。

确实当我将代码中的tm_year行更改为

referenceDateComponent.tm_year = 60;

代码工作并打印1721200226,约为19921天或54。5年,这正是1960年12月31日至今的差异。

但是,如果您将tm_year设为否定,那么您需要在1900年之前申请某个日期,并且使用time_t我们的time_t的典型定义无效#39;一直在讨论。

tm_year还有其它可能性。它可能是浮点数。它可以是无符号而不是有符号。它可以是64位类型,这意味着它是&#39; d有大约600,000,000,000年的范围,偶然超过32位time_t可以容纳。)

所以虽然这里有几个反对者告诉你不要,虽然有很多模糊的困难与时区和闰秒以及Gregorian以外的日历有关,但你通常可以使用{{1}为20世纪和本世纪的日期做基本的日历数学,直到2038年臭名昭着的&#34; Y2.038K problem&#34;会打。 (我担心,它会比不那么臭名昭着的Y2K problem更糟糕,但那是另一个故事。)

正如我所说,你的代码在1970年以前的日期适用于我。以下是我推荐用于基于time_t的简单日期计算的内容(以及已经提到过的警告):

time_t makedate(int year, int month, int day)
{
    struct tm tm = {0};
        tm.tm_hour = 12;
    tm.tm_min = tm.tm_sec = 0;
    tm.tm_year = year - 1900;
    tm.tm_mon = month - 1;
    tm.tm_mday = day;
    tm.tm_isdst = -1;
    return mktime(&tm);
}

int main()
{
    long int d = difftime(makedate(2015, 7, 17), makedate(2015, 6, 1));
    printf("%ld sec = %ld days = %.2f years\n", d, d/86400, d/86400./365.25);

    d = difftime(makedate(2015, 7, 17), makedate(2014, 7, 17));
    printf("%ld sec = %ld days = %.2f years\n", d, d/86400, d/86400./365.25);

    d = difftime(makedate(1975, 7, 17), makedate(1965, 7, 17));
    printf("%ld sec = %ld days = %.2f years\n", d, d/86400, d/86400./365.25);

    d = difftime(makedate(1950, 1, 11), makedate(1925, 1, 1));
    printf("%ld sec = %ld days = %.2f years\n", d, d/86400, d/86400./365.25);

    d = difftime(makedate(2030, 12, 31), makedate(2025, 12, 31));
    printf("%ld sec = %ld days = %.2f years\n", d, d/86400, d/86400./365.25);
}

就像你的代码一样,它利用了令人惊讶的强大mktime函数,可以做它可以做的一切。它处理闰年,没问题。它不处理闰秒或日历更改。

如果按照你说的那样,你对1900年以前的日期感兴趣,我恐怕你运气不好。 time_t根本无法在大多数系统上代表这些日期,因此您将不得不寻求其他解决方案。