Reputation: 2007
I'm doing a lot of calculations with times, building time objects relative to other time objects by adding seconds. The code is supposed to run on embedded devices and servers. Most documentations say about time_t
that it's some arithmetic type, storing usually the time since the epoch. How safe is it to assume that time_t
store a number of seconds since something? If we can assume that, then we can just use addition and subtraction rather than localtime
, mktime
and difftime
.
So far I've solved the problem by using a constexpr bool time_tUsesSeconds
, denoting whether it is safe to assume that time_t
uses seconds. If it's non-portable to assume time_t
is in seconds, is there a way to initialize that constant automatically?
time_t timeByAddingSeconds(time_t theTime, int timeIntervalSeconds) {
if (Time_tUsesSeconds){
return theTime + timeIntervalSeconds;
} else {
tm timeComponents = *localtime(&theTime);
timeComponents.tm_sec += timeIntervalSeconds;
return mktime(&timeComponents);
}
}
Upvotes: 15
Views: 27570
Reputation: 29209
If C++11 is available, you can use std::chrono::system_clock
's to_time_t
and from_time_t
to convert to/from std::chrono::time_point
, and use chrono's arithmetic operators.
If your calculations involve the Gregorian calendar, you can use the HowardHinnant/date library, or C++20's new calendar facilities in chrono (they have essentially the same API).
Upvotes: 4
Reputation: 3230
The fact that it is in seconds is stated by the POSIX specification, so, if you're coding for POSIX-compliant environments, you can rely on that.
The C++ standard also states that time_t
must be an arithmetic type.
Anyway, the Unix timing system (second since the Epoch) is going to overflow in 2038. So, it's very likely that, before this date, C++ implementations will switch to other non-int data types (either a 64-bit int or a more complex datatype). Anyway, switching to a 64-bit int would break binary compatibility with previous code (since it requires bigger variables), and everything should be recompiled. Using 32-bit opaque handles would not break binary compatibility, you can change the underlying library, and everything still works, but time_t
would not a time in seconds anymore, it'd be an index for an array of times in seconds. For this reason, it's suggested that you use the functions you mentioned to manipulate time_t
values, and do not assume anything on time_t
.
Upvotes: 17
Reputation: 2007
(Answering own question)
One answer suggests that as long as one is using posix, time_t
is in seconds and arithmetic on time_t
should work.
A second answer calculates the time_t per second, and uses that as a factor when doing arithmetic. But there are still some assumptions about time_t
made.
In the end I decided portability is more important, I don't want my code to fail silently on some embedded device. So I used a third way. It involves storing an integer denoting the time since the program starts. I.e. I define
const static time_t time0 = time(nullptr);
static tm time0Components = *localtime(&time0);
All time values used throughout the program are just integers, denoting the time difference in seconds since time0
. To go from time_t
to this delta seconds, I use difftime
. To go back to time_t
, I use something like this:
time_t getTime_t(int timeDeltaSeconds) {
tm components = time0Components;
components.tm_sec += timeDeltaSeconds;
return mktime(&components);
}
This approach allows making operations like +
,-
cheap, but going back to time_t
is expensive. Note that the time delta values are only meaningful for the current run of the program. Note also that time0Components has to be updated when there's a time zone change.
Upvotes: 0
Reputation: 25908
Rather than determine whether time_t
is in seconds, since time_t
is an arithmetic type, you can instead calculate a time_t
value that represents one second, and work with that. This answer I wrote before explains the method and has some caveats, here's some example code (bad_time()
is a custom exception class, here):
time_t get_sec_diff() {
std::tm datum_day;
datum_day.tm_sec = 0;
datum_day.tm_min = 0;
datum_day.tm_hour = 12;
datum_day.tm_mday = 2;
datum_day.tm_mon = 0;
datum_day.tm_year = 30;
datum_day.tm_isdst = -1;
const time_t datum_time = mktime(&datum_day);
if ( datum_time == -1 ) {
throw bad_time();
}
datum_day.tm_sec += 1;
const time_t next_sec_time = mktime(&datum_day);
if ( next_sec_time == -1 ) {
throw bad_time();
}
return (next_sec_time - datum_time);
}
You can call the function once and store the value in a const, and then just use it whenever you need a time_t
second. I don't think it'll work in a constexpr
though.
Upvotes: 2
Reputation: 76245
There is no requirement in standard C or in standard C++ for the units that time_t
represents. To work with seconds portably you need to use struct tm
. You can convert between time_t
and struct tm
with mktime
and localtime
.
Upvotes: 2
Reputation: 3264
My two cents: on Windows it is in seconds over time but the time it takes for one second to increment to the next is usually 18*54.925 ms and sometimes 19*54.925. The reason for this is explained in this post.
Upvotes: 0