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, /*char buff[20];*/
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 DWORD
s 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 withFILETIME
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
/*declare const that represent week,day,hour,minute,second*/ 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; /*1. Diffence BETWEEN two datetime //equivalent of DATEDIFF function from SQLServer //Returns the number of date and time boundaries crossed //between two specified dates. */ double /* return count of period between pst1 and pst2*/ DT_PeriodsBetween ( const __int64 datepart, /*datepart that we want to count, {nano100SecInDay ...}*/ const SYSTEMTIME* pst1, /*valid datetime*/ const SYSTEMTIME* pst2 /*valid datetime*/ ) { FILETIME ft1,ft2; __int64 *pi1,*pi2; /*convert SYSTEMTIME to FILETIME //SYSTEMTIME is only representation, so need convert to //FILETIME for make some calculation*/ SystemTimeToFileTime (pst1,&ft1); SystemTimeToFileTime (pst2,&ft2); /*convert FILETIME to __int64 //FILETIME is struct with two DWORD, for receive //ability calculation we must reference to FILETIME as to int*/ pi1 = (__int64*)&ft1; pi2 = (__int64*)&ft2; /*compare two datetimes and (bigger date - smaller date)/period*/ return (CompareFileTime(&ft1,&ft2)==1) ? (((*pi1)-(*pi2))/(double)datepart) : (((*pi2)-(*pi1))/(double)datepart); } /*2. Add/Subtract weeks,days,hours... to/from datetime //and receiving new datetime. //equivalent of DATEADD function from SQLServer //Returns a new datetime value based on adding an interval // to the specified date. */ SYSTEMTIME /*new datetime*/ DT_AddDiff ( const __int64 datepart, /*datepart with we want to manipulate, {nano100SecInDay ...}*/ const __int64 num, /*value used to increment/decrement datepart*/ const SYSTEMTIME* pst /*valid datetime which we want change*/ ) { FILETIME ft; SYSTEMTIME st; __int64* pi; SystemTimeToFileTime (pst,&ft); pi = (__int64*)&ft; (*pi) += (__int64)num*datepart; /*convert FAILETIME to SYSTEMTIME*/ FileTimeToSystemTime (&ft,&st); /*now, st contain new valid datetime, so return it*/ return st; }
SAMPLE use of functions
/*1*/ SYSTEMTIME s1={2003,11,0,28,12/*hours*/,0,0,0},s2={2003,12,0,1,0,0,0,0}; printf("%f/n",DT_PeriodsBetween(nano100SecInDay,&s1,&s2));
/*2*/ char buff[100]; s1=DT_AddDiff(nano100SecInDay,-1,&s2); //diff 1 day from s2 /*WinAPI function GetDateFormat uses for receive string with datetime in specifid format*/ GetDateFormat ( LOCALE_USER_DEFAULT, NULL, &s1, "yyyy MM dd", //my format 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.
<script src="/script/togglePre.js" type="text/javascript"></script>