Reputation: 3736
Take the following piece of code:
static void printTime(const struct tm* t, const time_t stamp){
printf("%d-%d-%d, %d:%d:%d (DST %s) (stamp: %zu)\n",
1900 + t->tm_year, t->tm_mon + 1, t->tm_mday, t->tm_hour, t->tm_min, t->tm_sec, t->tm_isdst ? "Active" : "Inactive", stamp);
}
int main(){
time_t t = 1540633936;
struct tm tStruct;
localtime_r(&t, &tStruct);
printTime(&tStruct, t);
for (unsigned i = 0; i < 14; ++i){
tStruct.tm_sec += 7200;
//tStruct.tm_hour += 2;
tStruct.tm_isdst = -1;
t = mktime(&tStruct);
localtime_r(&t, &tStruct);
printTime(&tStruct, t);
}
return 0;
}
It shows two ways to increment the date. The documentation of mktime tells me:
The mktime() function modifies the fields of the tm structure as follows: tm_wday and tm_yday are set to values determined from the contents of the other fields; if structure members are outside their valid interval, they will be normalized (so that, for example, 40 October is changed into 9 November); tm_isdst is set (regardless of its initial value) to a positive value or to 0, respectively, to indicate whether DST is or is not in effect at the specified time.
Based on this I expect normalization to work in such a way that adding 7200 seconds is equivalent to adding two hours. But the output differs:
tStruct.tm_sec += 7200;
Gives:
2018-10-27, 11:52:16 (DST Active) (stamp: 1540633936) 2018-10-27, 13:52:16 (DST Active) (stamp: 1540641136) 2018-10-27, 15:52:16 (DST Active) (stamp: 1540648336) 2018-10-27, 17:52:16 (DST Active) (stamp: 1540655536) 2018-10-27, 19:52:16 (DST Active) (stamp: 1540662736) 2018-10-27, 21:52:16 (DST Active) (stamp: 1540669936) 2018-10-27, 23:52:16 (DST Active) (stamp: 1540677136) 2018-10-28, 1:52:16 (DST Active) (stamp: 1540684336) 2018-10-28, 2:52:16 (DST Inactive) (stamp: 1540691536) 2018-10-28, 3:52:16 (DST Inactive) (stamp: 1540695136) 2018-10-28, 5:52:16 (DST Inactive) (stamp: 1540702336) 2018-10-28, 7:52:16 (DST Inactive) (stamp: 1540709536) 2018-10-28, 9:52:16 (DST Inactive) (stamp: 1540716736) 2018-10-28, 11:52:16 (DST Inactive) (stamp: 1540723936) 2018-10-28, 13:52:16 (DST Inactive) (stamp: 1540731136)
(notice the wrong timejumps directly after DST change)
tStruct.tm_hour += 2;
Gives:
2018-10-27, 11:52:16 (DST Active) (stamp: 1540633936) 2018-10-27, 13:52:16 (DST Active) (stamp: 1540641136) 2018-10-27, 15:52:16 (DST Active) (stamp: 1540648336) 2018-10-27, 17:52:16 (DST Active) (stamp: 1540655536) 2018-10-27, 19:52:16 (DST Active) (stamp: 1540662736) 2018-10-27, 21:52:16 (DST Active) (stamp: 1540669936) 2018-10-27, 23:52:16 (DST Active) (stamp: 1540677136) 2018-10-28, 1:52:16 (DST Active) (stamp: 1540684336) 2018-10-28, 3:52:16 (DST Inactive) (stamp: 1540695136) 2018-10-28, 5:52:16 (DST Inactive) (stamp: 1540702336) 2018-10-28, 7:52:16 (DST Inactive) (stamp: 1540709536) 2018-10-28, 9:52:16 (DST Inactive) (stamp: 1540716736) 2018-10-28, 11:52:16 (DST Inactive) (stamp: 1540723936) 2018-10-28, 13:52:16 (DST Inactive) (stamp: 1540731136) 2018-10-28, 15:52:16 (DST Inactive) (stamp: 1540738336)
Which is the expected behaviour (to me, at least).
So, my question is: is there actually an error? Or is this documented behaviour, somewhere?
This behaviour also happens when tm_hour needs to be changed by mktime. Take the following example:
tStruct.tm_hour += 25;
Gives:
2018-10-27, 11:52:16 (DST Active) (stamp: 1540633936) 2018-10-28, 12:52:16 (DST Inactive) (stamp: 1540727536) 2018-10-29, 13:52:16 (DST Inactive) (stamp: 1540817536) 2018-10-30, 14:52:16 (DST Inactive) (stamp: 1540907536) 2018-10-31, 15:52:16 (DST Inactive) (stamp: 1540997536) 2018-11-1, 16:52:16 (DST Inactive) (stamp: 1541087536) 2018-11-2, 17:52:16 (DST Inactive) (stamp: 1541177536) 2018-11-3, 18:52:16 (DST Inactive) (stamp: 1541267536) 2018-11-4, 19:52:16 (DST Inactive) (stamp: 1541357536) 2018-11-5, 20:52:16 (DST Inactive) (stamp: 1541447536) 2018-11-6, 21:52:16 (DST Inactive) (stamp: 1541537536) 2018-11-7, 22:52:16 (DST Inactive) (stamp: 1541627536) 2018-11-8, 23:52:16 (DST Inactive) (stamp: 1541717536) 2018-11-10, 0:52:16 (DST Inactive) (stamp: 1541807536) 2018-11-11, 1:52:16 (DST Inactive) (stamp: 1541897536)
tStruct.tm_sec += 90000
Gives:
2018-10-27, 11:52:16 (DST Active) (stamp: 1540633936) 2018-10-28, 11:52:16 (DST Inactive) (stamp: 1540723936) 2018-10-29, 12:52:16 (DST Inactive) (stamp: 1540813936) 2018-10-30, 13:52:16 (DST Inactive) (stamp: 1540903936) 2018-10-31, 14:52:16 (DST Inactive) (stamp: 1540993936) 2018-11-1, 15:52:16 (DST Inactive) (stamp: 1541083936) 2018-11-2, 16:52:16 (DST Inactive) (stamp: 1541173936) 2018-11-3, 17:52:16 (DST Inactive) (stamp: 1541263936) 2018-11-4, 18:52:16 (DST Inactive) (stamp: 1541353936) 2018-11-5, 19:52:16 (DST Inactive) (stamp: 1541443936) 2018-11-6, 20:52:16 (DST Inactive) (stamp: 1541533936) 2018-11-7, 21:52:16 (DST Inactive) (stamp: 1541623936) 2018-11-8, 22:52:16 (DST Inactive) (stamp: 1541713936) 2018-11-9, 23:52:16 (DST Inactive) (stamp: 1541803936) 2018-11-11, 0:52:16 (DST Inactive) (stamp: 1541893936)
Upvotes: 1
Views: 345
Reputation: 16243
Depending on your exact time zone (and jurisdiction), at some time in the early morning on 2018-10-28, the clock is moved back 1 hour because DST ends. From your examples, it seems that happens at 3:00 in your timezone / jurisdiction.
In the first case (adding 7200 seconds to 2018-10-28, 1:52:16), the tm_sec
value is outside of the normal range (0 - 59), so mktime
can determine that you've added 2 hours, and since it knows that that's traversing the DST boundary, it adjusts the time accordingly. This results in 2018-10-28, 2:52:16, which is 2 hours after 2018-10-28, 1:52:16.
For the next increment in the first case (adding 7200 seconds to 2018-10-28, 2:52:16), the exact same thing happens again (since you're again traversing the DST boundary - you have reset tm_isdst
to -1
after all). This results in 2018-10-28, 3:52:16, which is 2 hours after 2018-10-28, 2:52:16.
In the second case (adding 2 hours to 2018-10-28, 1:52:16), the tm_hour
value is still inside of the normal range (0 - 23), so mktime
cannot determine that you've added 2 hours, and it just treats it like a local time. This results in 2018-10-28, 3:52:16, which is 3 hours after 2018-10-28, 1:52:16.
To avoid issues like these :
tm_isdst
to -1
unless needed (and you understand what will happen)Upvotes: 0