Number of days between two dates C++
Asked Answered
W

5

24

I saw examples for C#, Java, but for C++ i cant find solution to calculate how many days between two dates.

For example between 2012-01-24 and 2013-01-08

Thanks!

Walterwalters answered 8/1, 2013 at 15:53 Comment(2)
What have you tried so far ? What data type are you using to store this date ? From me it could be as simple as (date1 - date2).to_days(); or even, using C++11 and appropriate code ("2012-01-24"_date - "2013-01-08"_date).to_days();Heinrike
If you're going to use this algorithm for historical data, watch out, because the past is surprisingly discontinuous. For example, how many days are there between 1582/10/5 and 1582/10/14? Answer: 1 if you're in Spain, Portugal, or Italy.Bryon
C
31

This is one way.

#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;
}

my output

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

Here is a ref to Original author post

Cubiculum answered 8/1, 2013 at 15:58 Comment(6)
there is a Ref to post, Let me make it boldCubiculum
I saw this (otherwise I wouldn't have known it), but it's hard to see.Bartie
Thanks for intimation I will consider this in my next repliesCubiculum
You should also emphasize that it doesn't work. You may get inconsistent results if the dates span the date where you change between summer and winter time. (Use 12:0:0 for the time, not 0:0:0.) And of course, the order of the elements in the struct tm aren't specified; you need something like std::tm a; a.tm_year = 104; a.tm_mon = 5; a.tm_mday = 24; a.tm_hour = 12;.Guv
This approach doesn't account for daylight savings time shifts, leap seconds, or holes in the Gregorian calendar. As long as you are always considering midnight of each day and round off to the nearest number of days, the first two pitfalls won't hurt you. As long as you aren't using a date range that spans a hole (any time after 1582 is safe), the third pitfall won't get you either.Captivity
If i calculate between August 15, 1979 and June 15, 2018 it gives me 14182 which is 2 days short. The correct number of days are 14184. I'll try to fix this.Cchaddie
T
21

Update for 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';
}

Output:

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

If the {year, month, day} data exists in ints, then it just looks like:

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;
// ...

The type of sys_days{y} - sys_days{x} is std::chrono::days which is a type alias for std::chrono::duration<signed integral type, std::ratio<86'400>>.


New answer for an old question:

Using this C++11/C++14 header-only date library, you can now write:

#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";
}

Which outputs:

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

If you don't want to depend on this library, you can write your own, using the same date algorithms that the above date library uses. They are found in this paper: chrono-Compatible Low-Level Date Algorithms. The algorithm from this paper that is being exercised in this example is this one:

// 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;
}

See chrono-Compatible Low-Level Date Algorithms for details about how this algorithm works, unit tests for it, and its range of validity.

This algorithm models the proleptic Gregorian calendar, which extends the Gregorian calendar indefinitely, both forwards and backwards. To model other calendars (such as the Julian calendar), you will need other algorithms, such as the ones shown here. Once you have other calendars set up, and synchronized to the same serial epoch (these algorithms use 1970-01-01 Gregorian, which is also the Unix time epoch), you can easily compute the number of days not only between any two dates, but also between any two calendars which you have modeled.

This gives you the freedom of not having to hard-code in a date for the switch from Julian to Gregorian. You just have to know which calendar your input data is referenced against.

Sometimes dates in historical documents that might otherwise be ambiguous are annotated with Old Style / New Style to indicate the Julian or Gregorian calendar respectively.

If you are also concerned about the time of day with your dates, this same date library seamlessly integrates with the <chrono> library for use of hours, minutes, seconds, milliseconds, microseconds and nanoseconds, and with system_clock::now() to get the current date and time.

If you are concerned about time zones, an additional (separate) timezone library is written on top of the date library to handle time zones using the IANA timezone database. If needed, the timezone library also has a facility for computations that include leap seconds.

Theobald answered 10/8, 2015 at 19:54 Comment(2)
what if the dates were originally stored as std::string? How would you convert from string to a type that sys_days() can use?Rosemarierosemary
Search SO for "[c++] user:576911 parse".Theobald
W
19

Convert your dates to integer denoting the number of days since an epoch, then subtract. In this example i chosed Rata Die, an explanation of the algorithm can be found at <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);
Wakeup answered 8/1, 2013 at 21:20 Comment(2)
what does (153*m - 457)/5 + d - 306 mean?Novel
@immiao, The algorithm shifts February to the end of the year. (153 * m - 457)/5 computes the number of preceding days of the shifted month. There are 306 days between March 1 of the year zero and December 31.Wakeup
O
6

you can try the boost date_time library

Obloquy answered 8/1, 2013 at 15:55 Comment(0)
C
0

To avoid making your own function you can use date_time from Boost.

Chercherbourg answered 8/1, 2013 at 21:53 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.