chetanchadha
chetanchadha

Reputation: 11

mktime & asctime giving random values sometimes correct sometimes year will be 1970

#include <stdio.h>      /* puts */
#include <time.h>       /* time_t, struct tm, time, localtime, strftime */
#include <bits/stdc++.h>
#include<unistd.h>

using namespace std;

int main ()
{
  char CURRZONE[] = "TZ=Asia/Kolkata";
  char DESTZONE[] = "TZ=UTC";

  time_t stamp;
  struct tm datetime;
  putenv(CURRZONE);
  char buffer[20];
  uint16_t timevec[] = {15,28,12,30,06,2021};
  datetime.tm_year = timevec[5]-1900;
  datetime.tm_mon = timevec[4]-1;
  datetime.tm_mday = timevec[3];
  datetime.tm_hour = timevec[2];
  datetime.tm_min = timevec[1];
  datetime.tm_sec = timevec[0];
  stamp = mktime(&datetime);
  putenv(DESTZONE);
  strftime (buffer,20,"%Y:%m:%d %T",localtime(&stamp));
  std::string str(buffer);
  cout<<str<<"\n";
  return 0;
}

it's sometimes giving me the correct output which is 2021:06:30 06:58:15

othertimes it's giving me 1969:12:31 23:59:59

I don't need to compile the program again if I run it again its giving a different value. This behaviour is in CPP not with C. Not able to understand the reason behind the same.

Upvotes: 1

Views: 193

Answers (1)

Ian Abbott
Ian Abbott

Reputation: 17413

The error can be reproduced reliably on a system using Glibc's implementation of mktime by setting datetime.tm_isdst to any positive value. That tells mktime that DST is in effect for the supplied time information. That seems to be causing mktime to return an error (return value (time_t)-1 with errno set to EOVERFLOW), perhaps because DST has not been in effect for Asia/Kolkata since October 15, 1945 and mktime has no idea what amount of DST correction should be applied. This is probably implementation-dependent1.

OP's code leaves datetime.tm_isdst uninitialized, so the error occurs randomly (an example of undefined behavior). Setting datetime.tm_isdst to 0 (meaning DST is not in effect) or a negative value (meaning mktime should attempt to determine whether DST is in effect or not) prevents the error occuring.


POSIX (but not the C or C++ standards) requires mktime to set errno to indicate an error. That could be used to determine whether the return value of (time_t)-1 indicates an error or is an actual time value. By setting errno = 0; before the call to mktime and checking it afterwards, it can be used to confirm whether the return value of (time_t)-1 represents a genuinely unsuccessful result or not:

    errno = 0;
    stamp = mktime(&datetime);
    if (stamp == (time_t)-1 && errno)
    {
        /* error */
    }
    else
    {
        /* OK */
    }

(Note: it is important to use stamp == (time_t)-1 rather than stamp == -1 for portability reasons.)


1 The implementation of mktime in the IANA tz distribution code does not return an error under the same conditions and applies a DST offset of 3600 seconds. Perhaps the Glibc implementation could be improved. It looks like the Glibc implementation of mktime came from Gnulib, and that if there is a mismatch between the requested tm_isdst value and the actual tm_isdst value for the specified time, it will search neighboring transitions for a DST offset, but only within a range spanning 536454000 seconds (about 17 years) before and after the specified time. If no tm_isdst match is found within that range, it gives up and returns an error with errno set to EOVERFLOW.

Upvotes: 4

Related Questions