C++ Time returned is two hours out - c++

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

Related

Add days to date in C++

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

Convert a time string with DST to UTC timestamp

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.

mktime subtracts an hour from my dates [duplicate]

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

check file's LastWriteTime is yesterday by SYSTEMTIME

I want to check lots of files which the LastWriteTimes are the same as the date of the system's yesterday or not. My question is how to set the date-time of yesterday in the SYSTEMTIME format.
The following is my code.
bool checkLastWriteTime(FILETIME ftLastWriteTime)
{
SYSTEMTIME stUTC, stLocal;
// Convert the last-write time to local time.
FileTimeToSystemTime(&ftLastWriteTime, &stUTC);
SystemTimeToTzSpecificLocalTime(NULL, &stUTC, &stLocal);
// Build a string showing the date and time.
_tprintf(TEXT("%02d/%02d/%d %02d:%02d\n"),
stLocal.wMonth, stLocal.wDay, stLocal.wYear,
stLocal.wHour, stLocal.wMinute);
SYSTEMTIME localTime;
GetLocalTime(&localTime);
//
//How to get the date of yesterday from localTime?
//
if (stLocal.wYear == localTime.wYear && stLocal.wMonth == localTime.wMonth && stLocal.wDay == localTime.wDay)
{
return true;
}
else
{
return false;
}
}
"Yesterday" is a range. It is pretty easy to calculate when it ended, that happened at 12 am this morning:
SYSTEMTIME now;
GetLocalTime(&now);
SYSTEMTIME stYesterdayEnd = { now.wYear, now.wMonth, now.wDayOfWeek, now.wDay };
FILETIME ftYesterdayEnd;
SystemTimeToFileTime(&stYesterdayEnd, &ftYesterdayEnd);
It started 24 hours before that. So you need to subtract as many 100 nanosecond units from ftYesterdayEnd. A bit tricky with FILETIME, we'll use a LARGE_INTEGER to make it easy:
LARGE_INTEGER liYesterdayBeg = { ftYesterdayEnd.dwLowDateTime, ftYesterdayEnd.dwHighDateTime };
ULONGLONG oneday = 24ULL * 60 * 60 * 1000 * 1000 * 10;
liYesterdayBeg.QuadPart -= oneday;
FILETIME ftYesterdayBeg = { liYesterdayBeg.LowPart, liYesterdayBeg.HighPart };
Now you're set to write your function:
bool wasWrittenYesterday(FILETIME ftLastWriteTime)
{
// As above
//...
return ftLastWriteTime >= ftYesterdayBeg && ftLastWriteTime < ftYesterdayEnd
}
You need to convert SYSTEMTIME to FILETIME, then subtract the day and reconvert to SYSTEMTIME.
Something like this:
FILETIME ft;
ULARGE_INTEGER uli;
__int64 oneDay;
GetLocalTime(&localTime);
SystemTimeToFileTime(&localTime, &ft);
memcpy(&uli, (ULARGE_INTEGER *)&ft, sizeof(ULARGE_INTEGER));
// get one day in 100ns parts
oneDay = (__int64)10 * (__int64)1000 * (__int64)1000;
oneDay *= (__int64)60 * (__int64)60 * (__int64)24;
// subtract one day
uli.QuadPart -= oneDay;
FileTimeToSystemTime((LPFILETIME)&uli, &localTime);
After running this code localTime contains now minus 24 hours.
See also Remarks within SYSTEMTIME reference

construct date in a different locale than current

I have a requirement, where a datetime, locale and daylight savings is input as fixed length string, such as YYMMDDHHMMCY, where the legend is provided below.
YY(year)
MM(month)
DD(day)
HH(hour)
MM(minute)
Timezone (C for central, P for Pacific, M for mountain, E for eastern)
Daylight savings (Y if daylight savings is in effect, other wise N)
What is needed is the ability to construct time in a specified timezone and then convert it to local timezone in C/C++. We dont use Boost, are there existing functions which would allow for the requirements. I am aware of strptime, which after some massaging the data, i can use it, but i am wondering if there are functions as described above which would allow me to construct a struct in a specified locale.
Use sscanf() or strptime() to extract most of the fields. The timezone character and DST character will need to be decoded on their own. Since you are using only a 2 digit year, your need to define your range. Example below uses 1970-2069. Use the extracted timezone character to form the usual timezone name. Before calling mktime(), set TZ to the timezone name. Then, with a time_t in hand, convert to your local time.
#include <stdlib.h>
#include <stdio.h>
#include <time.h>
extern time_t mktime_TZ(struct tm *tm, const char *tz);
extern time_t DecodeTimeString_time_t(const char *time_string);
void DecodeTimeString_Local(const char *time_string, struct tm *local) {
// Various error handling not shown
time_t t;
t = DecodeTimeString_time_t(time_string);
*local = *localtime(&t);
}
time_t DecodeTimeString_time_t(const char *time_string /* YYMMDDHHMMCY */) {
struct tm tm;
char Zone, DST;
int result = sscanf(time_string, "%2d%2d%2d%2d%2d%[CEMP]%[NY]",
&tm.tm_year, &tm.tm_mon, &tm.tm_mday, &tm.tm_hour, &tm.tm_min, &Zone, &DST);
if (result != 7) {
; // handle error
}
// Your need to decide how to handle 2 digits years
// Assume 70-99 is 1970-1999 and 0 to 69 is 2000-2069
if (tm.tm_year < 70) tm.tm_year += 2000-1900;
tm.tm_mon--; // Assume DateString used "01" for January, etc.
tm.tm_sec = 0;
tm.tm_isdst = Zone == 'Y';
const char *TZ;
switch (Zone) {
case 'P': TZ = "PST8PDT"; break; // Pacific
case 'M': TZ = "MST7MDT"; break; // mountain
case 'C': TZ = "CST6CDT"; break; // central
case 'E': TZ = "EST5EDT"; break; // eastern
}
time_t t = mktime_TZ(&tm, TZ);
return t;
}
// Form time_t from struct tm given a TZ
time_t mktime_TZ(struct tm *tm, const char *tz) {
time_t t;
const char *old_tz = getenv("TZ");
if (setenv("TZ", tz, 1 /* overwrite */)) {
return -1; // handle error
}
tzset();
t = mktime(tm);
if (old_tz) {
if (setenv("TZ", old_tz, 1 /* overwrite */)) {
return -1; // handle error
}
}
else {
if (unsetenv("TZ")) {
return -1; // handle error
}
}
tzset();
return t;
}