问题
What I would like to do with my simple program is to calculate a difference in seconds between two dates.
time_t referenceDate;
time_t dateNow = time(0);
struct tm referenceDateComponent = {0};
referenceDateComponent.tm_hour = 0;
referenceDateComponent.tm_min = 0;
referenceDateComponent.tm_sec = 0;
referenceDateComponent.tm_year = 89;
referenceDateComponent.tm_mon = 11;
referenceDateComponent.tm_mday = 31;
referenceDate = mktime(&referenceDateComponent);
long seconds = difftime(dateNow, referenceDate);
Whit the code above the application works fine, but if try to set tm.year
negative (to build a date before 1900) the mktime()
function return -1
I know that time_t
type manage only dates starting from Jan 1, 1970 UTC according with the documentation:
For historical reasons, it is generally implemented as an integral value representing the number of seconds elapsed since 00:00 hours, Jan 1, 1970 UTC (i.e., a unix timestamp). Although libraries may implement this type using alternative time representations.
I know there are also the Boost
libraries but is not a usable solution for me.
So my question would be, there is any way to get difference in seconds from dates starting before 1970?
回答1:
I recommend using the C++11 std::chrono
namespace and <chrono> standard headers and the standard functions and classes inside them.
You might also consider difftime from the C standard and localtime & mktime
And there are a lot of good other reasons to upgrade to C++11 at least (or C++14 if you can). Several good recent free software compilers GCC and Clang/LLVM support that standard (compile with -std=c++11
or -std=gnu++14
if you want GNU extensions & C++14)
BTW, your question is much more complex than you believe. Calendars has changed. Julian/Gregorian calendar transition happened in the XXth century in Russia. My late mother was born in 1919, during emigration and the civil war, in a Russian village whose legal system was disputed at that time (Russian revolution did not occur instantly). She had some papers mentioning 13th december 1919, and other papers mentioning 26th december 1919, referring to the same date in two different calendars. How would your software deal with that? I'm not even sure that timezone is enough!
BTW, I'm not sure that Boost or C++11 <chrono>
can reliably deal with such calendar issues.
nwp mentioned in a comment a very good computerphile video: Problem with Time & Timezones.
回答2:
You've already answered this question. time_t
represents a number of seconds since the UNIX epoch, not number of seconds since some arbitrary time before that. What you are trying to do fundamentally makes no sense.
If you're stuck on C++03, regardless of your ambiguous claims about what you can and cannot use, you will have to use Boost.DateTime.
Otherwise, the standard library has some nice modern timekeeping features in <chrono>
.
回答3:
When you're trying to do calendar arithmetic using time_t
, you naturally have to worry about the type and representation of time_t
, which is of course implementation-defined. It's almost always a signed, integral type. On Unix and modern MacOS systems it's seconds since 1970, and for compatibility I think it might be used that way on Windows, too. It tends to be 32 bits. Putting that all together, it can typically represent dates between December 13, 1901 and January 18, 2038.
And indeed when I changed the tm_year
line in your code to
referenceDateComponent.tm_year = 60;
the code worked and printed 1721200226, which is about 19921 days or 54.5 years, which is exactly the difference between December 31, 1960 and today.
But if you set tm_year
to be negative, you'd be asking for a date before 1900, and that's not going to work using the typical definition of time_t
we've been discussing.
(It's true there are other possibilities for time_t
. It could be floating point. It could be unsigned instead of signed. It could be a 64-bit type, meaning it'd have a range of almost 600,000,000,000 years, which is incidentally more than a 32-bit tm_year
can hold.)
So although there are several naysayers here telling you not to, and although there are certainly plenty of obscure difficulties having to do with time zones and leap seconds and calendars other than Gregorian, you can usually get away with using time_t
to do basic calendar math for dates in the 20th century and in this century up until 2038, when the infamous "Y2.038K problem" is going to hit. (It will, I fear, be somewhat worse than the not-so-infamous Y2K problem, but that's another story.)
As I said, your code worked for me for dates before 1970. Here's what I'd recommend using for simple time_t
-based date calculations (and with caveats as already mentioned):
time_t makedate(int year, int month, int day)
{
struct tm tm = {0};
tm.tm_hour = 12;
tm.tm_min = tm.tm_sec = 0;
tm.tm_year = year - 1900;
tm.tm_mon = month - 1;
tm.tm_mday = day;
tm.tm_isdst = -1;
return mktime(&tm);
}
int main()
{
long int d = difftime(makedate(2015, 7, 17), makedate(2015, 6, 1));
printf("%ld sec = %ld days = %.2f years\n", d, d/86400, d/86400./365.25);
d = difftime(makedate(2015, 7, 17), makedate(2014, 7, 17));
printf("%ld sec = %ld days = %.2f years\n", d, d/86400, d/86400./365.25);
d = difftime(makedate(1975, 7, 17), makedate(1965, 7, 17));
printf("%ld sec = %ld days = %.2f years\n", d, d/86400, d/86400./365.25);
d = difftime(makedate(1950, 1, 11), makedate(1925, 1, 1));
printf("%ld sec = %ld days = %.2f years\n", d, d/86400, d/86400./365.25);
d = difftime(makedate(2030, 12, 31), makedate(2025, 12, 31));
printf("%ld sec = %ld days = %.2f years\n", d, d/86400, d/86400./365.25);
}
Just like your code, this leverages the surprisingly powerful mktime
function, and can do everything it can do. It handles leap years, no problem. It does not handle leap seconds or calendar changes.
And if, as you say, you're interested in dates before 1900, I'm afraid you're out of luck. time_t
simply cannot represent those dates on most systems, so you're going to have to pursue some other solution.
来源:https://stackoverflow.com/questions/31474486/how-to-calculate-time-differences-in-c-with-time-t-before-the-epoch