如何比较两个tm(来自ctime)变量

How to compare two tm (from ctime) variables

本文关键字:tm 来自 ctime 变量 两个 何比较 比较      更新时间:2023-10-16

我刚刚开始学习C++。

我使用的是带有Visual Studio版本的Windows 7 Ultimate x64:

Microsoft Visual Studio Enterprise 2017 
Version 15.9.12
VisualStudio.15.Release/15.9.12+28307.665
Installed Version: Enterprise
Visual C++ 2017   00369-90013-89248-AA631
Microsoft Visual C++ 2017
Visual Studio Tools for CMake   1.0
Visual Studio Tools for CMake

我试着比较这两个日期:

1582 10月15日
2009年6月19日18:00

因为我想知道日期是否晚于1582年10月15日。

要做到这一点,我有一段代码:

#include <ctime>
tm date = { 0, 0, 18, 19, 5, 109, 0, 0, 0 };
tm gregorian = { 0, 0, 0, 15, 9, (1582 - 1900), 0, 0, 0};
double aux = std::difftime(std::mktime(&date), std::mktime(&gregorian));

但是aux等于0.0。我认为它一定不同于零。

我是做错了什么还是结果是正确的?

一旦使用较旧的日期,就应该小心使用日期和时间函数较老的可能在1970-01-01之前的一天就已经开始了。如果你回到1582-10-15年,也就是公历的生效日期,那么就需要格外小心。

怎么了

在代码中,mktime()将一个日期转换为time_t,然后用它来区分两个日期。根据C标准:

clock_t和CCD_ 6是实现定义的。

通常,time_t以自1970-01-01以来经过的秒数表示。这需要知道(或假设(日期和时间的时区(默认为0:00:00(,并且不仅要对闰年,还要对闰秒有一个完美的了解。

如果日期无效或无法在实现支持的time_t范围内转换,mktime()将返回错误代码-1。因此,一个更安全的代码版本会检查潜在的错误:

std::time_t th = std::mktime(&date); 
std::time_t tl = std::mktime(&gregorian); 
if (th==-1 || tl==-1) {
std::cout<<"At least one of the date couldn't be converted"<<std::endl;
}
else {
double aux = std::difftime(th, tl);
std::cout << aux<< " "<< aux/3600.0/24.0/365.25 <<std::endl; 
}

在线演示,并提供了一个运行良好的实现。对于MSVC,转换在1970-01-01之前的任何日期都失败。这里有记录。

有更好的解决方案吗

现在,如果你回到1582-10-15年,那么将其转换为比较两个旧日期的精确秒数就没有那么大意义了
因此,您可能需要考虑boost::gregorian::date。这个库可以处理从1400-01年1月到9999-31年12月的日期。它不是转换为秒,而是将日期转换为天数。

然后,您可以通过获得天数差异来可靠地比较1582-10-15和1582-10-14等日期:

using namespace boost::gregorian;
date d0(1582, 10, 15);
date d1(1582, 10, 25); 
date d2(1582, 10, 14); 
std::cout << d1-d0 << std::endl;
std::cout << d2-d0 << std::endl;

为什么旧日如此复杂

有趣的是,在上面的例子中,你会看到BOOST会进行理论计算。它将在1582-10-14和1582-10-15之间找到一天的差异(演示(。

当然,我们都知道这是荒谬的,因为格里高利历的引入导致1582年10月4日紧接着是1582年的10月15日。

另请注意,根据国家的不同,其他日期也存在这种日历不一致的情况。例如,根据维基百科的数据,1752年9月2日星期三是1752年九月14日星期四。

最后,如果你比较公历之前的日期,你需要接受歧义。以1492年10月12日为例,哥伦布在美洲踏上了一只脚。事实上,这是儒略历中的一个日期。在公历中,它实际上是1492年10月21日。

有更简单的选择吗

现在,time_t有点过时了。C++标准是指定义相关函数的C标准。

现代C++提供了std::chronostd::chrono::time_point类型。问题是,目前还没有可用的函数可以转换到日历和从日历转换:这些函数只能与C++20一起提供。

但是,如果你只想比较两个旧日期,假设没有日历不一致,你可以只比较年份,如果相等,则比较月份,如果相等则比较日期。我知道这听起来很荒谬,但无论你的C++版本和实现是什么,这都会毫无问题地工作