Dhiraj Neve
Dhiraj Neve

Reputation: 235

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

Upvotes: 15

Views: 11464

Answers (4)

Rajat Gupta
Rajat Gupta

Reputation: 1

Here is a trick to fix your code:

int main()
{

    // Need to know if daylight saving is on or off, use the following trick
    // Get the current time in seconds since epoch, convert it to local time,
    // tm_isdst(is daylight saving) value, in the tm variable returned by the localtime(), will be set accordingly
    time_t now = time(0);
    struct tm *tm2= localtime(&now); 
    
    struct tm storage={0,0,0,0,0,0,0,0,tm2->tm_isdst}; // Note: used the is daylight saving on flag fetched above
    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;

}

Upvotes: 0

Kresimir
Kresimir

Reputation: 797

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.

Upvotes: 1

rve
rve

Reputation: 6055

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 of mktime
  • set the timezone to UTC before calling mktime (see also example from timegm) :
    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)

Upvotes: 22

Jeffery Thomas
Jeffery Thomas

Reputation: 42588

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.

Upvotes: 5

Related Questions