How to deal with timezone and Windows FILETIME in C++? - c++

I have a windows filetime (for example, 132522078890080000)
In Python I easily can convert it like that. Result is 1607716289 (Sat Dec 12 2020 00:51:29 GMT+0500 (Yekaterinburg Standard Time)). That's right!
But in C++ I tried to convert it like that
#include <iostream>
#include <ctime>
#define WINDOWS_TICK 10000000
#define SEC_TO_UNIX_EPOCH 11644473600LL
uint64_t WindowsTickToUnixSeconds(uint64_t windowsTicks)
{
return (uint64_t)(windowsTicks / WINDOWS_TICK - SEC_TO_UNIX_EPOCH);
}
int main() {
const uint64_t in_raw = 132522078890080000;
auto unix_timestamp = WindowsTickToUnixSeconds(in_raw);
time_t out = unix_timestamp;
std::cout << "Timestamp: " << unix_timestamp << std::endl;
std::cout << "Local: " << asctime(localtime(&out)) << std::endl;
std::cout << "GMT: " << asctime(gmtime(&out)) << std::endl;
return 0;
}
And result of that is
Timestamp: 1607734289
Local: Sat Dec 12 05:51:29 2020
GMT: Sat Dec 12 00:51:29 2020
As you can see the timestamps are different (1607734289, 1607716289, difference is 5 hours (because timezone is Asia/Yekaterinburg)).
I can easily subtract 5 hours but in that case it won't work in another timezone.
So how can I get correct timestamp?

Related

C++ - Calculate the millisecond from ptime in total seconds

How do i calculate the millisecond difference from the following Ptime ,I am using boost::ptime
I'm trying to calculate the time_duration in milliseconds to find the difference. i get value like 999975 but expected value is 975
ptime PreviousgpsTime = Mon Jun 28 17:07:10.054 2021
ptime NextgpsTime = Mon Jun 28 17:07:11.025 2021
double totalDiff = (NextgpsTime-PreviousgpsTime).total_milliseconds();
How to fix this and get the actual time duration.
Live On Coliru:
#include <boost/date_time/posix_time/posix_time.hpp>
int main()
{
using namespace boost::posix_time;
ptime PreviousgpsTime = time_from_string("2021-Jun-28 17:07:10.054");
ptime NextgpsTime = time_from_string("2021-Jun-28 17:07:11.025");
long totalDiff = (NextgpsTime - PreviousgpsTime).total_milliseconds();
std::cout << "From " << PreviousgpsTime << " to " << NextgpsTime << " is " << totalDiff << "ms\n";
}
Prints
From 2021-Jun-28 17:07:10.054000 to 2021-Jun-28 17:07:11.025000 is 971ms

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.

time_t to boost date conversion giving incorrect result

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();
}

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.

Converting 13 decimals to datetime

I have a project about cars with GPS. I need to return the start and the finish moment for each car.
So we have:
time_t x, y;
Because I will use later them for a transformation.
I have a problem. I read from an external file data in this format:
auto1
1439467747492
auto1
1439467748512
...etc.
auto1->name of the car;
1439467747492->the moment in time of the car
I tried to get the first position of the first moment and the last moment for each car. This is the code in C++:
long test = momenti[choice1]/1000;
time_t x = test;
cout << " Momentul initial:\n " << ctime(&x) << endl;
long test1 = momentf[choice1] / 1000;
time_t y = test1;
cout << " Momentul final:\n " << ctime(&y) << endl;
I receive the same date for every car. Is something like momenti[i]=momentf[i]
What did I do wrong?
It is not good. According epoch converter we should get this : GMT: Thu, 13 Aug 2015 12:09:07 GMT
Here is how you can get this output with C++11/14 and using this free, open source date library which extends the C++ <chrono> library to handle dates.
#include "date.h"
#include <chrono>
#include <iostream>
int
main()
{
using namespace std::chrono;
using namespace std;
using namespace date;
using time_point = std::chrono::time_point<system_clock, milliseconds>;
auto tp = time_point{1439467747492ms};
auto dp = floor<days>(tp);
auto time = make_time(tp - dp);
auto ymd = year_month_day{dp};
cout << "GMT: " << weekday{dp} << ", " << ymd.day() << ' ' << ymd.month()
<< ' ' << ymd.year() << ' ' << time << " GMT\n";
}
Output:
GMT: Thu, 13 Aug 2015 12:09:07.492 GMT
I threw in the fractional seconds for fun, and it seemed a shame to waste them (the C lib won't give them to you). If you really don't want them, it is easy to fix:
auto time = make_time(floor<seconds>(tp) - dp);
Now the output is:
GMT: Thu, 13 Aug 2015 12:09:07 GMT
You need C++14 for the 1439467747492ms above. If you only have C++11 you can sub in this instead: milliseconds{1439467747492}. If you only have C++03, then you are 13 years behind the times and stuck with ctime. ;-)
The chrono solution will offer you greater type safety, more flexibility, and greater performance.
If i can fix and the latitude and longitude problem would be great lol
If you can translate latitude and longitude into an IANA timezone name (and there are tools to do this), I've got a IANA timezone database parser for you which interoperates with <chrono> and "date.h".
#include <iostream>
#include <cstring>
#include <time.h>
using namespace std;
int main()
{
long test = 1439467747492;
time_t x = test;
cout << ctime( &x ) << endl;
return 0;
}
Produces
Tue Sep 18 20:15:32 1990