I'm trying to convert a human readable time string which has been set to a specific timezone with DST (e.g Tehran +4.30/3.30) to UTC timestamp. I'm using ARM mbed-os platform on an STM device which has a lack of some C time functions like strptime.
Anyway, I converted the time string to a timestamp but I can't figure it out how to change it to UTC. The string time is set for specific timezone which has DST so I can't simply add/remove the timezone gap.
I also prefer to get timezone as an argument for my current_time function, so I can use it with different timezones.
time_t current_time(void)
{
time_t epoch = 0;
parser.send("AT+CCLK?");
string resp = read_until("OK\r\n");
//result would be something like this:
/*
+CCLK: "19/06/23,19:33:42+18"
OK
*/
// extract time string
unsigned int pos = resp.find("+CCLK: \"");
unsigned int pos2 = resp.rfind("\"\r\n");
if ((pos != string::npos) && (pos2 != string::npos))
{
string time_str = resp.substr(pos + 8, pos2 - pos - 11);
//convert to timestamp
int hh, mm, ss, yy, mon, day;
struct tm when = {0};
sscanf(time_str.c_str(), "%d/%d/%d,%d:%d:%d", &yy, &mon, &day, &hh, &mm, &ss);
when.tm_hour = hh;
when.tm_min = mm;
when.tm_sec = ss;
when.tm_year = 2000 + yy - 1900;
when.tm_mon = mon - 1;
when.tm_mday = day;
epoch = mktime(&when);
// below doesn't work all time because we have DST
// add 3.30 (+1.00 daylight) offset to get UTC
epoch = epoch - (time_t)((4 * 3600) + (30 * 60));
}
return epoch;
}
Try:
when.tm_isdst = -1;
prior to calling mktime, and removing the epoch UTC offset adjustment.
Related
I am trying to add days to a formatted date in C++, but without any success.
The date is passed as a SYSTEMTIME type, and days to add in long type.
In the following code example i am adding the days in a date converted to long, and this is wrong, i am using this just as an example.
long FormatDate(SYSTEMTIME* cStartTime, long daysToAdd)
{
UCHAR szToday[16];
sprintf((char*)szToday, "%04d%02d%02d", cStartTime->wYear, cStartTime->wMonth, (cStartTime->wDay));
long finalDate = atol((char*)szToday) + daysToAdd // e.g. if szToday is "20210601" and daysToAdd is 10, then finalDate is 20210611
return finalDate;
}
Thanks.
After some search and debugging i am using the following code, and it's working.
Note that hour, minute, second and millisecond from CustomDate must be set, otherwise it won't work.
In this scenario i'm adding seconds, so it could be more generic. So when i need to convert to days i do this: daysToAdd * 24 * 60 * 60.
SYSTEMTIME AddSeconds(SYSTEMTIME s, INT64 seconds) {
FILETIME f;
SystemTimeToFileTime(&s, &f);
(INT64&)f += seconds * 10000000L;
FileTimeToSystemTime(&f, &s);
return s;
}
void Func()
{
INT64 daysToAdd = 15;
SYSTEMTIME customDate;
customDate.wYear = 2021;
customDate.wMonth = 1;
customDate.wDay = 1;
customDate.wHour = 0;
customDate.wMinute = 0;
customDate.wSecond = 0;
customDate.wMilliseconds = 0;
INT64 secondsToAdd = daysToAdd * 24 * 60 * 60;
SYSTEMTIME finalDate = AddSeconds(customDate, secondsToAdd);
}
I need to take the current local time, including milliseconds, and pass it to some embedded device. This device has now idea about calendar time, but has its own timer with 1 ms accuracy. So, when this device receives the current timestamp, it opens log file and writes this timestamp to the beginning. From now, it writes different messages to the log, each one with number of milliseconds elapsed from this initial time. Finally, embedded device log file is uploaded to the host and should be parsed, with all relative time intervals converted back to full calendar time. The first part in the host program looks like this:
struct timestamp
{
int year; // 0-based
int month; // [1-12]
int day; // [1-31]
int hour; // [0-23]
int minute; // [0-59]
int sec; // [0-59]
int ms; // [0-999]
};
timestamp time_point_to_timestamp(std::chrono::time_point<std::chrono::system_clock> tp)
{
auto seconds = std::chrono::time_point_cast<std::chrono::seconds>(tp);
auto fraction = tp - seconds;
auto milliseconds = std::chrono::duration_cast<std::chrono::milliseconds>(fraction);
time_t tt = std::chrono::system_clock::to_time_t(tp);
tm* ptm = localtime(&tt);
timestamp t;
t.year = ptm->tm_year + 1900;
t.month = ptm->tm_mon + 1;
t.day = ptm->tm_mday;
t.hour = ptm->tm_hour;
t.minute = ptm->tm_min;
t.sec = ptm->tm_sec;
t.ms = static_cast<int>(milliseconds.count());
return t;
}
void start()
{
timestamp ts = time_point_to_timestamp(std::chrono::system_clock::now());
// send ts to embedded device
// ...
}
Now, when I get the log from device back to the host, it looks like this:
2018 6 24 8 25 52 598 // start time ts
500 message 1 // ms elapsed from ts
2350 message 2 // ms elapsed from ts
...
I need to parse this file and convert every message, printing its full date and time. For example, 500 will be converted to:
2018 6 24 8 25 53 098
So, I need some way to convert timestamp to any C++ type, that allows to add time intervals to it (time_point, duration?), and print it in human readable form. My compiler supports C++14.
I'd do this:
int64_t to_epoch_ms(time_point<system_clock> tp)
{
return duration_cast<milliseconds>(tp.time_since_epoch()).count();
}
Then pass the milliseconds since epoch to the device, where it can be logged as e.g. 1529819166927. Adding milliseconds is trivial and fast, whether you do it directly using the int64_t or by converting back to a time_point:
time_point<system_clock> from_epoch_ms(int64_t ms)
{
return {milliseconds(ms)};
}
auto tp1 = from_epoch_ms(ms + 123);
auto tp1 = from_epoch_ms(ms) + milliseconds(456);
Bit of a c++ newbie so here we go;
I have a method that is parsing a date/time, however that date/time is passed to me always with 00:00:00 as the hh:mm:ss. As such i want to add in the values of the current systime in place of those values. I have methods that do this, and the first method is returning the correct time in UTC format.
bool CTRHTranslationRTNS::ParseDateSysTime(const char* pszString, time_t& tValue)
{
ASSERT(pszString != NULL);
// DateTime fields.
enum { YEAR, MONTH, DAY, HOURS, MINS, SECS, NUM_FIELDS };
CStringArray astrFields;
// Split the string into the date and time fields.
int nFields = CStringParser::Split(pszString, "- :T", astrFields);
// Not DD/MM/YYYY HH:MM:SS format.
if (nFields != NUM_FIELDS)
return false;
int anFields[NUM_FIELDS] = { 0 };
// Parse field numbers.
for (int i = 0; i < NUM_FIELDS; ++i)
anFields[i] = atoi(astrFields[i]);
tm oTime = { 0 };
//Add System Time instead
time_t sysyemTimeNow;
struct tm * ptm;
time ( &sysyemTimeNow );
ptm = gmtime ( &sysyemTimeNow );
// Copy fields to time struct.
oTime.tm_mday = anFields[DAY];
oTime.tm_mon = anFields[MONTH] - 1;
oTime.tm_year = anFields[YEAR] - 1900;
oTime.tm_hour = ptm->tm_hour;
oTime.tm_min = ptm->tm_min;
oTime.tm_sec = ptm->tm_sec;
oTime.tm_isdst = -1;
// Convert to time_t.
tValue = mktime(&oTime);
// Invalid field values.
if (tValue < 0)
return false;
return true;
}
In the second method I do some formatting on the date/time and this results in 2 hours being removed from the time.
string CTRHTranslationRTNS::ConvertDateSysTimeToDateInUTC(const string& bossDate)
{
time_t dealDate;
if (ParseDateSysTime(bossDate.c_str(), dealDate))
{
struct tm * ptm = gmtime(&dealDate);
char buffer [80];
strftime(buffer,80,"%Y-%m-%d %H:%M:%S",ptm);
return string(buffer);
}
else
{
throw exception(string("Invalid date/SysTime value: ").append(bossDate).c_str());
}
}
Just to be clear, the ParseDateSysTime method returns the time with the correct UTC value of 11:53, but as soon as
struct tm * ptm = gmtime(&dealDate);
is called the time changes to 08:53. It suggests this is a product of calling the gmtime() method but i am not sure.
Many Thanks
Graham
The reson is the mktime() method used in the first function uses local time, but gmtime() uses UTC time.
See http://www.cplusplus.com/reference/clibrary/ctime/mktime/ and http://www.cplusplus.com/reference/clibrary/ctime/gmtime/ for further explanation.
Try this function:
CTime Time2UTC(CTime original)
{
CString Formatted = original.FormatGmt(L"%Y%m%d%H%M%S");
int Year, Month, Day, Hour, Minute;
if (Formatted != L"" && Formatted.GetLength() >= 12)
{
Year = _wtol(Formatted.Left(4));
Month = _wtol(Formatted.Mid(4, 2));
Day = _wtol(Formatted.Mid(6,2));
Hour = _wtol(Formatted.Mid(8, 2));
Minute = _wtol(Formatted.Mid(10, 2));
CTime result(Year, Month, Day, Hour, Minute, 0);
return result;
}
else
return (CTime)NULL;
}
Basically I'm looking for a standard way to convert a string like 2014/08/29-11:42:05.042 into a time_point object. I know how to do it with boost, but can it be done with STL libraries only? How?
It would be great if if could specify the format like %y/%m/%d-%H:%M:%S.%f or something alike.
Ok, at least for a fixed format with millisecond resolution this works. Attempting to make this code capable of accepting any string format will be like reinventing the wheel (i.e. there are functions for all this in Boost.
std::chrono::system_clock::time_point string_to_time_point(const std::string &str)
{
using namespace std;
using namespace std::chrono;
int yyyy, mm, dd, HH, MM, SS, fff;
char scanf_format[] = "%4d.%2d.%2d-%2d.%2d.%2d.%3d";
sscanf(str.c_str(), scanf_format, &yyyy, &mm, &dd, &HH, &MM, &SS, &fff);
tm ttm = tm();
ttm.tm_year = yyyy - 1900; // Year since 1900
ttm.tm_mon = mm - 1; // Month since January
ttm.tm_mday = dd; // Day of the month [1-31]
ttm.tm_hour = HH; // Hour of the day [00-23]
ttm.tm_min = MM;
ttm.tm_sec = SS;
time_t ttime_t = mktime(&ttm);
system_clock::time_point time_point_result = std::chrono::system_clock::from_time_t(ttime_t);
time_point_result += std::chrono::milliseconds(fff);
return time_point_result;
}
std::string time_point_to_string(std::chrono::system_clock::time_point &tp)
{
using namespace std;
using namespace std::chrono;
auto ttime_t = system_clock::to_time_t(tp);
auto tp_sec = system_clock::from_time_t(ttime_t);
milliseconds ms = duration_cast<milliseconds>(tp - tp_sec);
std::tm * ttm = localtime(&ttime_t);
char date_time_format[] = "%Y.%m.%d-%H.%M.%S";
char time_str[] = "yyyy.mm.dd.HH-MM.SS.fff";
strftime(time_str, strlen(time_str), date_time_format, ttm);
string result(time_str);
result.append(".");
result.append(to_string(ms.count()));
return result;
}
To test it I tried like this and made sure that the string correctly represented the current date time:
auto tp_src = system_clock::now();
string value = time_point_to_string(tp_src);
auto tp_cnv = string_to_time_point(value);
auto error = duration_cast<milliseconds>(tp_src - tp_cnv).count();
Assert::IsTrue(error == 0);
Bit of a c++ newbie so here we go;
I have a method that is parsing a date/time, however that date/time is passed to me always with 00:00:00 as the hh:mm:ss. As such i want to add in the values of the current systime in place of those values. I have methods that do this, and the first method is returning the correct time in UTC format.
bool CTRHTranslationRTNS::ParseDateSysTime(const char* pszString, time_t& tValue)
{
ASSERT(pszString != NULL);
// DateTime fields.
enum { YEAR, MONTH, DAY, HOURS, MINS, SECS, NUM_FIELDS };
CStringArray astrFields;
// Split the string into the date and time fields.
int nFields = CStringParser::Split(pszString, "- :T", astrFields);
// Not DD/MM/YYYY HH:MM:SS format.
if (nFields != NUM_FIELDS)
return false;
int anFields[NUM_FIELDS] = { 0 };
// Parse field numbers.
for (int i = 0; i < NUM_FIELDS; ++i)
anFields[i] = atoi(astrFields[i]);
tm oTime = { 0 };
//Add System Time instead
time_t sysyemTimeNow;
struct tm * ptm;
time ( &sysyemTimeNow );
ptm = gmtime ( &sysyemTimeNow );
// Copy fields to time struct.
oTime.tm_mday = anFields[DAY];
oTime.tm_mon = anFields[MONTH] - 1;
oTime.tm_year = anFields[YEAR] - 1900;
oTime.tm_hour = ptm->tm_hour;
oTime.tm_min = ptm->tm_min;
oTime.tm_sec = ptm->tm_sec;
oTime.tm_isdst = -1;
// Convert to time_t.
tValue = mktime(&oTime);
// Invalid field values.
if (tValue < 0)
return false;
return true;
}
In the second method I do some formatting on the date/time and this results in 2 hours being removed from the time.
string CTRHTranslationRTNS::ConvertDateSysTimeToDateInUTC(const string& bossDate)
{
time_t dealDate;
if (ParseDateSysTime(bossDate.c_str(), dealDate))
{
struct tm * ptm = gmtime(&dealDate);
char buffer [80];
strftime(buffer,80,"%Y-%m-%d %H:%M:%S",ptm);
return string(buffer);
}
else
{
throw exception(string("Invalid date/SysTime value: ").append(bossDate).c_str());
}
}
Just to be clear, the ParseDateSysTime method returns the time with the correct UTC value of 11:53, but as soon as
struct tm * ptm = gmtime(&dealDate);
is called the time changes to 08:53. It suggests this is a product of calling the gmtime() method but i am not sure.
Many Thanks
Graham
The reson is the mktime() method used in the first function uses local time, but gmtime() uses UTC time.
See http://www.cplusplus.com/reference/clibrary/ctime/mktime/ and http://www.cplusplus.com/reference/clibrary/ctime/gmtime/ for further explanation.
Try this function:
CTime Time2UTC(CTime original)
{
CString Formatted = original.FormatGmt(L"%Y%m%d%H%M%S");
int Year, Month, Day, Hour, Minute;
if (Formatted != L"" && Formatted.GetLength() >= 12)
{
Year = _wtol(Formatted.Left(4));
Month = _wtol(Formatted.Mid(4, 2));
Day = _wtol(Formatted.Mid(6,2));
Hour = _wtol(Formatted.Mid(8, 2));
Minute = _wtol(Formatted.Mid(10, 2));
CTime result(Year, Month, Day, Hour, Minute, 0);
return result;
}
else
return (CTime)NULL;
}