Dealing with daylight savings time - C++

Sriram picture Sriram · Mar 27, 2013 · Viewed 12.2k times · Source

Given an input date and time (in string format), I am trying to get the epoch time for it using time functions given in ctime like mktime. Converting the time_t epoch time back to a date and time results in a date and time that is one hour less than the original. I have gone through some discussions which say that there may be an adjustment of one hour in case of daylight savings time. Here is the code:

//sample strptime program.

#include <iostream>
#include <ctime>
#include <string>
using namespace std;

long parseTime(string time) {

  cout << "Time entered = " << time << endl;

  long timeSinceEpoch;

  struct tm t;

  if(time.find("/") != string::npos) {
    //format of date is mm/dd/yyyy. followed by clock in hh:mm (24 hour clock).
    if(strptime(time.c_str(), "%m/%e/%Y %H:%M", &t) == NULL) {
      cout << "Error. Check string for formatting." << endl;
    }
  } else if(time.find("-") != string::npos) {
    //format of date is yyyy-mm-dd hh:mm:ss (hh in 24 hour clock format).
    cout << "I am here." << endl;
    if(strptime(time.c_str(), "%Y-%m-%e %H:%M:%S", &t) == NULL) {
      cout << "Error. Check string for formatting of new date." << endl;
    }
  }

  cout << "Details of the time structure:" << endl;
  cout << "Years since 1900 = " << t.tm_year << endl;
  cout << "Months since January = " << t.tm_mon << endl;
  cout << "Day of the month = " << t.tm_mday << endl;
  cout << "Hour = " << t.tm_hour << " Minute = " << t.tm_min << " second = " << t.tm_sec << endl;

  timeSinceEpoch = mktime(&t);
  time_t temp = mktime(&t);
  cout << "Time since epoch = " << timeSinceEpoch << endl;

  cout << "Reconverting to the time structure:" << endl;
  struct tm* t2 = localtime(&temp);
  cout << "Details of the time structure:" << endl;
  cout << "Years since 1900 = " << t2->tm_year << endl;
  cout << "Months since January = " << t2->tm_mon << endl;
  cout << "Day of the month = " << t2->tm_mday << endl;
  cout << "Hour = " << t2->tm_hour << " Minute = " << t2->tm_min << " second = " << t2->tm_sec << endl;

  return timeSinceEpoch;
}

int main(int argc, char *argv[]) {

  string date, t;
  cout << "Enter date: " << endl;
  cin >> date;
  cout << "Enter time: " << endl;
  cin >> t;

  struct tm time;
  string overall = date + " " + t;

  long result = parseTime(overall);
  cout << "Time in date + time = " << overall << " and since epoch = " << result << endl;

return 0;
}

The troublesome input is: date: 2013-03-11 time: 04:41:53

My questions(s):
1. Checking for tm_idst flag returns a non-zero, indicating that DST is in effect. However, how do I know what timezone is being talked about?
2. The timestamp given above may not have been recorded in the same time zone as I am in. Is there a way to specify timezone so that the tm_idst flags are correctly set?
3. How do I deal with DST when I am unsure of what timezone the timestamp was recorded in?

Answer

Petesh picture Petesh · Mar 27, 2013

Plain C++ is pretty sparse with timezone data, and without a timezone specification in the time to be formatted, there are a few periods where you will get an inconsistent result - e.g. the repeated time following the clocks going back. This is why it is always recommended to record all timestamps in UTC - i.e. never apply a timezone to the recorded timestamp, record it in GMT, and then back-and-forth with that value as a display variable, which you have total control over.

Linux/BSD have some additional fields that can be used to determine the timezone, and the offset from UTC - e.g. on linux it's the __tm_gmtoff field, and in BSD(/Mac OS X) it's called tm_gmtoff.

There is an additional field labelling the timezone, which is __tm_zone on linux and tm_zone in BSD(/Mac OS X), but that field is only populated when you get localtime.

I altered your example slightly, and got the following output:

Time entered = 2013-04-05 15:00
I am here.
Error. Check string for formatting of new date.
Details of the time structure:
Years since 1900 = 113
Months since January = 3
Day of the month = 5
Hour = 15 Minute = 0 second = 0
gmtoff = 0
Time since epoch = 1365174000
Reconverting to the time structure:
Details of the time structure:
Years since 1900 = 113
Months since January = 3
Day of the month = 5
Hour = 16 Minute = 0 second = 0
gmtoff = 3600
Zone = IST
Time in date + time = 2013-04-05 15:00 and since epoch = 1365174000

If you're using this structure in Windows, you're going to have to use another mechanism, though, as it doesn't have these two extra fields.