Daylight Saving Time and UTC-to-local time conversions with WinAPIs - c++

I'm trying to see if WindAPIs that convert from local to UTC time and vice versa are Daylight Saving Time accurate. For instance, let's take LocalFileTimeToFileTime API. Its description states:
LocalFileTimeToFileTime uses the current settings for the time zone
and daylight saving time. Therefore, if it is daylight saving time,
this function will take daylight saving time into account, even if the
time you are converting is in standard time.
So I'm testing it with this code:
//Say, if DST change takes place on Mar-8-2015 at 2:00:00 AM
//when the clock is set 1 hr forward
//Let's check the difference between two times:
SYSTEMTIME st1_local = {2015, 3, 0, 8, 1, 30, 0, 0}; //Mar-8-2015 1:30:00 AM
SYSTEMTIME st2_local = {2015, 3, 0, 8, 3, 30, 0, 0}; //Mar-8-2015 3:30:00 AM
//Convert to file-time format
FILETIME ft1_local, ft2_local;
VERIFY(::SystemTimeToFileTime(&st1_local, &ft1_local));
VERIFY(::SystemTimeToFileTime(&st2_local, &ft2_local));
//Then convert from local to UTC time
FILETIME ft1_utc, ft2_utc;
VERIFY(::LocalFileTimeToFileTime(&ft1_local, &ft1_utc));
VERIFY(::LocalFileTimeToFileTime(&ft2_local, &ft2_utc));
//Get the difference
LONGLONG iiDiff100ns = (((LONGLONG)ft2_utc.dwHighDateTime << 32) | ft2_utc.dwLowDateTime) -
(((LONGLONG)ft1_utc.dwHighDateTime << 32) | ft1_utc.dwLowDateTime);
//Convert from 100ns to seconds
LONGLONG iiDiffSecs = iiDiff100ns / 10000000LL;
//I would expect 1 hr
ASSERT(iiDiffSecs == 3600); //But I get 7200, which is 2 hrs!
So what am I missing here?

SystemTimeToFileTime() interprets its first argument as a UTC time (which has no concept of DST), so your ft1_local and ft2_local objects will always be two hours apart since you're changing the data format, but not the actual point in time. LocalFileTimeToFileTime() will then apply the same offset to whatever you pass to it, so ft1_utc and ft2_utc will always end up two hours apart, also.
As the documentation says, "LocalFileTimeToFileTime uses the current settings for the time zone and daylight saving time" (emphasis mine), so if at the current time you're four hours behind UTC, for instance, it'll just deduct four hours from whatever time you pass to it, regardless of whether that time originally represented some time at the other side of DST.
EDIT: Per the comments, here's how you'd get the seconds difference between two local times in standard C:
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
int main(void) {
struct tm start_time;
start_time.tm_year = 115;
start_time.tm_mon = 2;
start_time.tm_mday = 8;
start_time.tm_hour = 1;
start_time.tm_min = 30;
start_time.tm_sec = 0;
start_time.tm_isdst = -1;
struct tm end_time;
end_time.tm_year = 115;
end_time.tm_mon = 2;
end_time.tm_mday = 8;
end_time.tm_hour = 3;
end_time.tm_min = 30;
end_time.tm_sec = 0;
end_time.tm_isdst = -1;
time_t start_tm = mktime(&start_time);
time_t end_tm = mktime(&end_time);
if ( start_tm == -1 || end_tm == -1 ) {
fputs("Couldn't get local time.", stderr);
exit(EXIT_FAILURE);
}
double seconds_diff = difftime(end_tm, start_tm);
printf("There are %.1f seconds difference.\n", seconds_diff);
return EXIT_SUCCESS;
}
which outputs:
paul#thoth:~/src$ ./difftime
There are 3600.0 seconds difference.
paul#thoth:~/src$
as you're expecting.
Note that, with struct tm:
tm_year is expressed in years since 1900, so to get 2015 we write 115
tm_mon is in the range 0 though 11, so March is 2, not 3.
The other time members are as you'd expect
When tm_isdst is set to -1, mktime() will attempt to find out for itself whether DST was in effect at the local time we supplied, which is what we want it to do, here.

In despite of all the beauty of Paul Griffiths' solution, I can't use it due to an apparent locale limitation. (C is obviously showing its age.) So I had to go with a pure WinAPI approach. Next is what I came up with. Correct me if I'm wrong (especially people with access to time zones other than the US one that Microsoft's mktime seems to be favoring):
SYSTEMTIME st1 = {2015, 3, 0, 8, 1, 30, 0, 0}; //Mar-8-2015 1:30:00 AM
SYSTEMTIME st2 = {2015, 3, 0, 8, 3, 30, 0, 0}; //Mar-8-2015 3:30:00 AM
LONGLONG iiDiffNs;
if(GetLocalDateTimeDifference(&st1, &st2, &iiDiffNs))
{
_tprintf(L"Difference is %.02f sec\n", (double)iiDiffNs / 1000.0);
}
else
{
_tprintf(L"ERROR (%d) calculating the difference.\n", ::GetLastError());
}
Then this is the actual implementation. One important aspect to note here is that the method below may not work reliably on Windows XP due to the lack of APIs to retrieve time zone info for a specific year.
Some declarations first:
enum DST_STATUS{
DST_ERROR = TIME_ZONE_ID_INVALID, //Error
DST_NONE = TIME_ZONE_ID_UNKNOWN, //Daylight Saving Time is NOT observed
DST_OFF = TIME_ZONE_ID_STANDARD, //Daylight Saving Time is observed, but the system is currently not on it
DST_ON = TIME_ZONE_ID_DAYLIGHT, //Daylight Saving Time is observed, and the system is currently on it
};
#define FILETIME_TO_100NS(f) (((LONGLONG)f.dwHighDateTime << 32) | f.dwLowDateTime)
BOOL GetLocalDateTimeDifference(SYSTEMTIME* pStBegin_Local, SYSTEMTIME* pStEnd_Local, LONGLONG* pOutDiffMs = NULL);
BOOL ConvertLocalTimeToUTCTime(SYSTEMTIME* pSt_Local, SYSTEMTIME* pOutSt_UTC = NULL);
DST_STATUS GetDSTInfoForYear(USHORT uYear, TIME_ZONE_INFORMATION* pTZI = NULL);
And the implementation:
BOOL GetLocalDateTimeDifference(SYSTEMTIME* pStBegin_Local, SYSTEMTIME* pStEnd_Local, LONGLONG* pOutDiffMs)
{
//Calculate difference between two local dates considering DST adjustments between them
//INFO: May not work correctly on Windows XP for a year other than the current year!
//'pStBegin_Local' = local date/time to start from
//'pStEnd_Local' = local date/time to end with
//'pOutDiffMs' = if not NULL, receives the difference in milliseconds (if success)
//RETURN:
// = TRUE if success
// = FALSE if error (check GetLastError() for info)
BOOL bRes = FALSE;
LONGLONG iiDiffMs = 0;
int nOSError = NO_ERROR;
if(pStBegin_Local &&
pStEnd_Local)
{
//Convert both dates to UTC
SYSTEMTIME stBeginUTC;
if(ConvertLocalTimeToUTCTime(pStBegin_Local, &stBeginUTC))
{
SYSTEMTIME stEndUTC;
if(ConvertLocalTimeToUTCTime(pStEnd_Local, &stEndUTC))
{
//Then convert into a more manageable format: FILETIME
//It will represent number of 100-nanosecond intervals since January 1, 1601 for each date
FILETIME ftBeginUTC;
if(::SystemTimeToFileTime(&stBeginUTC, &ftBeginUTC))
{
FILETIME ftEndUTC;
if(::SystemTimeToFileTime(&stEndUTC, &ftEndUTC))
{
//Now get the difference in ms
//Convert from 100-ns intervals = 10^7, where ms = 10^3
iiDiffMs = (FILETIME_TO_100NS(ftEndUTC) - FILETIME_TO_100NS(ftBeginUTC)) / 10000LL;
//Done
bRes = TRUE;
}
else
nOSError = ::GetLastError();
}
else
nOSError = ::GetLastError();
}
else
nOSError = ::GetLastError();
}
else
nOSError = ::GetLastError();
}
else
nOSError = ERROR_INVALID_PARAMETER;
if(pOutDiffMs)
*pOutDiffMs = iiDiffMs;
::SetLastError(nOSError);
return bRes;
}
BOOL ConvertLocalTimeToUTCTime(SYSTEMTIME* pSt_Local, SYSTEMTIME* pOutSt_UTC)
{
//Convert local date/time from 'pSt_Local'
//'pOutSt_UTC' = if not NULL, receives converted UTC time
//RETURN:
// = TRUE if success
// = FALSE if error (check GetLastError() for info)
BOOL bRes = FALSE;
SYSTEMTIME stUTC = {0};
int nOSError = NO_ERROR;
if(pSt_Local)
{
//First get time zone info
TIME_ZONE_INFORMATION tzi;
if(GetDSTInfoForYear(pSt_Local->wYear, &tzi) != DST_ERROR)
{
if(::TzSpecificLocalTimeToSystemTime(&tzi, pSt_Local, &stUTC))
{
//Done
bRes = TRUE;
}
else
nOSError = ::GetLastError();
}
else
nOSError = ::GetLastError();
}
else
nOSError = ERROR_INVALID_PARAMETER;
if(pOutSt_UTC)
*pOutSt_UTC = stUTC;
::SetLastError(nOSError);
return bRes;
}
DST_STATUS GetDSTInfoForYear(USHORT uYear, TIME_ZONE_INFORMATION* pTZI)
{
//Get DST info for specific 'uYear'
//INFO: Year is not used on the OS prior to Vista SP1
//'pTZI' = if not NULL, will receive the DST data currently set for the time zone for the year
//RETURN:
// = Current DST status, or an error
// If error (check GetLastError() for info)
DST_STATUS tzStat = DST_ERROR;
int nOSError = NO_ERROR;
//Define newer APIs
DWORD (WINAPI *pfnGetDynamicTimeZoneInformation)(PDYNAMIC_TIME_ZONE_INFORMATION);
BOOL (WINAPI *pfnGetTimeZoneInformationForYear)(USHORT, PDYNAMIC_TIME_ZONE_INFORMATION, LPTIME_ZONE_INFORMATION);
//Load APIs dynamically (in case of Windows XP)
HMODULE hKernel32 = ::GetModuleHandle(L"Kernel32.dll");
ASSERT(hKernel32);
(FARPROC&)pfnGetDynamicTimeZoneInformation = ::GetProcAddress(hKernel32, "GetDynamicTimeZoneInformation");
(FARPROC&)pfnGetTimeZoneInformationForYear = ::GetProcAddress(hKernel32, "GetTimeZoneInformationForYear");
TIME_ZONE_INFORMATION tzi = {0};
//Use newer API if possible
if(pfnGetDynamicTimeZoneInformation &&
pfnGetTimeZoneInformationForYear)
{
//Use new API for dynamic time zone
DYNAMIC_TIME_ZONE_INFORMATION dtzi = {0};
tzStat = (DST_STATUS)pfnGetDynamicTimeZoneInformation(&dtzi);
if(tzStat == DST_ERROR)
{
//Failed -- try old method
goto lbl_fallback_method;
}
//Get TZ info for a year
if(!pfnGetTimeZoneInformationForYear(uYear, &dtzi, &tzi))
{
//Failed -- try old method
goto lbl_fallback_method;
}
}
else
{
lbl_fallback_method:
//Older API (also used as a fall-back method)
tzStat = (DST_STATUS)GetTimeZoneInformation(&tzi);
if(tzStat == DST_ERROR)
nOSError = ::GetLastError();
else
nOSError = ERROR_NOT_SUPPORTED;
}
if(pTZI)
{
*pTZI = tzi;
}
::SetLastError(nOSError);
return tzStat;
}

Related

Clamp framerate in Windows

I have a simple loop
LARGE_INTEGER ticks_per_second;
::QueryPerformanceFrequency(&ticks_per_second);
MSG msg = { 0 };
while (true)
{
if (msg.message == WM_QUIT)
exit(0);
if (::PeekMessageW(&msg, NULL, 0U, 0U, PM_REMOVE))
{
::TranslateMessage(&msg);
::DispatchMessageW(&msg);
continue;
}
static double last_time_s = 0;
LARGE_INTEGER cur_time_li;
::QueryPerformanceCounter(&cur_time_li);
double cur_time_s = (double)cur_time_li.QuadPart / (double)ticks_per_second.QuadPart;
double diff_s = cur_time_s - last_time_s;
double rate_s = 1 / 30.0f;
uint32_t slept_ms = 0;
if (diff_s < rate_s)
{
slept_ms = (uint32_t)((rate_s - diff_s) * 1000.0);
::Sleep(slept_ms);
}
update();
::printf("updated %f %u\n", diff_s, slept_ms);
last_time_s = cur_time_s;
}
And want update() to be called 30 times per second, but not more often
With this code it goes wrong, in console I getting something like this:
updated 0.031747 1
updated 0.001997 31
updated 0.031912 1
updated 0.001931 31
updated 0.031442 1
updated 0.002084 31
Which is seems to be correct only for first update, second one called too fast, and I can't understand why
I understand that update, PeekMessageW and etc. also wasting time, but even if I create a while (true) loop and comment update() out, it's still printing similar result
I using DirectX 11 with vsync turned off for rendering (rendering inside update function):
g_pSwapChain->Present(0, 0);
How do I fix code to make update() stable called 30 times in one second?
I don't think casting to double is good idea.I would run something like this:
static LARGE_INTEGER last_time_s = { 0 };
::QueryPerformanceCounter(&cur_time_li);
time_diff_microsec.QuadPart = cur_time_li.QuadPart - last_time_s.QuadPart;
// To avoid precision lost, convert to seconds *before* dividing by ticks-per-second.
time_diff_microsec.QuadPart *= 1000000;
time_diff_microsec.QuadPart /= ticks_per_second.QuadPart;
double rate_s = 1 / 30.0f;
uint32_t slept_ms = 0;
if (time_diff_microsec.QuadPart >= rate_s)// if (diff_s < rate_s)
{
// slept_ms = (uint32_t)(rate_s - time_diff_microsec.LowPart);// *1000.0);
// ::Sleep(slept_ms);
//}
//update();
::printf("updated %lld %u\n", time_diff_microsec.QuadPart, slept_ms);
}
last_time_s.QuadPart = time_diff_microsec.QuadPart/ 1000000;
}
Just brief "sketch". Not verified that calculations are correct though.

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

How to find out the next time when the clock will be adjusted for Daylight Saving?

I'm curious, if there's any way to find out the UTC date/time when the next Daylight Saving adjustment will take place?
Something akin to what Windows reports (see circled):
This information is provided in Windows by the EnumDynamicTimeZoneInformation function.
See http://msdn.microsoft.com/en-us/library/windows/desktop/hh706893%28v=vs.85%29.aspx
There is a database that has code and data: http://www.iana.org/time-zones
I don't think there's a specific API for this. I would just do a binary search, using localtime (and maybe time and mktime) from <ctime> (C++) or <time.h> (C).
A basic approach is to scan ahead three months at a time until the tm_isdst flag in the returned data structure is flipped. Then you can start binary searching between the last two two dates to figure out exactly when it flips.
See http://www.cplusplus.com/reference/ctime/tm/ for reference material.
I appreciate all your replies. And, yes, indeed I was asking about a WinAPI for Windows.
I did more research and came up with the following method that does what I wanted. It uses C++ and MFC's COleDateTime for easier date/time calculations. Other than that it's just C++ and WinAPIs. Please check if I understood the documentation for the DYNAMIC_TIME_ZONE_INFORMATION correctly. Here's the code:
int GetNextDaylightSavingAdjustmentTime(SYSTEMTIME* pOutDtNextDST_Local, int* pnOutAdjustmentMin)
{
//Get next time when DST adjustment will take place
//'pOutDtNextDST_Local' = if not NULL, receives the (local) time when next DST adjustment will take place
//'pnOutAdjustmentMin' = if not NULL, receives the amount of adjustment in minutes
//RETURN:
// = 1 if got the time, or
// = 0 if DST is not used
// = -1 if error (check GetLastError() for info)
int nOSError = NO_ERROR;
//Load API dynamically (in case of Windows XP)
BOOL (WINAPI *pfnGetDynamicTimeZoneInformation)(PDYNAMIC_TIME_ZONE_INFORMATION);
(FARPROC&)pfnGetDynamicTimeZoneInformation =
::GetProcAddress(::GetModuleHandle(L"Kernel32.dll"), "GetDynamicTimeZoneInformation");
DWORD tzID;
SYSTEMTIME StandardDate;
SYSTEMTIME DaylightDate;
int nBiasDaylight;
//Use newer API if possible
if(pfnGetDynamicTimeZoneInformation)
{
DYNAMIC_TIME_ZONE_INFORMATION dtzi = {0};
tzID = pfnGetDynamicTimeZoneInformation(&dtzi);
StandardDate = dtzi.StandardDate;
DaylightDate = dtzi.DaylightDate;
nBiasDaylight = dtzi.DaylightBias;
}
else
{
//Older API
TIME_ZONE_INFORMATION tzi = {0};
tzID = GetTimeZoneInformation(&tzi);
StandardDate = tzi.StandardDate;
DaylightDate = tzi.DaylightDate;
nBiasDaylight = tzi.DaylightBias;
}
int nRes = -1;
int nAdjMins = 0;
SYSTEMTIME stDstChange;
memset(&stDstChange, 0, sizeof(stDstChange));
SYSTEMTIME stDst;
if(tzID == TIME_ZONE_ID_STANDARD ||
tzID == TIME_ZONE_ID_DAYLIGHT)
{
stDst = tzID != TIME_ZONE_ID_DAYLIGHT ? DaylightDate : StandardDate;
if(stDst.wMonth >= 1 &&
stDst.wMonth <= 12 &&
stDst.wDay >= 1 &&
stDst.wDayOfWeek >= 0 &&
stDst.wDayOfWeek <= 6)
{
//Get adjustment bias
nAdjMins = tzID != TIME_ZONE_ID_DAYLIGHT ? -nBiasDaylight : nBiasDaylight;
if(stDst.wYear == 0)
{
//Relative date
SYSTEMTIME stLocal;
::GetLocalTime(&stLocal);
//Begin from the 1st day of the month &
//make sure that the date is in the future
COleDateTime dt;
for(int nYear = stLocal.wYear;; nYear++)
{
dt.SetDateTime(nYear, stDst.wMonth, 1, stDst.wHour, stDst.wMinute, stDst.wSecond);
if(dt > COleDateTime::GetCurrentTime())
break;
}
int nRequiredWeek = stDst.wDay >= 1 && stDst.wDay <= 5 ? stDst.wDay : 5;
for(int nCntDOW = 1;;)
{
//0=Sunday, 1=Monday; 2=Tuesday; 3=Wednesday; 4=Thursday; 5=Friday; 6=Saturday
int dow = dt.GetDayOfWeek() - 1;
ASSERT(dow >= 0 && dow <= 6);
if(dow == stDst.wDayOfWeek)
{
if(nCntDOW >= nRequiredWeek)
{
//Stop
break;
}
else
{
nCntDOW++;
}
}
//Go to next day
dt += COleDateTimeSpan(1, 0, 0, 0);
}
//Convert back to system time
if(dt.GetAsSystemTime(stDstChange))
{
//Success
nRes = 1;
}
else
{
//Failed
nOSError = ERROR_INVALID_FUNCTION;
ASSERT(NULL);
}
}
else
{
//Absolute date
stDstChange = stDst;
nRes = 1;
}
}
else
{
//Failed
nOSError = ERROR_INVALID_PARAMETER;
ASSERT(NULL);
}
}
else
{
//DST is not used
if(tzID == TIME_ZONE_ID_UNKNOWN)
{
nRes = 0;
}
else
{
//Error
nOSError = ERROR_INVALID_DATA;
ASSERT(NULL);
}
}
if(pOutDtNextDST_Local)
*pOutDtNextDST_Local = stDstChange;
if(pnOutAdjustmentMin)
*pnOutAdjustmentMin = nAdjMins;
::SetLastError(nOSError);
return nRes;
}
PS. And scratch my request for the UTC time. As I learned, it is easier to deal with local time in this situation.

C++ Time returned is two hours out

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