Introduction
When I started to use WinAPI datetime - I found many classes and several functions, but didn't find an explanation for simple operations with datetime. I describe here two operations:
- Difference between two datetimes (datetime2-datetime1) and receiving weeks, days, hours, elapsed between them.
- Add/Subtract weeks, days, hours, to/from datetime and receiving new datetime.
With WinAPI we receive more then 29 thousand years for manipulation (start from 01.01.1601). To be more exact - 0x8000000000000000 100-nanoseconds intervals == from zero [01 Jan 1601, 00:00:00] to 0x800...-1 [14 sep 30828, 02:48:05] where (1 sec = 1.000.000.000 nanoseconds)
WinAPI has two structs to store datetime - FILETIME and SYSTEMTIME. It was a surprise for me but SYSTEMTIME is only representation of datetime and for real operations with datetime we need to use FILETIME. (why did Microsoft title it FILE ? where is the logic ?)
SYSTEMTIME
Why do I say that SYSTEMTIME is only a representation? Look at this:
typedef struct _SYSTEMTIME
{
WORD wYear;
WORD wMonth;
WORD wDayOfWeek;
WORD wDay;
WORD wHour;
WORD wMinute;
WORD wSecond;
WORD wMilliseconds;
} SYSTEMTIME;
What do you see? Right - nothing says to us that SYSTEMTIME is ONLY a representation. But if you receive datetime to SYSTEMTIME:
SYSTEMTIME st;
GetSystemTime (&st);
and make such operation as add 100 days:
st.wDay += 100;
and than try to receive string representation of this new date:
GetDateFormat
(
LOCALE_USER_DEFAULT,
NULL,
&st,
"yyyy MM dd",
buff,
20
);
You receive nothing. GetLastError returns number of error, GetDateFormat return 0. Of course, if you assign to wDay number valid for wMonth, you receive expected result (example - for Feb 2003 valid days from 1 to 28, if you assign wDay=29 you receive nothing). So, conclusion - SYSTEMTIME is not for above mentioned operations.
Ok, let us move to FILETIME. FILETIME contains two DWORD variables:
typedef struct _FILETIME
{
DWORD dwLowDateTime;
DWORD dwHighDateTime;
} FILETIME;
Two DWORDs allow FILETIME to store 64 bit values. Value of FILETIME is equal to number of 100-nanosecond intervals since 01 Jan 1601, 00:00:00. So, if (FILETIME == 0) than we have 01 Jan 1601, 00:00:00. If (FILETIME == 10000000*60*60*24) we have 02 Jan 1601, 00:00:00.
(1 sec = 10000000 (100 nanoseconds) - in one min 60 secs, in one hour 60 mins and in one day 24 hours :)
64 bit value means that we can store 0xFFFFFFFFFFFFFFFF intervals ! But manipulate only 0x800..., in two times less. This restriction is superimposed by FileTimeToSystemTime. Read help:
The FileTimeToSystemTime function only works with FILETIME values that are less than 0x8000000000000000.
I think internals of this function use double (for convert to store double we need SIGNED __int64), and we lose 1 bit to sign, and receive in two times less max value. But I think it is not a tragedy :) - 29 thousand is enough.
Go to algorithms
const __int64 nano100SecInWeek=(__int64)10000000*60*60*24*7;
const __int64 nano100SecInDay=(__int64)10000000*60*60*24;
const __int64 nano100SecInHour=(__int64)10000000*60*60;
const __int64 nano100SecInMin=(__int64)10000000*60;
const __int64 nano100SecInSec=(__int64)10000000;
double
DT_PeriodsBetween
(
const __int64 datepart,
const SYSTEMTIME* pst1,
const SYSTEMTIME* pst2
)
{
FILETIME ft1,ft2;
__int64 *pi1,*pi2;
SystemTimeToFileTime (pst1,&ft1);
SystemTimeToFileTime (pst2,&ft2);
pi1 = (__int64*)&ft1;
pi2 = (__int64*)&ft2;
return (CompareFileTime(&ft1,&ft2)==1) ?
(((*pi1)-(*pi2))/(double)datepart) : (((*pi2)-(*pi1))/(double)datepart);
}
SYSTEMTIME
DT_AddDiff
(
const __int64 datepart,
const __int64 num,
const SYSTEMTIME* pst
)
{
FILETIME ft;
SYSTEMTIME st;
__int64* pi;
SystemTimeToFileTime (pst,&ft);
pi = (__int64*)&ft;
(*pi) += (__int64)num*datepart;
FileTimeToSystemTime (&ft,&st);
return st;
}
SAMPLE use of functions
SYSTEMTIME s1={2003,11,0,28,12,0,0,0},s2={2003,12,0,1,0,0,0,0};
printf("%f\n",DT_PeriodsBetween(nano100SecInDay,&s1,&s2));
char buff[100];
s1=DT_AddDiff(nano100SecInDay,-1,&s2);
GetDateFormat
(
LOCALE_USER_DEFAULT,
NULL,
&s1,
"yyyy MM dd", buff,
100
);
printf("20031201 - 1 day = %s\n",buff);
These functions can't be used for work with month, quarter, year because it they are not constants. For work with such time intervals you need to modify the above algorithms.