Reputation: 12279
It's a strange situation. Check out this Coliru code:
#include <iostream>
#include <utility>
#include <ctime>
using namespace std;
#define SEGS 60
#define MINS 60
#define HOURS 24
int days(tm* date1, tm* date2)
{ return (mktime(date1) - mktime(date2)) / SEGS / MINS / HOURS; }
tm mkdate(int day, int mon, int year)
{
tm date = {0, 0, 0};
date.tm_mday = day;
date.tm_mon = mon - 1;
date.tm_year = year - 1900;
return date;
}
int main()
{
tm date1 = mkdate(31, 12, 2030);
tm date2 = mkdate(1, 1, 2000);
cout << days(&date1, &date2) << endl;
// 11322... OK 30 * 365 (1/1/2000 - 1/1/2030)
// + 8 (leap years) + 364 (from 1/1/2030 - 31/12/2030).
date1 = mkdate(31, 12, 2030);
date2 = mkdate(1, 1, 1930);
cout << days(&date1, &date2) << endl;
// 36889... OK; but in my machine, it returns 36888.
date1 = mkdate(31, 12, 1943);
date2 = mkdate(1, 1, 1943);
cout << days(&date1, &date2) << endl;
// 364... OK: but in my machine, it returns 363.
date1 = mkdate(30, 6, 1943);
date2 = mkdate(1, 6, 1943);
cout << days(&date1, &date2) << endl;
// 29... OK; but in my machine, it returns 28.
date1 = mkdate(27, 6, 1943);
date2 = mkdate(26, 6, 1943);
cout << days(&date1, &date2) << endl;
// 1... OK; but in my machine, it returns 0.
return 0;
}
The comments after each example are those from Coliru and my laptop. The Coliru outputs are corrects, but my machine is who prints the wrong numbers.
If you read the code, the difference between days is correctly computed (first example, from 1/1/2000 to 31/12/2030).
But if year 1943 is in the middle of a date interval, it seems a day is lost. Second example: 1/1/1930 - 31/12/2030.
After lots of tests, I found out the problem was in Juny/1943. Third example: 1/6/1943 - 30/6/1943, returning 28 days instead of 29.
More concretly, it seems 26th and 27th are the same day. Fourth example: 26/6/1943 - 27/6/1943, returning 0 days.
My machine is a Ubuntu 14.02.2 LTS with Linux 3.13.0-52-generic x86_64, using gcc (g++) 4.8.2.
What does the problem come from? Some kind of bug of GNU libc implementation?
Upvotes: 2
Views: 372
Reputation:
It is a timezone issue. Add this at the beginning of main():
// set the timezone to UTC
setenv("TZ", "", 1);
tzset();
Also your days() function should be written like this:
double days (tm *date1, tm *date2) {
return difftime (mktime(date1), mktime(date2)) / 86400;
}
The root cause is that time_t represents seconds since the epoch in UTC whereas mktime() interprets its argument as a date in the local timezone. Hence it must convert it to UTC to produce a time_t. Thus if there are discontinuities relative to UTC between the two dates in the local timezone the result might be wrong.
The real solution though is not to use mktime(). It is not intended for your application (and in addition to DST there are other issues like leap seconds).
What your are trying to do is calculate the number of days between two dates in the (proleptic) gregorian calendar. You should implement this yourself, for example by converting them to julian day numbers.
Upvotes: 3
Reputation: 11
This is not a timezone problem, even if the problem does not manifest with UTC.
There are several issues with this program, but the key one one is the fact that tm_isdst
fields are not initialized. This field affect the behaviour of mktime
, which introduces an adjustment in the tm_hour
fields.
With this particular dates (26/6/1943, 27/6/1943), timezone (Europe/Madrid) and 0 as the content of both tm_isdst
fields, you get a difference of 23 hours (82800 s) between both dates instead of 24 hours (86400 s). Then, in function days
, 82800 is divided by 86400 with an integer division. The result is 0 and the day off is totally lost.
Plese, just add:
date.tm_isdst = -1;
to your mkdate
function to avoid this, by telling mktime
to check the dates against the timezone for DST before computing the number of seconds since the epoch.
Upvotes: 1
Reputation: 368389
On Ubuntu 15.04, 32 bit, I get
edd@don:/tmp$ ./mkdate
11322
-12821
364
29
1
edd@don:/tmp$
where as on Ubuntu 15.04, 64 bit, I get
edd@max:/tmp$ ./mkdate
11322
36889
364
29
1
edd@max:/tmp$
That is g++ 4.9.2 in both cases. The difference is due to time_t
being four and eight bytes, respectively, so that the date difference calculation in your code gets an overflow. But I do not see what may be a library bug in your older release.
Upvotes: 0