2

I am new to c++, so I am wondering whether there is some library which could help deal with dates more fluently.

I have a fairly plain task. I have a starting date in different values and I have to get what date it will be when I increase the date by a random number of days.

I figured mktime and time_t objects seams to be helpful with what I am trying to do. If they are the answer could someone give me a link to a good guide?

masoud
  • 55,379
  • 16
  • 141
  • 208
Povylas
  • 776
  • 4
  • 16
  • 31

7 Answers7

3

A new answer for a decade-old question because times have changed and tools have gotten better.

In C++20, a date can be represented in several different succinct and useful ways.

  • std::chrono::sys_days
  • std::chrono::year_month_day
  • std::chrono::year_month_weekday

(this is not an exhaustive list)

Each data structure above represents a date, but has advantages and disadvantages, much like we have several container types that represent a sequence of values (vector, list, deque, etc.) with advantages and disadvantages.

For adding days to a date, sys_days is the clear best choice for a date representation. sys_days is nothing but a count of days since (or before) 1970-01-01. It is a type alias for:

time_point<system_clock, days>

where days is a std::chrono::duration:

duration<signed integer type of at least 25 bits, ratio_multiply<ratio<24>, hours::period>>

So adding days to sys_days is simply integral arithmetic under the hood.

And C++20 allows seamless conversion between {year, month, day} concepts and sys_days. So this looks like:

sys_days tp = sys_days{January/30/2022} + days{400};  // tp = 2023-03-06

Integrals can be used as inputs to the above formula. However when working with <chrono> it is best to try and stay within the strong type system of <chrono>.:

int y = 2022;
int m = 1;
int d = 30;
int num_days = 400;
sys_days tp = sys_days{month(m)/d/y} + days{num_days};  // tp = 2023-03-06

In any event, tp can be easily observed by just printing it out:

cout << tp << '\n';

Output:

2023-03-06

Other formatting options are available, and programmatic access to the values for the year, month and day are also available. It is best to keep these values within the chrono strong types year, month and day, but conversions to integral types are also available.

Howard Hinnant
  • 206,506
  • 52
  • 449
  • 577
3
masoud
  • 55,379
  • 16
  • 141
  • 208
2

A day is usually 86400 seconds (except for leap seconds) . You can add that to a time_t and get a new time_t etc. Then you can use mktime & localtime to convert it to struct tm which is displayable with strftime and could be parsable with strptime

Basile Starynkevitch
  • 223,805
  • 18
  • 296
  • 547
  • 1
    Adding to a `time_t` directly is unfortunately not a portable solution, as different libraries/platforms might use different representations for `time_t` (see https://www.cplusplus.com/reference/ctime/time_t/ ). – André Offringa Jan 30 '22 at 19:05
1

I just wrote my own function to add Days, Months and Years to an existing DATE class. I couldn't test it yet, but maybe it helps:

bool DATE::add(int Day, int Month, int Year){
int DaysPerMonth[13] = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
this -> Day += Day;
while(this -> Day > DaysPerMonth[ this-> Month ]){
    if((this -> Year % 4 == 0 && this -> Year % 100 != 0) || this -> Year % 400 == 0){
        DaysPerMonth[2] = 29; 
    }
    this -> Day -= DaysPerMonth[ this-> Month ];
    this -> Month++;
    if(this -> Month > 12){
        this -> Month = 1;
        this -> Year++;
    } 
}
this -> Month   = ( this -> Month + (Month % 12));
this -> Year    = ( this -> Year + Year + (Month/12));
if((this -> Year % 4 == 0 && this -> Year % 100 != 0) || this -> Year % 400 == 0){
    DaysPerMonth[2] = 29;   
    // check pathologic case wether date is 1 of March and added Year targets switchyear 
    if( this -> Day == 1 && this -> Month == 3){            
        this -> Day = 29;
        this -> Month = 2;
    }
}
if(this -> Month < 1 || this -> Month > 12 || this -> Day < 1 || this -> Day > DaysPerMonth[this->Month]){  
    valid = false;
    cerr << "something went wrong, calculated Date is: " << this -> Day << "."<< this -> Month << "." << this -> Year << endl << flush;
    return false;
}else{
    return true;
}

}

Karl Adler
  • 15,780
  • 10
  • 70
  • 88
1

Well, there is either the Boost Date and time module. Of if your compiler is new enough there is the C++11 chrono namespace.

Some programmer dude
  • 400,186
  • 35
  • 402
  • 621
0

A portable method using the standard ctime header, is to construct an mt struct using localtime() (or gmtime() to use UTC time). Then, add the number of days to the tm_day member and convert the structure back to a time using mktime().

The tm_day member represents the day of the month, but mktime allows it to exceed the number of days in a month, and even allows it to be negative in case it is necessary to subtract days from the date, so you do not have to guard for month-wrapping.

An example:

#include <ctime>

time_t add_days(const time_t& time_value, int days) {
  tm tm_value = *localtime(&time_value);
  tm_value.tm_mday += days;
  return mktime(&tm_value);
}

A downside of this method can be that the std libraries might not implement localtime() in a thread-safe way. This can be fixed by using localtime_r() instead, but that is less portable.

André Offringa
  • 362
  • 2
  • 7
0

Beware I just discovered that

std::chrono::system_clock::time_point event;
event = event + days{1}; // in my implementation: using days = duration<_GLIBCXX_CHRONO_INT64_T, ratio<86400>>;

is not the same as

std::chrono::system_clock::time_point event;
event = event + 1d; // https://en.cppreference.com/w/cpp/chrono/operator%22%22d

The later will cause an error: no match for ‘operator+’

Again time, calendars, time zones are complicated and I'd guess that most of the times a calendar day is 24h but not all the times.

If I just had to do arithmetic with a date I'd probably use a year_month_day.

I'm still exploring how to do things being at least aware of corner cases when dealing with dates, time and time zones.

  • 3
    `1d` is a literal for `day{1}`. Note it is singular. `day` is not a duration, but a calendrical specifier for a day of the month. In contrast `days{1}` (plural) is a duration of length 1 day (24 hours, or 86400 seconds). Here is a more detailed discussion on this issue but using `month` and `months` to explain it: https://stackoverflow.com/q/62249520/576911 – Howard Hinnant Jul 12 '23 at 02:19