Click here to Skip to main content
15,867,453 members
Articles / Programming Languages / C++

WinAPI - Simple Operations with datetime

Rate me:
Please Sign up or sign in to vote.
4.89/5 (23 votes)
30 Nov 20033 min read 164.5K   51   14
Simple operations with datetime - add/diff to/from datetime, difference between datetime on WinAPI

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:

  1. Difference between two datetimes (datetime2-datetime1) and receiving weeks, days, hours, elapsed between them.
  2. Add/Subtract weeks, days, hours, to/from datetime and receiving new datetime.

With WinAPI, we receive more than 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 a 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:

C++
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:

C++
SYSTEMTIME st;
GetSystemTime (&st);

and make such operation as add 100 days:

C++
st.wDay += 100;

and then try to receive string representation of this new date:

C++
GetDateFormat
 (
  LOCALE_USER_DEFAULT,
  NULL,
  &st,
  "yyyy MM dd",
  buff,     /*char buff[20];*/
  20
 );

You receive nothing. GetLastError returns number of error, GetDateFormat returns 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 the above mentioned operations.

Ok, let us move to FILETIME. FILETIME contains two DWORD variables:

C++
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

C++
/*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. Difference 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

C++
/*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));
C++
/*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 specified 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.

License

This article has no explicit license attached to it, but may contain usage terms in the article text or the download files themselves. If in doubt, please contact the author via the discussion board below.

A list of licenses authors might use can be found here.


Written By
Russian Federation Russian Federation
Alexey Utkin

Comments and Discussions

 
QuestionDo not cast a pointer to a FILETIME structure to either a ULARGE_INTEGER* or __int64* Pin
Noobs R Us9-Sep-16 3:37
professionalNoobs R Us9-Sep-16 3:37 
GeneralFully working date/time add function with Year and Month support Pin
sixstorm8-Jan-10 11:05
sixstorm8-Jan-10 11:05 
GeneralRe: Fully working date/time add function with Year and Month support Pin
Member 71363227-Jun-10 2:41
Member 71363227-Jun-10 2:41 
BugRe: Fully working date/time add function with Year and Month support Pin
Vasiliy Zverev22-May-12 3:28
Vasiliy Zverev22-May-12 3:28 
GeneralUpdated code to avoid cast from FILETIME to __int64 Pin
JeffRoz8-Apr-07 5:11
JeffRoz8-Apr-07 5:11 
GeneralThank you so much, Add/diff sec function Pin
powenko6-Feb-07 11:18
powenko6-Feb-07 11:18 
GeneralMore accurate solution Pin
David White28-Sep-06 5:31
David White28-Sep-06 5:31 
GeneralThis function has really helped Pin
cyrix053-Feb-05 15:39
cyrix053-Feb-05 15:39 
GeneralYou can't cast from FILETIME to __int64 Pin
J. Daniel Smith25-Aug-04 4:41
J. Daniel Smith25-Aug-04 4:41 
Questionwhat about month & year part ? Pin
Member 1610092-Dec-03 15:16
Member 1610092-Dec-03 15:16 
AnswerRe: what about month & year part ? Pin
AlexeyU2-Dec-03 17:41
AlexeyU2-Dec-03 17:41 
Questionwhat about month & year part ? Pin
tiw_ashish2-Dec-03 15:16
susstiw_ashish2-Dec-03 15:16 
GeneralTime is critical Pin
Georgi Petrov2-Dec-03 3:26
Georgi Petrov2-Dec-03 3:26 
GeneralRe: Time is critical Pin
AlexeyU2-Dec-03 11:42
AlexeyU2-Dec-03 11:42 
hi
Of course you can use it as you wish.
It is nicely that my article can be useful.

But i think you will be need use milliseconds in your application - so you will be need format time directly from SYSTEMTIME (becouse GetTimeFormat dont support milliseconds).






--
---
Aleksey

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.