1
#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.

Eric Postpischil
  • 195,579
  • 13
  • 168
  • 312
  • `1969:12:31 23:59:59` corresponds to `(time_t)-1` which is probably an error value. – Ian Abbott Jul 01 '21 at 11:34
  • 3
    Try memsetting `datetime` to zero? Are there not other members in there, which are, perhaps causing `mktime` to set an error value. – Bathsheba Jul 01 '21 at 11:35
  • Do not tag both C and C++ except when asking about differences or interactions between the two languages. – Eric Postpischil Jul 01 '21 at 11:35
  • The code does not set `datetime.tm_isdst` which could result in DST being randomly applied or not (although Asia/Kolkata does not use DST). – Ian Abbott Jul 01 '21 at 11:36
  • `struct tm datetime = {0};` would probably be much safer... – Serge Ballesta Jul 01 '21 at 11:50
  • see [Why should I not `#include `?](https://stackoverflow.com/q/31816095/995714), [Why is “using namespace std;” considered bad practice?](https://stackoverflow.com/q/1452721/995714) – phuclv Jul 01 '21 at 13:02

1 Answers1

4

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.

Ian Abbott
  • 15,083
  • 19
  • 33
  • Thanks for the insights, what should I do when I have few locations which have DST applicable and need that for accurate conversion and others where DST is not applicable. – chetanchadha Jul 02 '21 at 17:13
  • @chetanchadha You can set `tm_isdst` to a negative value if you are not sure if DST is applicable or not, but if the specified time is in a "DST overlap" region (when the clocks go back), the result will be ambiguous. – Ian Abbott Jul 02 '21 at 17:21