3

I have a function called int differenceDatesInDays(string& date). This function should get a string value as a date (YYYY-MM-DD) and compare it with today´s date.

I don´t know if there is already something inside the STL, i could not find a matching algorithm. I just found out that boost has a solution for this, but i don´t want to use boost.

So, this is my code so far:

int differenceDatesInDays(string& date) {
    string year = date.substr(0, 4);
    string month = date.substr(5,2);
    string day = date.substr(8, string::npos);

    int y = stoi(year);
    int m = stoi(month);
    int d = stoi(day);

    time_t time_now = time(0);
    tm* now = localtime(&time_now);

    int diffY = y - (now->tm_year + 1900);
    int diffM = m - (now->tm_mon + 1);
    int diffD = d - (now->tm_mday);

    int difference = (diffY * 365) + (diffM * 30) + diffD;

    return difference;
}

I don´t know how to find out if the month has 30, 31 or 28 days.

user3653164
  • 979
  • 1
  • 8
  • 24
  • There are only 12 months so you can set up a const array that holds each month's days. Index it by the month. For February, which normally has 28 days, you will have to determine if it's a leap year, in which case it has 29 days. – Anon Mail Nov 14 '15 at 19:59

3 Answers3

7

Once you have two integral triples: {y1, m1, d1} and {y0, m0, d0}, the most efficient way to compute the difference between them is to use the public domain function days_from_civil to compute a serial day count of each triple, and subtract them:

diff = days_from_civil(y1, m1, d1) - days_from_civil(y0, m0, d0);

Here is days_from_civil repeated:

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

This will have a far greater range of validity than your typical tm-based code. And it will be faster. And you aren't forced to deal with time of day. And if all your information is compile-time constants, and you're in C++14, you can get your answer at compile-time.

Howard Hinnant
  • 206,506
  • 52
  • 449
  • 577
  • Hahaha. Completely trivial stuff :) I love it. I will use this the day I can't use a "standard" library implementation of this. – sehe Nov 14 '15 at 21:54
  • Howard, you must be a complete C++ addict -- spending saturday night to give such answers here. -- I better shut my terminal now ... Btw, I'm slowly catching up with you on [this one](http://stackoverflow.com/questions/5008804/generating-random-integer-from-a-range/19728404#19728404) – Walter Nov 14 '15 at 21:58
  • @Walter He's been writing date/time manipulation libraries for the standard library and elsewhere. He linked to the "library" that he wrote this for initially:) You can probably trust this code for that reason! – sehe Nov 14 '15 at 22:01
  • @sehe I knew, but he still is addicted (as are you ;) – Walter Nov 14 '15 at 22:02
  • Btw, astronomers still use the Julian calender. What can you offer there? – Walter Nov 14 '15 at 22:03
  • 1
    Re: trivial stuff: It is much less trivial to map the other way: From a serial count of days to a y/m/d triple. Many implementations will have iteration in their solution. But not `civil_from_days`. – Howard Hinnant Nov 14 '15 at 22:24
  • Re the Julian calendar: http://howardhinnant.github.io/date_algorithms.html#Example:%20Converting%20between%20the%20civil%20calendar%20and%20the%20Julian%20calendar – Howard Hinnant Nov 14 '15 at 22:25
  • Btw, within one era, the 13th day of each month is more often a Friday than other weekday – Walter Nov 15 '15 at 16:36
  • @Walter: Nice little factoid. I wrote a program using http://howardhinnant.github.io/date_v2.html to prove it in 14 lines. – Howard Hinnant Nov 15 '15 at 17:52
4

Since you tagged it with Boost, why don't you use it and get all the stuff for free:

Live On Coliru

#include <boost/date_time/gregorian/gregorian.hpp>

int differenceDatesInDays(std::string const& s) {
    using namespace boost::gregorian;
    return (day_clock::local_day() - from_string(s)).days();
}

int main() {
    return differenceDatesInDays("2015-01-01");
}

Prints

317
sehe
  • 374,641
  • 47
  • 450
  • 633
  • This is much more useful than the accepted answer; pity the OP didn't want to use boost (despite the tag). – Walter Nov 14 '15 at 21:52
2

Something along these lines, perhaps:

int differenceDatesInDays(string& date) {
    // Parse `date` as in your code
    int y = ...;
    int m = ...;
    int d = ...;

    tm then = {0};
    then.tm_year = y - 1900;
    then.tm_mon = m - 1;
    then.tm_day = d;
    time_t then_secs = mktime(&then);

    time_t time_now = time(0);
    tm* now = localtime(&time_now);
    tm today = {0};
    today.tm_year = now->tm_year;
    today.tm_mon = now->tm_mon;
    today.tm_day = now->tm_day;
    time_t today_secs = mktime(&today);

    return (today_secs - then_secs) / (24*60*60);
}
Igor Tandetnik
  • 50,461
  • 4
  • 56
  • 85