When I run the following code strptime seems to ignore the timezone value. Just sets value of the local timezone (which is +10).
This is the output, (Running on Linux, compiled with gcc 4.6.3):
-----------2013-04-24T9:47:06+400 - %Y-%m-%dT%H:%M:%S%z
TM Break H:9 is DST:0 GMT Off:0
The epoch value: 1366760826
DateTime in String: 04/24/13 - 09:47AM +1000
-----------2013-04-24T11:47:06+800 - %Y-%m-%dT%H:%M:%S%z
TM Break H:11 is DST:0 GMT Off:36000
The epoch value: 1366768026
DateTime in String: 04/24/13 - 11:47AM +1000
-----------2013-04-24T9:47:06+0 - %Y-%m-%dT%H:%M:%S%z
TM Break H:9 is DST:0 GMT Off:36000
The epoch value: 1366760826
DateTime in String: 04/24/13 - 09:47AM +1000
-----------2013-04-24T9:47:06+4 - %Y-%m-%dT%H:%M:%S%z
TM Break H:9 is DST:0 GMT Off:36000
The epoch value: 1366760826
DateTime in String: 04/24/13 - 09:47AM +1000
This is the code:
void date_Test(){
string dateStrings[] = {"2013-04-24T9:47:06+400"
, "2013-04-24T11:47:06+800"
, "2013-04-24T9:47:06+0"
, "2013-04-24T9:47:06+4"};
string formatStrings[] = {"%Y-%m-%dT%H:%M:%S%z"
, "%Y-%m-%dT%H:%M:%S%z"
, "%Y-%m-%dT%H:%M:%S%z"
, "%Y-%m-%dT%H:%M:%S%z"};
process_Timezone(dateStrings, formatStrings);
}
void process_Timezone(string dateStrings[], string formatStrings[]){
int num = 4;
for (int i = 0; i < num; i++) {
cout << endl << "-----------" << dateStrings[i] << " - " << formatStrings[i] << endl;
tm *dtm = new tm;
strptime(dateStrings[i].c_str(), formatStrings[i].c_str(), dtm);
cout << "TM Break \tH:" << dtm->tm_hour << " is DST:" << dtm->tm_isdst << " GMT Off:" << dtm->tm_gmtoff << endl;
time_t ep_dt = mktime(dtm);
cout << "The epoch value: \t" << ep_dt << endl;
char buffer[40];
strftime(buffer, 40,"%x - %I:%M%p %z", dtm);
cout << "DateTime in String: \t" << buffer << endl;
delete dtm;
}
}
According to http://en.wikipedia.org/wiki/ISO_8601 your one and three digit timezone offsets are not valid ISO 8601 values (the form used by strptime at least on Linux), which requires hh[:][mm] as the format.
Might be late, but strptime does correctly parse the %z specifier, it stores it in tm.tm_gmtoff variable. This variable is not in the standard, but a gnu extension. mktime does not make use of this variable.
You can try
strptime(<time_string>, dtm);
std::cout << dtm->tm_gmtoff;
to see the parsed time offset.
Note .tm_gmtoff is in second, so to get the correct epoch time, you can do
auto offset = dtm->tm_gmtoff;
auto _epoch = mktime(dtm);
auto final_epoch = _epoch + offset
Sources
https://www.gnu.org/software/libc/manual/html_node/Broken_002ddown-Time.html
https://github.com/andikleen/glibc/blob/master/time/strptime_l.c#L749
The answer from #Nguyen Ha Kien helped me, but to get the right epoch time, I needed to factor in my local UTC offset too, after it's been filled in by mktime, and not override libc's determination of whether DST was active at the specified time here:
int offset = dtm->tm_gmtoff;
// Ask libc to work out whether the local timezone is in DST at this time.
dtm->tm_isdst = -1;
time_t ep_dt = mktime(dtm);
ep_dt -= offset - dtm->tm_gmtoff;
int rather than auto just because I'm compiling on a 2011 era setup, hopefully like that used by pt123 originally, to show that the answer from Mark B gets strptime to parse tm_gmtoff for me:
(ia32)martind#sirius:~/playpen$ ls /lib/libc-2.11.3.so
/lib/libc-2.11.3.so
(ia32)martind#sirius:~/playpen$ gcc -v
...
gcc version 4.4.5 (Debian 4.4.5-8)
(ia32)martind#sirius:~/playpen$ g++ c-strptime-ignores-timezone-when-parsing.cpp
(ia32)martind#sirius:~/playpen$ ./a.out
-----------2013-04-24T9:47:06+0400 - %Y-%m-%dT%H:%M:%S%z
TM Break H:9 is DST:0 GMT Off:14400
The only additional code was:
#include <iostream>
#include <string>
using namespace std;
void process_Timezone(string dateStrings[], string formatStrings[]);
void date_Test();
int main(){
date_Test();
}
Related
I want to get the date, month and year information from the string.
Example Date String: Thu, 30 Jul 2020 00:51:08 -0700 (PDT)
PDT here is for Pacific Daylight time. This string offset (-0700) can change based on system timezone when the file was created.
I need to write a c++ program to extract date, month and year from this string.
Any thoughts on how to go about this?
This is a story of evolution. The correct answer greatly depends on your current toolset (how modern it is). And even if it is completely modern, there are still better tools coming.
Homo habilis
In C++98 we could stand upright. And we had tools to scan ints out of arrays of chars. scanf was the tool to do this. This result was not type safe, but we could scan ints and strings and then reinterpret those values as the components of a date: year, month and day. This might look something like this:
#include <cstdio>
#include <cstring>
#include <iostream>
int
main()
{
using namespace std;
string s = "Thu, 30 Jul 2020 00:51:08 -0700 (PDT)";
char const* months[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun",
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
char wd[4] = {};
int d;
char mon[4] = {};
int y;
sscanf(s.c_str(), "%s %d %s %d", wd, &d, mon, &y);
int m;
for (m = 0; m < 12; ++m)
if (strcmp(months[m], mon) == 0)
break;
++m;
cout << y << '\n';
cout << m << '\n';
cout << d << '\n';
}
This outputs:
2020
7
30
Notes:
The " 00:51:08 -0700 (PDT)" is never even parsed. It could be parsed. But it is a lot more work.
There's no error checking. This might be a valid date or might not.
There's no type safety. The results are just ints and if you mix them up, it's a run-time error, not a compile-time error.
Neanderthal
Using C++98, there's also a popular but non-standard solution: strptime.
#include <time.h>
#include <iostream>
int
main()
{
using namespace std;
string s = "Thu, 30 Jul 2020 00:51:08 -0700 (PDT)";
tm tm;
strptime(s.c_str(), "%a, %d %b %Y %T", &tm);
cout << tm.tm_year + 1900 << '\n';
cout << tm.tm_mon + 1 << '\n';
cout << tm.tm_mday << '\n';
cout << tm.tm_hour << '\n';
cout << tm.tm_min << '\n';
cout << tm.tm_sec << '\n';
}
strptime is in the POSIX standard, but not in the C or C++ standards. It is also supported by MS Visual Studio. So it is a popular extension. And with good reason. It is much higher level, and puts the results into a struct tm: A type representing a date/time; the beginnings of type safety.
Output:
2020
7
30
0
51
8
There are still some problems:
" -0700 (PDT)" is never parsed. There's no way to ask strptime to do this.
There are weird and inconsistent offsets on the different fields of tm. For example the month is zero-based and the day is one-based. But at least it knows how to parse the time too, and relatively easily.
Error checking is there but easy to ignore. strptime returns NULL if something bad happens.
Cro-Magnon
With C++11 arrived an actual C++ wrapper around strptime that was officially recognized by the C++ standard with std::get_time:
#include <iomanip>
#include <iostream>
#include <sstream>
int
main()
{
using namespace std;
string s = "Thu, 30 Jul 2020 00:51:08 -0700 (PDT)";
istringstream in{s};
in.exceptions(ios::failbit);
tm tm;
in >> get_time(&tm, "%a, %d %b %Y %T");
cout << tm.tm_year + 1900 << '\n';
cout << tm.tm_mon + 1 << '\n';
cout << tm.tm_mday << '\n';
cout << tm.tm_hour << '\n';
cout << tm.tm_min << '\n';
cout << tm.tm_sec << '\n';
}
With a C++ wrapper you can parse from streams, which gives you access to throwing an exception on parse failure. But it is still a simple wrapper and so the result is just a tm. This has the same weirdness as the previous solution.
The output is the same as in the previous solution:
2020
7
30
0
51
8
Homo sapiens
Though the strongly typed std::chrono time_point / duration system was introduced in C++11, it is not until C++20 that it is integrated with the civil calendar, gaining get_time-like functionality, and going far beyond that.
#include <chrono>
#include <iostream>
#include <sstream>
int
main()
{
using namespace std;
using namespace std::chrono;
string s = "Thu, 30 Jul 2020 00:51:08 -0700 (PDT)";
istringstream in{s};
in.exceptions(ios::failbit);
local_seconds t;
in >> parse("%a, %d %b %Y %T %z (%Z)", t);
auto td = floor<days>(t);
year_month_day ymd{td};
hh_mm_ss hms{t-td};
cout << ymd << ' ' << hms << '\n';
cout << ymd.year() << '\n';
cout << ymd.month() << '\n';
cout << ymd.day() << '\n';
cout << hms.hours() << '\n';
cout << hms.minutes() << '\n';
cout << hms.seconds() << '\n';
}
Output:
2020-07-30 00:51:08
2020
Jul
30
0h
51min
8s
The first thing to notice is the much stronger type-safety. No longer is there a need to convert everything to ints to print it out. And no longer is it necessary to convert to ints to do other operations such as arithmetic and comparison.
For example ymd.year() has type std::chrono::year, not int. If necessary, one can explicitly convert between these two representations. But it is generally unnecessary, and akin to a risky reinterpret_cast.
There are no longer unintuitive biases such as 1900, or zero-based counts in unexpected places.
Output generally includes the units for easier debugging.
The " -0700 (PDT)" is parsed here! These values are not used in the results, but they must be there, else there is a parse error. And if you want to get these values, they are available with very simple changes:
string abbrev;
minutes offset;
in >> parse("%a, %d %b %Y %T %z (%Z)", t, abbrev, offset);
...
cout << offset << '\n';
cout << abbrev << '\n';
Now the output includes:
-420min
PDT
If you need the fields in UTC, instead of in local time, that is one simple change:
sys_seconds t;
instead of:
local_seconds t;
Now the offset is subtracted from the parsed time point to result in a UTC time_point (a std::chrono::time_point based on system_clock) instead and the output changes to:
2020-07-30 07:51:08
2020
Jul
30
7h
51min
8s
This allows you to easily parse local times plus offset directly into system_clock::time_point.
Though not shipping yet (as I write this), vendors are working on implementing this. And in the meantime you can get this functionality with a free, open-source, header-only C++20 <chrono> preview library that works with C++11/14/17. Just add #include "date/date.h" and using namespace date; and everything just works. Though with C++11/14 you will need to substitute hh_mm_ss<seconds> hms{t-td}; for hh_mm_ss hms{t-td}; (lack of CTAD).
#include <time.h>
char *strptime(const char *buf, const char *format, struct tm *tm);
I have a collection of unix timestamps I am converting to boost (1.65.1) dates but the conversions seem to break down when they get too far in the future. Anything around 2040 and beyond seems to be wrapping in some way back to post 1900.
Given the following code...
{
std::time_t t = 1558220400;
boost::gregorian::date date = boost::posix_time::from_time_t(t).date();
std::cout << "Date: " << date << std::endl;
}
{
std::time_t t = 2145500000;
boost::gregorian::date date = boost::posix_time::from_time_t(t).date();
std::cout << "Date: " << date << std::endl;
}
{
std::time_t t = 2500000000;
boost::gregorian::date date = boost::posix_time::from_time_t(t).date();
std::cout << "Date: " << date << std::endl;
}
... I get the following output...
Date: 2019-May-18
Date: 2037-Dec-27
Date: 1913-Feb-13
... however I am expecting the following output...
Expected output:
Date: 2019-May-18
Date: 2037-Dec-27
Date: 2049-Mar-22
Is there something I am doing wrong here?
It appears that you're experiencing the Year 2038 problem.
The largest number representable by 32 bit signed integer is 2'147'483'647. 2'147'483'647 seconds since 00:00:00 UTC on 1st of January 1970 (the UNIX epoch) is 03:14:07 UTC on 19th of January 2038. Any UNIX time after that is unrepresentable using a 32 bit signed integer.
Either std::time_t on the system is 32 bits, or it is converted into 32 bits inside the boost library. You can see from the source that boost converts the input into long using static_cast (and still does in version 1.70). long is 32 bits for example on windows, even on 64 bit architectures. It is 64 bits on many other systems such as 64 bit Linux.
In C++20 this can now look like:
#include <chrono>
#include <iostream>
int
main()
{
using namespace std::chrono;
{
std::time_t t = 1558220400;
auto date = floor<days>(system_clock::from_time_t(t));
std::cout << "Date: " << date << '\n';
}
{
std::time_t t = 2145500000;
auto date = floor<days>(system_clock::from_time_t(t));
std::cout << "Date: " << date << '\n';
}
{
std::time_t t = 2500000000;
auto date = floor<days>(system_clock::from_time_t(t));
std::cout << "Date: " << date << '\n';
}
}
Output:
Date: 2019-05-18
Date: 2037-12-27
Date: 2049-03-22
If your time_t is 32 bits, then the above isn't quite sufficient to fix the problem. In that case, you must avoid the C API completely. This looks like:
{
auto t = 1558220400;
auto date = floor<days>(sys_seconds{seconds{t}});
std::cout << "Date: " << date << '\n';
}
{
auto t = 2145500000;
auto date = floor<days>(sys_seconds{seconds{t}});
std::cout << "Date: " << date << '\n';
}
{
auto t = 2500000000;
auto date = floor<days>(sys_seconds{seconds{t}});
std::cout << "Date: " << date << '\n';
}
If your vendor isn't shipping this part of C++20 yet, a free, open-source preview that works with C++11/14/17 is available.1
Just add:
#include "date/date.h"
...
using namespace date;
1 Full disclosure: I am the lead author of this library. I am not pursuing any financial gain from this effort. But sometimes people get grumpy if I don't fully disclose this information.
As noted by eerorika this is caused by integer overflow since the boost::posix_time::from_time_t is casting the 64bit time_t value to a 32 bit long (on Windows).
If you are in a pinch and find yourself in the same position then you can use the following function to perform the conversion:
boost::gregorian::datetimet_to_date(time_t t)
{
auto time = boost::posix_time::ptime(boost::gregorian::date(1970,1,1));
int64_t current_t = t;
long long_max = std::numeric_limits<long>::max();
while(current_t > 0)
{
long seconds_to_add = 0;
if(current_t >= long_max)
seconds_to_add = long_max;
else
seconds_to_add = static_cast<long>(current_t);
current_t -= seconds_to_add;
time += boost::posix_time::seconds(seconds_to_add);
}
return time.date();
}
What's a standard way to get a date time in ISO8601 format on Windows using C++? Specifically, I would like it to be formatted as:
2017-02-22T10:00:00.123-05:00
2017-02-22T10:00:00.123 >>> -05:00 <<< # how to print the offset?
I was looking into combining the output of GetLocalTime and GetTimeZoneInformation, but this looks esoteric. There are similar questions on SO, however, I've not found a single one that prints UTC offset in a desired format. Is there a better approach?
The format specifier %z gives you the timezone offset as described in the documentation (e.g. MSDN on strftime) but lefts out the ':'. You can use it like this to get the ':' into your string:
struct tm tmNow;
time_t now = time(NULL); // Get the current time
_localtime64_s(&tmNow, &now);
char bufferTime[26];
char bufferTimezoneOffset[6];
size_t tsizTime = strftime(bufferTime, 26, "%Y-%m-%dT%H:%M:%S", &tmNow); // The current time formatted "2017-02-22T10:00:00"
size_t tsizOffset = strftime(bufferTimezoneOffset, 6, "%z", &tmNow); // The timezone offset -0500
strncpy_s(&bufferTime[tsizTime], 26, bufferTimezoneOffset, 3); // Adds the hour part of the timezone offset
bufferTime[tsizTime + 3] = ':'; // insert ':'
strncpy_s(&bufferTime[tsizTime + 4], 26, &bufferTimezoneOffset[3], 3); // Adds the minutes part of the timezone offset
puts(bufferTime); // Your output: "2017-02-22T10:00:00-05:00"
I left out the milliseconds, as they are not part of the localtime as far as I know.
Maybe something like this. We call GetLocalTime and GetTimeZoneInformation then pass it to the function which returns formatted string.
This is written quickly, not tested besides observing the fact it returns correct result on my machine now. It operates on the fact that SYSTEMTIME has a member Bias where UTC = Localtime + Bias and Bias is set in minutes. So get hours by dividing by 60 and taking absolute value of that. Then we get the minutes in similar way and set the sign depending on if Bias > 0
#include <Windows.h>
#include <string>
#include <sstream>
#include <iomanip>
#include <cmath>
std::string format_system_time(const SYSTEMTIME& sys_time, const TIME_ZONE_INFORMATION& time_zone)
{
std::ostringstream formatted_date_time;
formatted_date_time << std::setfill('0');
formatted_date_time << sys_time.wYear << "-" << std::setw(2) << sys_time.wMonth << "-" <<
std::setw(2) << sys_time.wDay << "T" << std::setw(2) << sys_time.wHour << ":" <<
std::setw(2) << sys_time.wMinute << ":" << std::setw(2) << sys_time.wSecond << "." <<
std::setw(3) << sys_time.wMilliseconds;
//UTC = localtime + bias; bias is in minutes
int utc_offset_hours = time_zone.Bias / 60;
int utc_offset_minutes = std::abs(time_zone.Bias - (utc_offset_hours * 60));
char offset_sign = time_zone.Bias > 0 ? '-' : '+';
formatted_date_time << offset_sign << std::setw(2) << std::abs(utc_offset_hours) << ":" << utc_offset_minutes;
return formatted_date_time.str();
}
int main(int argc, char* argv[])
{
SYSTEMTIME date_and_time;
GetLocalTime(&date_and_time);
TIME_ZONE_INFORMATION time_zone;
GetTimeZoneInformation(&time_zone);
auto& formatted_date_time = format_system_time(date_and_time, time_zone);
return 0;
}
I don't think there is a drop-in solution for c++ on Windows. The closest you can get is InternetTimeFromSystemTime but it is only documented to support RFC1123.
You probably have to code it yourself with GetLocalTime + GetTimeZoneInformation + wsprintf (or GetTimeZoneInformationForYear if you are not dealing with the current time).
Using Howard Hinnant's free, open-source timezone library, which works on VS-2013 and later, but does require some installation:
#include "tz.h"
#include <iostream>
int
main()
{
using namespace std;
using namespace std::chrono;
using namespace date;
auto zt = make_zoned(current_zone(), floor<milliseconds>(system_clock::now()));
cout << format("%FT%T%Ez\n", zt);
}
This just output for me:
2017-02-22T17:29:03.859-05:00
I've problem with use of the std::localtime function. When I transform a std::time_t to a local struct tm, it always use the american daylight saving time whereas I want to use the european one (France).
UTC : 03/19/16 16:56 is transformed in Local : 03/19/16 18:56.
At this date, normally, local is 17:56 (UTC+1). The DST happens on the 27th in France.
After several tests, it seems that the used DST is based on the american rule : DST happens on second sunday in march.
I've also changed the TZ environement variable, but it also fails.
`
if( putenv( "TZ=CET-1CEST-2,M3.5.0/2,M10.5.0/3" ) != 0 ) {
std::cout << "Unable to set TZ" << std::endl;
} else {
tz = getenv("TZ");
if (tz) std::cout << tz << std::endl;
else std::cout << "TZ not defined" << std::endl; tzset();
}
struct std::tm t;
t.tm_sec = 0;
t.tm_min = 56;
t.tm_hour = 18;
t.tm_mday = 19;
t.tm_mon = 3-1;
t.tm_year = 2016-1900;
t.tm_isdst = -1;
std::time_t tt = std::mktime(&t);
std::cout << "UTC: " << std::put_time(std::gmtime(&tt), "%c %Z") << '\n'; // UTC : 03/19/16 16:56
std::cout << "local: " << std::put_time(std::localtime(&tt), "%c %Z") << '\n'; // Local : 03/19/16 18:56 (not waited result)
`
As a precision, I use the bcc32c compiler (the embarcadero C++ clang based computer).
I hope I'm clear enough and you'll be able to help me.
Thanks in advance
If you have C++11 (or later) available, which includes <chrono>, and if you are willing to work with the <chrono> system. And if you are willing to use this free, open-source timezone library. Then this is a very simple matter:
#include "tz.h"
#include <iostream>
int
main()
{
using namespace date;
using namespace std::chrono_literals;
auto zt = make_zoned("Europe/Paris", sys_days{2016_y/mar/19} + 18h + 56min);
std::cout << format("%m/%d/%y %H:%M %Z", zt.get_sys_time()) << '\n';
std::cout << format("%m/%d/%y %H:%M %Z", zt) << '\n';
}
Output:
03/19/16 18:56 UTC
03/19/16 19:56 CET
In short, you form one zoned_time by pairing a timezone of your choosing
("Europe/Paris"), with a sys_time (UTC). Then you can format that zoned_time both by extracting the sys_time back out of it, and by formatting the zoned_time itself, which will use the local_time of the zoned_time.
I attempted to use formatting that was consistent with your comments. You could of course you any format you want. You can also include any std::locale your system supports as the first argument in the calls to format (which %c would take advantage of).
You can also extract individual fields both from the sys_time and from the local_time if you need to, or do other date or time computations. This is more than just a formatting library.
If you are starting with a time_t, there is a static member function of std::chrono::system_clock to convert a time_t to a system_clock::time_point. You can then use that time_point in place of sys_days{2016_y/mar/19} + 18h + 56min in this example:
auto zt = make_zoned("Europe/Paris", system_clock::from_time_t(t));
I am trying to save std::time_point into a std::stream and read it back. One problem is that using the standard functions 'loses' an hour somewhere. I.e., the time I read is 1 hour behind the time I write. I suspect that I need to set up daylight saving somewhere. I put together a small program that prints time to a std::stringstream and reads it back.
#include <iomanip>
#include <iostream>
#include <sstream>
#include <chrono>
#include <ctime>
using std::chrono::system_clock;
namespace chrono = std::chrono;
void test();
int main(int argc, char** argv)
{
std::stringstream ss;
auto start = system_clock::now();
std::time_t ts = system_clock::to_time_t(start);
std::tm time_out = *std::localtime(&ts);
ss << std::put_time(&time_out, "%Y-%m-%d %H:%M:%S %Z") << '\n';
std::cout << ss.str() << std::endl;
std::tm time_in;
ss >> std::get_time(&time_in, "%Y-%m-%d %H:%M:%S %Z");
std::cout << "Are time dsts equal? : " <<
(time_out.tm_isdst == time_in.tm_isdst) << '\n';
std::time_t rawTime = std::mktime(&time_in);
auto end = std::chrono::system_clock::from_time_t(rawTime);
std::cout << "Are time points equal? : " << (start == end) << '\n';
// print the trouble makers
std::time_t start_time = system_clock::to_time_t(start);
std::time_t end_time = system_clock::to_time_t(end);
std::cout << "times: \n"
<< '\t' << std::put_time(std::localtime(&start_time), "%c %z") << '\n'
<< '\t' << std::put_time(std::localtime(&end_time), "%c %z") << '\n';
// this is a source of strange behaviour...
// std::cout << "Difference: "
// << chrono::duration_cast<chrono::seconds>(start - end).count()
// << std::endl;
return 0;
}
The strangest thing is that the program prints the following:
Are time dsts equal? : 1
Are time points equal? : 0
times:
Tue Dec 11 19:26:24 2012 +0000
Tue Dec 11 19:26:24 2012 +0000
And when I uncomment the 3 lines at the end of the program (printing the difference between the time points) the result is:
Are time dsts equal? : 0
Are time points equal? : 0
times:
Tue Dec 11 19:29:40 2012 +0000
Tue Dec 11 18:29:40 2012 +0000
Difference: 3600
Notice that dst(daylight saving time) is suddenly not equal and neither are the times.
I am using libc++ on Mac OS X 10.8.2 with XCode46-DP2. The clang++ versions I am using are Apple clang version 4.1 and clang version 3.2 (trunk 167239)
I guess my questions are:
A) As to the 1 hour difference, is this a bug in my library or am I not using the standard functions correctly? (The latter would not surprise me...)
B) What is going on with the code when I uncomment the three lines at the end of my program? This looks like a bug to me. Anyone care to try it on their platform?
I think we're looking at a bug with the %Z specifier, and maybe with the %z specifier as well, not sure yet.
I will look into the cause of these bugs more. However I wanted to go ahead and post to get you a workaround. I believe if you zero-initialize your input tm, and always assume it is with respect to your local timezone, then you will eliminate your errors:
std::tm time_in{0};