15

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
Dhiraj Neve
  • 235
  • 2
  • 4
  • 11
  • 1
    The code doesn't show that the hour is increasing. To do that, display the result that came from `strptime` as well as the result that came from the call to `mktime`. That will help pin down what's actually happening. – Pete Becker Aug 25 '12 at 12:51
  • Sorry, I can't parse that. It has three outputs instead of one, but no indication of where they came from. Please update the sample code to match the output. – Pete Becker Aug 25 '12 at 13:06

4 Answers4

22

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)
Community
  • 1
  • 1
rve
  • 5,897
  • 3
  • 40
  • 64
  • 1
    Thanks, It Helped and explained me my doubt. Now I need to run my piece of code on both type of system(DST on & off). e.g. If I put tm_isdst = 1 in code before calling to mktime(), it will give incorrect result on system where DST is off(it will end up decreasing tm_hour by one). Is there any other way to add seconds to given date? (It May be "avoid using mktime()" or "Make smart use of mktime() which will ignore tm_isdst flag") – Dhiraj Neve Aug 26 '12 at 02:38
  • @DhirajNeve: I added a couple of fixes. If you need to do more date/time related things, I think you should use a good C++ date/time library. – rve Aug 26 '12 at 07:00
  • Isn't `timegm` POSIX only? – lost_in_the_source May 08 '16 at 15:48
  • 1
    @stackptr `timegm` is a GNU extension and is not in POSIX. I think on Windows you have `_mkgmtime` which does the same thing if I remember correctly. – rve May 09 '16 at 06:54
5

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.

Jeffery Thomas
  • 42,202
  • 8
  • 92
  • 117
  • According to `man mktime` (http://linux.die.net/man/3/mktime) it can (and does) alter `stuct tm`. It normalizes its values and fills in missing fields. – rve Aug 25 '12 at 13:56
  • @rve it looks like you are right. I was misunderstood the wording. I thought it was normalizing for the purpose setting time_t. – Jeffery Thomas Aug 26 '12 at 01:48
1

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.

Kresimir
  • 777
  • 5
  • 20
0

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;

}
Adrian Mole
  • 49,934
  • 160
  • 51
  • 83