Confusing behaviour of mktime() function : increasing tm_hour count by one
Вопрос
I am executing below code.
int main()
{
struct tm storage={0,0,0,0,0,0,0,0,0};
char *p = NULL;
p = (char *)strptime("2012-08-25 12:23:12","%Y-%m-%d %H:%M:%S",&storage);
char buff[1024]={0};
strftime(buff,1024,"%Y-%m-%d %H:%M:%S",&storage);
cout << buff << endl;
storage.tm_sec += 20;
strftime(buff,1024,"%Y-%m-%d %H:%M:%S",&storage);
cout << buff << endl;
mktime(&storage);
strftime(buff,1024,"%Y-%m-%d %H:%M:%S",&storage);
cout << buff << endl;
return 0;
}
If above Program executed, It prints ' 2012-08-25 13:23:32' instead of '2012-08-25 12:23:32'. Please Help, why it is increasing tm_hour value. This works correctly if I put input date as '2012-02-25 12:23:32' in program, which is confusing.
OUtput ->
[user@rtpkvm55-vm2 root]$ ./a.out
2012-08-25 12:23:12
2012-08-25 12:23:32
2012-08-25 13:23:32
[user@rtpkvm55-vm2 root]$
Date Info on my system, -->
[user@rtpkvm55-vm2 root]$ date
Sat Aug 25 08:28:26 EDT 2012
Решение
What happens
The date you specified has daylight savings in effect but when calling mktime
, storage.tm_isdst
is zero. mktime
sees this and thinks "hey, they gave me a date with an incorrect daylight savings flag, lets fix it". Then it sets tm_isdst
to 1 and changes tm_hour
.
See also this answer.
To fix it
- use
timegm
instead ofmktime
- set the timezone to UTC before calling
mktime
(see also example fromtimegm
) :setenv("TZ", "", 1); tzset(); mktime();
- use a good date-time library (like boost::locale::date_time/boost::date_time, but read the Q&A section on the
boost::locale::date_time
page before picking one)
Другие советы
Wow, there just is no way around it. It must be a bug in your system's implementation of mktime(3). mktime(3) should not alter the struct tm *
passed to it.
I would suggest checking the value of storage.tm_isdst
. Try setting it to 0 to ensure it's not confused about DST. If that doesn't work, try setting it to -1 to let it auto determine the proper value.
mktime - convert broken-down time into time since the Epoch
A positive or 0 value for tm_isdst causes mktime() to presume initially that Daylight Savings Time, respectively, is or is not in effect for the specified time. A negative value for tm_isdst causes mktime() to attempt to determine whether Daylight Saving Time is in effect for the specified time.
I was wrong about mktime(3) not modifying struct tm *
. It is the correct behavior to normalize the value.
You have to set tm_isdst
in the tm
struct otherwise it is uninitialised, and thus gets set to a random garbage value. Then, when you call mktime
depending on which random garbage is in tm_isdst
variable, it either applies daylight saving time or it doesn't, seemingly unpredictably.
However, if you set it to -1
, you tell mktime
that you don't know whether daylight saving time is in effect, so the first call to mktime
will fix it.
Therefore, the simplest way to fix this issue is adding:
storage.tm_isdst = -1;
before calling mktime
.