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?
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.