5

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

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);

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

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

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

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

所以我的问题是,有什么方法可以从 1970 年之前开始的日期获得秒数差异?

4

3 回答 3

8

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

您还可以考虑来自 C 标准和localtime &的difftimemktime

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

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

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

nwp在评论中提到了一个非常棒的电脑发烧友视频:Time & Timezones 问题

于 2015-07-17T11:15:53.490 回答
1

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

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

否则,标准库在<chrono>.

于 2015-07-17T11:13:41.377 回答
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。它可能是浮点数。它可能是无符号的而不是有符号的。它可能是 64 位类型,这意味着它的范围将近 600,000,000,000 年,顺便说一下,它不止一个32 位tm_year可以容纳。)

因此,尽管这里有几个反对者告诉你不要这样做,虽然在时区、闰秒和公历以外的日历方面肯定有很多晦涩难懂的问题,但你通常可以time_t不用为日期做基本的日历数学在 20 世纪和本世纪直到 2038 年,臭名昭著的“ Y2.038K 问题”即将到来。(我担心它会比不那么臭名昭著的千年虫问题更糟,但那是另一回事了。)

正如我所说,您的代码适用于 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根本无法在大多数系统上表示这些日期,因此您将不得不寻求其他解决方案。

于 2015-07-17T13:14:15.440 回答