两个日期之间的天数 C++

24

我看到了C#和Java的示例,但对于C++,我找不到计算两个日期之间相隔天数的解决方案。

例如:在2012年01月24日和2013年01月08日之间相隔多少天?

谢谢!


3
你目前为止尝试了什么?你使用什么数据类型来存储这个日期?对我来说,它可能只需要简单地使用(date1 - date2).to_days();,甚至可以使用C++11和适当的代码("2012-01-24"_date - "2013-01-08"_date).to_days(); - Geoffroy
3
如果你要将这个算法用于历史数据,请注意,因为过去的时间是出奇地不连续的。例如,在1582/10/5和1582/10/14之间有多少天?答案是:[如果你在西班牙、葡萄牙或意大利,则为1天。] (http://en.wikipedia.org/wiki/Gregorian_calendar#Adoption_in_Europe) - Kevin
5个回答

30

这是一种方式。

#include <iostream>
#include <ctime>

int main()
{
    struct std::tm a = {0,0,0,24,5,104}; /* June 24, 2004 */
    struct std::tm b = {0,0,0,5,6,104}; /* July 5, 2004 */
    std::time_t x = std::mktime(&a);
    std::time_t y = std::mktime(&b);
    if ( x != (std::time_t)(-1) && y != (std::time_t)(-1) )
    {
        double difference = std::difftime(y, x) / (60 * 60 * 24);
        std::cout << std::ctime(&x);
        std::cout << std::ctime(&y);
        std::cout << "difference = " << difference << " days" << std::endl;
    }
    return 0;
}

我的输出

Thu Jun 24 01:00:00 2004
Mon Jul 05 01:00:00 2004
difference = 11 days

这里是原作者帖子的引用链接


有一个帖子的引用,请让我将其加粗。 - sharafjaffri
我看到了这个(否则我就不会知道它),但是很难看到。 - leemes
谢谢您的提醒,我会在下次回复中考虑这个问题。 - sharafjaffri
9
你还应该强调它无法正常工作。如果日期跨越夏令时和冬令时之间的日期,则可能会得到不一致的结果。(使用 12:0:0 作为时间,而不是 0:0:0 )。当然,struct tm 结构体元素的顺序未指定;你需要像这样设置:std::tm a; a.tm_year = 104; a.tm_mon = 5; a.tm_mday = 24; a.tm_hour = 12; - James Kanze
2
这种方法没有考虑夏令时的变化、闰秒或者公历中的缺口。只要你总是考虑每天的午夜并四舍五入到最近的整数天,前两个问题就不会对你造成影响。只要你不使用跨越缺口的日期范围(1582年之后的任何时间都是安全的),第三个问题也不会影响你。 - Edward Brey
如果我计算1979年8月15日至2018年6月15日之间的天数,结果是14182天,但实际上应该是14184天。我会尝试修复这个问题。 - chankruze

20

C++20更新:

#include <chrono>
#include <iostream>

int
main()
{
    using namespace std::chrono;
    using namespace std;
    auto x = 2012y/1/24;
    auto y = 2013y/1/8;
    cout << x << '\n';
    cout << y << '\n';
    cout << "difference = " << sys_days{y} - sys_days{x} << 'n';
}

输出:

2012-01-24
2013-01-08
difference = 350d

如果{年,月,日}数据以int形式存在,则它看起来像这样:
int xy = 2012;
int xm = 1;
int xd = 24;
int yy = 2013;
int ym = 1;
int yd = 8;
auto x = year{xy}/xm/xd;
auto y = year{yy}/ym/yd;
// ...
< p> sys_days {y} - sys_days {x} 的类型是std::chrono::days,它是std::chrono::duration <signed integral type, std::ratio <86'400>>的类型别名。


这是一道旧题的新回答:

使用这个 C++11/C++14 头文件日期库,你现在可以这样写:

#include "date.h"
#include <iostream>

int
main()
{
    using namespace date;
    using namespace std;
    auto x = 2012_y/1/24;
    auto y = 2013_y/1/8;
    cout << x << '\n';
    cout << y << '\n';
    cout << "difference = " << (sys_days{y} - sys_days{x}).count() << " days\n";
}

这将输出:

2012-01-24
2013-01-08
difference = 350 days

如果您不想依赖于这个库,您可以编写自己的库,使用与上述日期库相同的日期算法。这些算法可以在此文献中找到:兼容Chrono的低级日期算法。本示例中正在使用的来自该文献的算法是:
// Returns number of days since civil 1970-01-01.  Negative values indicate
//    days prior to 1970-01-01.
// Preconditions:  y-m-d represents a date in the civil (Gregorian) calendar
//                 m is in [1, 12]
//                 d is in [1, last_day_of_month(y, m)]
//                 y is "approximately" in
//                   [numeric_limits<Int>::min()/366, numeric_limits<Int>::max()/366]
//                 Exact range of validity is:
//                 [civil_from_days(numeric_limits<Int>::min()),
//                  civil_from_days(numeric_limits<Int>::max()-719468)]
template <class Int>
constexpr
Int
days_from_civil(Int y, unsigned m, unsigned d) noexcept
{
    static_assert(std::numeric_limits<unsigned>::digits >= 18,
             "This algorithm has not been ported to a 16 bit unsigned integer");
    static_assert(std::numeric_limits<Int>::digits >= 20,
             "This algorithm has not been ported to a 16 bit signed integer");
    y -= m <= 2;
    const Int era = (y >= 0 ? y : y-399) / 400;
    const unsigned yoe = static_cast<unsigned>(y - era * 400);      // [0, 399]
    const unsigned doy = (153*(m + (m > 2 ? -3 : 9)) + 2)/5 + d-1;  // [0, 365]
    const unsigned doe = yoe * 365 + yoe/4 - yoe/100 + doy;         // [0, 146096]
    return era * 146097 + static_cast<Int>(doe) - 719468;
}

请参阅Chrono-Compatible Low-Level Date Algorithms以了解此算法的工作原理、单元测试以及其有效范围。
该算法模拟普罗利普提克公历,它无限地向前和向后扩展了公历。要模拟其他日历(例如儒略历),您需要其他算法,如此处所示的算法。一旦您设置了其他日历,并将其与相同的序列时代同步(这些算法使用1970年1月1日公历,也是Unix时间时代),您不仅可以轻松计算任意两个日期之间的天数,还可以计算您已模拟的任意两个日历之间的天数。
这使你不必硬编码日期以在朱利安历和公历之间切换,只需知道你的输入数据是参照哪个日历即可获得自由。
有时候,历史文献中的日期可能会存在歧义,此时可以使用旧样式/新样式进行注释,以分别表示朱利安历和公历。
如果你也关心日期的具体时间,这个相同日期库可以与<chrono>库无缝集成,以使用hoursminutessecondsmillisecondsmicrosecondsnanoseconds,并且可以使用system_clock::now()获取当前日期和时间。
如果您关心时区,时区库是在日期库的基础上编写的。它使用IANA时区数据库来处理时区。如果需要,时区库还可以进行包括闰秒在内的计算。请注意,保留HTML标签。

如果日期最初是存储为 std::string 类型的,那么你该如何将其转换成 sys_days() 可以使用的类型? - road_to_quantdom
在SO上搜索“[c++] user:576911 parse”。 - Howard Hinnant

19

将您的日期转换为整数,表示自时代以来的天数,然后进行减法运算。在这个例子中,我选择了Rata Die,该算法的解释可以在 <http://mysite.verizon.net/aesir_research/date/rata.htm> 找到。

int
rdn(int y, int m, int d) { /* Rata Die day one is 0001-01-01 */
    if (m < 3)
        y--, m += 12;
    return 365*y + y/4 - y/100 + y/400 + (153*m - 457)/5 + d - 306;
}

int days = rdn(2013, 1, 8) - rdn(2012, 1, 24);

(153*m - 457)/5 + d - 306 的含义是什么? - immiao
@immiao,该算法将二月份移到年底。 (153 * m - 457) / 5 计算了移动后的月份前面的天数。从公元0年3月1日到12月31日之间有306天。 - chansen

6

0

为了避免编写自己的函数,您可以使用Boost中的date_time


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接