RWTime is displaying localtime+1 - c++

Why RWTime is giving 1 hour more
#include <rw/rwtime.h>
#include <rw/rwdate.h>
#include <rw/rstream.h>
main(){
RWTime t; // Current time
RWTime d(RWTime::beginDST(1990, RWZone::local()));
cout << "Current time: " << RWDate(t) << " " << t <<
endl;
cout << "Start of DST, 1990: " << RWDate(d) << " " << d <<
endl;
}
Above program prints:
root#otp42mas:/home/nmsadm/sapna/cProgS# ./a.out
Current time: 10/27/10 10/27/10 17:08:06
Start of DST, 1990: 04/01/90 04/01/90 03:00:00
But date gives:
root#otp42mas:/home/nmsadm/sapna/cProgS# date
Wed Oct 27 16:08:10 IST 2010

My sixth sense is tingling, it tells me that the answer has something to do with the daylight savings time ... I'm not sure why, though ...

By default RWZone::local() will return an RWZone implementation based on North American DST transitions. RWZone::os() provides an RWZone implementation based DST transitions derived from the current system time zone. RWZone::local() can be updated to use RWZone::os() using:
RWZone::local(&RWZone::os());

Related

Why does ctime returns the same string for different time_t values?

I have a function which prints time_t values.
void Logger::describe()
{
cout << m_start_time << " " << m_end_time << "\n";
if (ctime(&m_start_time) == ctime(&m_end_time))
{
cout << "check1\n";
}
cout << m_vehicle->getPlate() << " " << ctime(&m_start_time) << " " << ctime(&m_end_time) << "\n";
}
// m_start_time and m_end_time are private variables of type time_t of the class Logger
For a sample output after waiting a couple of seconds I get
1634907786 1634907791
check1
bike1 Fri Oct 22 18:33:06 2021
Fri Oct 22 18:33:06 2021
As can be seen m_start_time and m_end_time are different but ctime returns the the same value. Can anyone help explain why ?
I'm using gcc 6.3.0 if it helps.
Read the information on the return value here: ctime
It is a pointer to a static string. You are not comparing the string content (see strcmp), only this pointer.

Problem when converting from time_t to tm then back to time_t

I have a time_t value of 1530173696 which represents Thursday, June 28, 2018 8:14:56 AM.
I want to round down the time to the nearest hour. Specifically, down to 1530172800, which represent Thursday, June 28, 2018 8:00:00 AM. So, my idea is to convert this time_t to a tm struct, and then assign its sec and min values to 0.
However, after I do that, and after I convert the modified tm back to a time_t value, the value I get is way off. I get a value of 1530158400 which represents Thursday, June 28, 2018 4:00:00 AM. That's 4 hours off. Even checking values of up to 8:59:59 AM still gives the rounded down value of 4:00:00 AM.
I wrote the code below to demonstrate the problem. I use VisulStudio 2017.
I don't understand what I am doing wrong. I appreciate any help. Thanks.
#include <iostream>
#include <time.h>
bool equalTMs(tm& tm1, tm& tm2);
void printTM(tm& myTM);
int main()
{
tm myTM;
time_t datetime = 1530173696;
//datetime = 1530176399; // to check the time_t value of 8:59 AM
gmtime_s(&myTM, &datetime);
myTM.tm_sec = 0;
myTM.tm_min = 0;
time_t myTime_T = mktime(&myTM);
tm sanityCheckTM;
time_t roundedDownToNearestHour = 1530172800;
gmtime_s(&sanityCheckTM, &roundedDownToNearestHour);
time_t sanityCheckTimeT = mktime(&sanityCheckTM);
std::cout << "datetime: " << datetime << std::endl;
std::cout << "myTime_T: " << myTime_T << std::endl;
std::cout << std::endl;
std::cout << "roundedDownToNearestHour: " << roundedDownToNearestHour << std::endl;
std::cout << "sanityCheckTimeT: " << sanityCheckTimeT << std::endl;
std::cout << std::endl;
std::cout << "myTM and sanityCheckTM equal? " << (equalTMs(myTM, sanityCheckTM) ? "true" : "false") << std::endl;
std::cout << "\nmyTM:-\n\n";
printTM(myTM);
std::cout << "\nsanityCheckTM:-\n\n";
printTM(sanityCheckTM);
std::cout << "\n";
time_t _time_t = 1530158400;
tm _tm;
gmtime_s(&_tm, &_time_t);
std::cout << "_time_t: " << _time_t << std::endl;
std::cout << "_tm and sanityCheckTM equal? " << (equalTMs(_tm, sanityCheckTM) ? "true" : "false") << std::endl;
std::cout << "\n_tm:-\n\n";
printTM(_tm);
}
void printTM(tm& myTM)
{
std::cout << "tm_sec: " << myTM.tm_sec << std::endl;
std::cout << "tm_min: " << myTM.tm_min << std::endl;
std::cout << "tm_hour: " << myTM.tm_hour << std::endl;
std::cout << "tm_mday: " << myTM.tm_mday << std::endl;
std::cout << "tm_mon: " << myTM.tm_mon << std::endl;
std::cout << "tm_year: " << myTM.tm_year << std::endl;
std::cout << "tm_wday: " << myTM.tm_wday << std::endl;
std::cout << "tm_yday: " << myTM.tm_yday << std::endl;
std::cout << "tm_isdst: " << myTM.tm_isdst << std::endl;
}
bool equalTMs(tm& tm1, tm& tm2)
{
return (tm1.tm_sec == tm2.tm_sec)
&& (tm1.tm_min == tm2.tm_min)
&& (tm1.tm_hour == tm2.tm_hour)
&& (tm1.tm_mday == tm2.tm_mday)
&& (tm1.tm_mon == tm2.tm_mon)
&& (tm1.tm_year == tm2.tm_year)
&& (tm1.tm_wday == tm2.tm_wday)
&& (tm1.tm_yday == tm2.tm_yday)
&& (tm1.tm_isdst == tm2.tm_isdst);
}
gmtime_s() returns a tm that is expressed in UTC time. You pass that to mktime(), which expects the tm to be expressed in LOCAL time instead. Your StackOverflow profile says you are located in Abu Dhabi, whose time zone is GMT+4. That is why you have a 4-hour discrepancy.
Use localtime_s() instead of gmtime_s().
Since 1530173696 is being used as a Unix Time (UTC excluding leap seconds), this can be solved without involving time zones.
Howard Hinnant's date/time library can be used to solve this problem, and to check that you're getting the right answer. However, skip to the end of this answer if you want to see how to do this very simply without the use of any library at all.
1530173696 is a count of seconds since 1970-01-01 UTC. If you want to convert this into a human readable date/time, one can:
#include "date/date.h"
#include <iostream>
int
main()
{
time_t datetime = 1530173696;
date::sys_seconds tp{std::chrono::seconds{datetime}};
using date::operator<<;
std::cout << tp << '\n';
}
which outputs:
2018-06-28 08:14:56
This does nothing but validate the input. Furthermore tp is nothing more than a std::chrono::time_point based on system_clock but with a precision of seconds. You can round this down to the hour with:
tp = floor<std::chrono::hours>(tp);
Here floor can be grabbed from "date.h" under namespace date, or if you have C++17 or later, you can use std::chrono::floor. You can use "date.h" to print tp out again and you will get:
2018-06-28 08:00:00
(as desired). To turn this back into a time_t, simply extract the duration, and then the count:
time_t myTime_T = tp.time_since_epoch().count();
This will have the value 1530172800 as expected.
Finally, if you do not need to print these time stamps out in a human readable form, you can do the math quite easily yourself:
time_t myTime_T = datetime / 3600 * 3600;
This is essentially the same operation as:
tp = floor<std::chrono::hours>(tp);
except that the floor version will continue to get the correct answer when the input is negative (a timestamp prior to 1970-01-01 00:00:00 UTC). The "manual" implementation will round up to the next hour when given a negative input.

Cross-platform C++: convert to/from UTC/local time WITH historical tzdata

I need to convert times from UTC to a timezone selected by the user. I also have to convert from user input in that time zone to store in UTC.
Currently timezones are defined in Olson format ("America/Los Angeles").
A solution was easy on Linux with timegm, but I can not find a cross platform solution (or any solution) that does the exact same thing on Windows.
I can not use Boost.Date_Time (http://www.boost.org/doc/libs/1_57_0/doc/html/date_time.html) because it does not support historical timezone changes such as varying DST periods over the years. Someone apparently submitted a patch years ago but it does not appear to have been accepted.
The only other solution that seems plausible is to use data and code from: https://www.iana.org/time-zones
Has anyone tried this, or do you have a better idea?
Here is a cross platform, open source C++11/C++14 timezone library that wraps the IANA timezone database, including all historical data. The IANA database is the currently maintained Olson database.
An example from this library is:
#include "tz.h"
#include <iostream>
int
main()
{
using namespace std::chrono_literals;
using namespace date;
auto departure = make_zoned("America/New_York",
local_days{dec/30/1978} + 12h + 1min);
auto flight_length = 14h + 44min;
auto arrival = make_zoned("Asia/Tehran",
departure.get_sys_time() + flight_length);
std::cout << "departure NYC time: " << departure << '\n';
std::cout << "flight time is " << make_time(flight_length) << '\n';
std::cout << "arrival Tehran time: " << arrival << '\n';
}
which outputs:
departure NYC time: 1978-12-30 12:01:00 EST
flight time is 14:44
arrival Tehran time: 1978-12-31 11:45:00 IRST
Note the date: December of 1978. This is historically accurate (as far as timezone handling is concerned). The paper goes on to demonstrate how the same flight on the next day has a different arrival time because of a timezone change in Tehran.
The example above does not handle leap seconds. However if you actually do want to handle leap seconds, the library can handle it with a small amount of extra work:
#include "tz.h"
#include <iostream>
int
main()
{
using namespace std::chrono_literals;
using namespace date;
auto departure = make_zoned("America/New_York",
local_days{dec/31/1978} + 12h + 1min);
auto departure_utc = to_utc_time(departure.get_sys_time());
auto flight_length = 14h + 44min;
auto arrival = make_zoned("Asia/Tehran",
to_sys_time(departure_utc + flight_length));
std::cout << "departure NYC time: " << departure << '\n';
std::cout << "flight time is " << make_time(flight_length) << '\n';
std::cout << "arrival Tehran time: " << arrival << '\n';
}
departure NYC time: 1978-12-31 12:01:00 EST
flight time is 14:44
arrival Tehran time: 1979-01-01 11:14:59 IRST
This is all described in detail at:
http://howardhinnant.github.io/date/tz.html
and freely available at:
https://github.com/HowardHinnant/date

Strange behaviour from references in an output stream

I have noticed strange behaviour when printing output to a stream. My code loops through a large dataset and, amongst other things, reads a timestamp from each item. The timestamp from the first item is stored so an elapsed time can be calculated.
CurTime = ev[i].MidasTimeStamp;
RunTimeElapsed = difftime(CurTime,StartTime);
cout << "StartTime: " << ctime(&StartTime) << "CurTime: " << ctime(&CurTime) << "Elapsed: " << RunTimeElapsed << " s" << endl;
The output printed to screen shows the same time printed twice e.g:
StartTime: Mon Sep 23 14:44:57 2013
CurTime: Mon Sep 23 14:44:57 2013
Elapsed: 360 s
But if split the print line into two:
cout << "StartTime: " << ctime(&StartTime);
cout << "CurTime: " << ctime(&CurTime) << "Elapsed: " << RunTimeElapsed << " s" << endl;
I get the expected output:
StartTime: Mon Sep 23 14:44:57 2013
CurTime: Mon Sep 23 14:50:57 2013
Elapsed: 360 s
The only change between the two outputs was to the cout line(s). This is easy enough to work around but I'd like to understand what's happening.
From the documentation on ctime:
The returned value points to an internal array whose validity or value
may be altered by any subsequent call to asctime or ctime.
The order of evaluation of subexpressions within the expression is unspecified. In particular, it is legal for the compiler to call ctime twice first, then call operator<< as necessary. This is what seems to happen in your case.

Boost Date Time Parsing string

I have looked at many examples seem to address this simple case. The string I want to parse is:
"2012-06-01 16:45:34 EDT"
I have tried to create a local_time_input_facet with the folloiwng:
"%Y-%m-%d %H:%M:%S %Z"
The zone pointer of the local_date_time object is always not set. Reading the documentation is confusing:
%Z *!
Full time zone name (output only). This flag is ignored when using the time_facet with a ptime.
"EDT" // Eastern Daylight Time
Has anyone done this before?
UPDATE: I have updated the code to illustrate the problem a little better:
using namespace std;
using namespace boost::local_time;
int main()
{
stringstream ss;
// Set up the input datetime format.
local_time_input_facet *input_facet
= new local_time_input_facet("%Y-%m-%d %H:%M:%S %ZP");
ss.imbue(std::locale(ss.getloc(), input_facet));
local_date_time ldt(not_a_date_time),ldt1(not_a_date_time);
// Read a time into ldt
ss.str("2012-06-01 17:45:34 EDT");
ss >> ldt;
ss.str("2012-06-01 17:45:34 CDT");
ss >> ldt1;
std::cerr << (ldt - ldt1).total_seconds() << std::endl;
// Write the time to stdout.
cout << "Full Time:\t" << ldt.to_string() << endl;
cout << "Local time:\t" << ldt.local_time() << endl;
cout << "Time zone:\t" << ldt.zone_as_posix_string() << endl;
cout << "Zone abbrev:\t" << ldt.zone_abbrev() << endl;
cout << "Zone offset:\t" << ldt.zone_abbrev(true) << endl;
cout << "Full Time:\t" << ldt1.to_string() << endl;
cout << "Local time:\t" << ldt1.local_time() << endl;
cout << "Time zone:\t" << ldt1.zone_as_posix_string() << endl;
cout << "Zone abbrev:\t" << ldt1.zone_abbrev() << endl;
cout << "Zone offset:\t" << ldt1.zone_abbrev(true) << endl;
return 0;
}
OUTPUT:
0
Full Time: 2012-Jun-01 17:45:34 EDT
Local time: 2012-Jun-01 17:45:34
Time zone: EDT+00
Zone abbrev: EDT
Zone offset: +0000
Full Time: 2012-Jun-01 17:45:34 CDT
Local time: 2012-Jun-01 17:45:34
Time zone: CDT+00
Zone abbrev: CDT
Zone offset: +0000
The Bug
According to boost's documentation here: http://www.boost.org/doc/libs/1_57_0/doc/html/date_time/date_time_io.html#date_time.format_flags
%Z is:
Full time zone name (output only).
It also says to %ZP:
Posix time zone string (available to both input and output).
So you need to change %Z to %ZP. Now your timestamps will parse. However, you'll notice that the zone offset will not be set.
Posix Time Zone Strings
For a Posix time zone string, you'll need to specify at least the zone abbreviation and the offset from UTC, e.g. EST-5.
A full Posix time zone string is formatted as:
"std offset dst [offset],start[/time],end[/time]"
with no spaces, according to http://www.boost.org/doc/libs/1_57_0/doc/html/date_time/local_time.html#date_time.local_time.posix_time_zone
Here are some examples of full Posix time zone strings for EST and PST:
EST-5EDT,M3.2.0,M11.1.0
PST-8PDT,M4.1.0,M10.1.0
This contains the information on when Daylight Savings Time is in effect.
However, you may be able to get away with EDT-4 in your case, depending on what you're doing with it.
It should be noted that as simple as Posix time zones are, they are limited in that they don't account for historical changes in time zone rules. I think it's best to just avoid working with time zones in the first place.
What if I can't control the input format?
As the OP noted in the comments, the offsets are not listed in his input timestamps. One solution is to read the zone abbreviation from the end of the input timestamp (e.g. "EDT"), and check it against a map of known zone abbreviations:
std::map<std::string, std::string> zone_map;
zone_map["EST"] = "EST-5EDT,M4.1.0,M10.5.0"; // Eastern Standard Time
zone_map["EDT"] = zone_map["EST"]; // Eastern Daylight Time
zone_map["PST"] = "PST-8PDT,M4.1.0,M10.1.0"; // Pacific Standard Time
zone_map["PDT"] = zone_map["PST"]; // Pacific Daylight Time
// ...
(Note the DST zones above should be the same as the standard time zones.)
You might also get away with with just storing simple UTC offsets:
zone_map["EST"] = "EST-5"; // Eastern Standard Time
zone_map["EDT"] = "EDT-4"; // Eastern Daylight Time
// ...
Working Example
Here's an example that uses a built-in timezone database:
#include <map>
#include <string>
#include <sstream>
#include <iostream>
#include <boost/date_time/local_time/local_time.hpp>
using namespace boost::local_time;
int main()
{
// A little database of time zones.
std::map<std::string, std::string> zone_map;
zone_map["EST"] = "EST-5EDT,M4.1.0,M10.5.0"; // Eastern Standard Time
zone_map["EDT"] = zone_map["EST"]; // Eastern Daylight Time
zone_map["PST"] = "PST-8PDT,M4.1.0,M10.1.0"; // Pacific Standard Time
zone_map["PDT"] = zone_map["PST"]; // Pacific Daylight Time
// ...
// This is our input timestamp.
std::string timestamp = "2012-06-01 16:45:34 EDT";
// Replace time zone abbrev with full Posix time zone.
const size_t abbrev_pos = timestamp.find_last_of(' ') + 1;
const std::string abbrev = timestamp.substr(abbrev_pos);
timestamp.replace(abbrev_pos, std::string::npos, zone_map[abbrev]);
std::cout << "Time stamp with full timezone: " << timestamp << std::endl;
// Set up the input datetime format.
local_time_input_facet *input_facet = new local_time_input_facet("%Y-%m-%d %H:%M:%S %ZP");
std::stringstream ss;
ss.imbue(std::locale(ss.getloc(), input_facet));
// This is our output date time.
local_date_time ldt(not_a_date_time);
// Read the timestamp into ldt.
ss.str(timestamp);
ss >> ldt;
// Write the time to stdout.
std::cout << "Full Time:\t" << ldt.to_string() << std::endl
<< "Local time:\t" << ldt.local_time() << std::endl
<< "Time zone:\t" << ldt.zone_as_posix_string() << std::endl
<< "Zone abbrev:\t" << ldt.zone_abbrev() << std::endl
<< "Zone offset:\t" << ldt.zone_abbrev(true) << std::endl;
return 0;
}
This outputs:
Time stamp with full timezone: 2012-06-01 16:45:34 EST-5EDT,M4.1.0,M10.5.0
Full Time: 2012-Jun-01 16:45:34 EDT
Local time: 2012-Jun-01 16:45:34
Time zone: EST-05EDT+01,M4.1.0/02:00,M10.5.0/02:00
Zone abbrev: EDT
Zone offset: -0400
While the solution here may not be ideal, it's the only way I can see of doing it.