2

I have a function that needs to calculate yesterday's, today's and tomorrow's date. I use localtime to get today's date. I add 1 to tm_mday in order to get tomorrow's date. The issue is if the current date is 3/31, it will become 3/32 if I add 1 to tm_mday. Is there any date package in C++ that handles carry over into next month or will I need to write logic to do this?

string get_local_date(const string &s) {
    time_t rawtime;
    struct tm * timeinfo;
    time (&rawtime);
    timeinfo = localtime(&rawtime);

    if (s == "tomorrow") {
        timeinfo->tm_mday += 3; // day becomes 3/32
    }
    if (s == "yesterday") {
        timeinfo->tm_mday -= 1;
    }

    char buffer[80];
    strftime(buffer,80,"%04Y-%m-%d",timeinfo);
    string str(buffer);

    return str; 
}
Tyler
  • 63
  • 2
  • 9
  • 1
    It's not hard logic to write yourself. The only tricky part is February because of leap years, and that's not hard either. – Mark Ransom Mar 29 '23 at 21:01
  • There are very, very few "make it so" buttons in C++ that only need to be located, and once pushed they do everything that a given task needs, and this isn't one of them. In all other cases it becomes necessary to implement the appropriate logic, all by yourself. Did you try simply writing down, in plain words, basic date math operations (next day, previous day)? Because once this is done, translating what's written down into C++ code should be fairly easy. – Sam Varshavchik Mar 29 '23 at 21:03
  • I know it's not hard but I'm just trying to see if there's anything out there that can do that. – Tyler Mar 29 '23 at 21:10
  • Why are you incrementing the day by `+3` for tomorrow? – Remy Lebeau Mar 29 '23 at 21:43

3 Answers3

6

On most systems, (std::)time_t is expressed as the integral seconds that have elapsed since the POSIX epoch (Jan 1 1970 00:00 UTC). As there are 86400 seconds in 1 day (not couning leap seconds), you can simply add 86400 for tomorrow, and subtract 86400 for yesterday, eg:

#include <string>
#include <ctime>

std::string get_local_date(const std::string &s) {

    std::time_t rawtime = std::time(nullptr);

    if (s == "tomorrow") {
        rawtime += 86400;
    }
    else if (s == "yesterday") {
        rawtime -= 86400;
    }

    char buffer[80] = {};
    std::strftime(buffer, 80, "%04Y-%m-%d", std::localtime(&rawtime));

    return buffer;
}

However, you really should use the native <chrono> library, which was introduced in C++11, to handle this kind of stuff, eg:

#include <string>
#include <chrono>
#include <ctime>

std::string get_local_date(const std::string &s) {

    using namespace std::literals::chrono_literals;

    auto clock = std::chrono::system_clock::now();

    if (s == "tomorrow") {
        clock += 24h; // clock += std::chrono::hours(24);
        /* alternatively, in C++20 and later:
        clock += std::chrono::days(1);
        */
    }
    else if (s == "yesterday") {
        clock -= 24h; // clock -= std::chrono::hours(24);
        /* alternatively, in C++20 and later:
        clock -= std::chrono::days(1);
        */
    }

    auto rawtime = std::chrono::system_clock::to_time_t(clock);

    char buffer[80] = {};
    std::strftime(buffer, 80, "%04Y-%m-%d", std::localtime(&rawtime));

    /* alternatively, in C++20 and later:
    return std::format("{:%F}", clock);
    */
}
Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
3

Here is a C++20 version that gets the local date for today, as opposed to the UTC date, which can be off by 1 day depending on your local time zone, and the time at which the program is run:

#include <chrono>
#include <format>
#include <iostream>
#include <string>

std::string
get_local_date(std::string const& s)
{
    using namespace std;
    using namespace chrono;

    zoned_time zt_now{current_zone(), system_clock::now()};
    auto date = floor<days>(zt_now.get_local_time());
    if (s == "tomorrow")
        ++date;
    else if (s == "yesterday")
        --date;
    else if (s != "today")
        throw "oops";
    return format("{:%F}", date);
}

int
main()
{
    std::cout << get_local_date("yesterday") << '\n';
    std::cout << get_local_date("today") << '\n';
    std::cout << get_local_date("tomorrow") << '\n';
}

This can easily be customized to another time zone which may output slightly different results by changing one line. For example:

   zoned_time zt_now{"Asia/Tokyo", system_clock::now()};

Demo.

Howard Hinnant
  • 206,506
  • 52
  • 449
  • 577
2

Even with plain old C, this is actually a pretty easy thing to manage, because mktime normalizes the date contained in a struct tm. For example:

#include <time.h>
#include <iostream>
#include <iomanip>

int main() { 
    time_t tnow = time(nullptr);    
    tm now = *localtime(&tnow);

    std::cout << std::put_time(&now, "%F\n"); // today
    now.tm_mday += 3;   // currently 30 March, so this will be next month
    mktime(&now);
    std::cout << std::put_time(&now, "%F\n");

    now.tm_mday -= 7;   // as I write this, tm_mday is now negative
    mktime(&now);
    std::cout << std::put_time(&now, "%F\n");

    now.tm_mday += 30;  // let's try a month ahead
    mktime(&now);
    std::cout << std::put_time(&now, "%F\n");

    now.tm_mday += 300; // How about nearly a year ahead?
    mktime(&now);
    std::cout << std::put_time(&now, "%F\n");
}

Result:

2023-03-30
2023-04-02
2023-03-26
2023-04-25
2024-02-19

So yes, all of those work just fine.

Now don't get me wrong. I'd certainly recommend using the C++ 20 date library or Howard Hinnant's date library (which is pretty much what got standardized into C++20) if you can--it's a great library and works really well. But if (for whatever reason) that's not easily available, you don't have to fall back to writing it all yourself. The date manipulation you need is almost as easy, even using only the C library.

Jerry Coffin
  • 476,176
  • 80
  • 629
  • 1,111