【问题标题】:How to calculate time differences in C++ with time_t before the epoch?如何在纪元之前使用 time_t 计算 C++ 中的时间差?
【发布时间】:2015-10-07 02:18:06
【问题描述】:

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

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 年之前开始的日期获得秒数差异?

【问题讨论】:

  • 你能改进你的标题吗?它没有以任何有用的方式描述您的问题。
  • 计算机上 1900 年之前的日期的实际用例是什么?是某个非常老的人的生日,还是比这更糟…… 格里高利/儒略的转变发生在 20 世纪的俄罗斯。所以编辑你的问题给出背景和动机以及更多解释
  • 您对 1970 年之前的日期或 1900 年之前的日期感兴趣吗?
  • @rwong:好多了。我希望能说服 OP 自己试一试,但是,无论如何,谢谢!

标签: c++ mktime time-t difftime


【解决方案1】:

我建议使用 C++11 std::chrono 命名空间和 <chrono> 标准头文件以及其中的标准函数和类。

您还可以考虑 C 标准中的 difftimelocaltime & mktime

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

顺便说一句,您的问题比您想象的要复杂得多。日历已更改。 Julian/Gregorian 日历转换发生在俄罗斯的 XXth 世纪。我已故的母亲于 1919 年在移民和内战期间出生在一个俄罗斯村庄,当时该村庄的法律制度存在争议(俄罗斯革命并没有立即发生)。她有一些论文提到 1919 年 12 月 13 日,而另一些论文提到了 1919 年 12 月 26 日,指的是两个不同日历中的同一日期。您的软件将如何处理?我什至不确定时区是否足够!

顺便说一句,我不确定 Boost 或 C++11 <chrono> 能否可靠地处理此类日历问题。

nwp 在评论中提到了一个很好的computerphile video: Problem with Time & Timezones

【讨论】:

    【解决方案2】:

    您已经回答了这个问题。 time_t 表示自 UNIX 纪元以来的秒数,而不是自之前某个任意时间以来的秒数。你试图做的事情根本没有意义。

    如果您卡在 C++03 上,无论您对可以使用什么和不可以使用什么的模棱两可的声明,您都必须使用 Boost.DateTime

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

    【讨论】:

    • “我知道还有 Boost 库,但对我来说不是一个可用的解决方案。”
    • OP 表示不能使用 Boost!
    【解决方案3】:

    当您尝试使用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 确实还有其他可能性。它可能是浮点数。它可能是无符号的而不是有符号的。它可能是 64 位类型,这意味着它的范围将近 600,000,000,000 年,顺便说一句,这超出了 32 位 tm_year 的容量。)

    因此,尽管这里有几个反对者告诉你不要这样做,而且虽然在时区、闰秒和公历以外的日历方面肯定有很多晦涩难懂的问题,但你通常可以使用 time_t 来避免对 20 世纪和本世纪直到 2038 年的日期进行基本的日历数学运算,届时臭名昭著的“Y2.038K problem”将会出现。 (我担心它会比不那么臭名昭著的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 在大多数系统上根本无法表示这些日期,因此您将不得不寻求其他解决方案。

    【讨论】:

      猜你喜欢
      • 2016-04-26
      • 2019-12-19
      • 2010-10-28
      • 2014-12-31
      • 1970-01-01
      • 2012-01-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多