Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Date and Time in C++

0.00/5 (No votes)
15 Oct 2002 1  
This is an article on the various usages of date and time in Windows C++

Introduction

Aside from strings, another area where things are probably a little more messy in the C++ and Windows world than it needed to be, has to be the date and time area.

Starting with C and with every library added, there are so many ways to get the date and time that people can easily get confused. So in this article, I hope to summarize and somewhat simplify the various date and time functionalities in Windows C++.

Some Terminology

As always, there are a few jargons that you may have to know about, before getting into this concept, so I thought I should list them right at the beginning:

UTC (Coordinated Universal Time): This is the standard international time or the Greenwich Mean Time.

epoch: Number of seconds elapsed since 00:00:00 on January 1, 1970, Coordinated Universal Time.

1. ASNI Standard Library

Our first stop is the ANSI Standard library and the time.h header file. As its names indicates, the roots originate in C, so everything is designed in a very C-ish form (i.e., structures, etc.)

I like to think of the contents of this file in two portions:

  1. the CPU related functions and types as represented by clock_t and clock() functions.
  2. the calendar time related functions and types as represented by almost everything else in that header file.

This article will only deal with calendar times.

The calendar time information in the Standard Library header file can themselves be broken into at least two groups:

  1. The local time or broken-down time which is represented by the tm structure as follows in the time.h header file:

    #ifndef _TM_DEFINED
    struct tm {
            int tm_sec;     /* seconds after the minute - [0,59] */
            int tm_min;     /* minutes after the hour - [0,59] */
            int tm_hour;    /* hours since midnight - [0,23] */
            int tm_mday;    /* day of the month - [1,31] */
            int tm_mon;     /* months since January - [0,11] */
            int tm_year;    /* years since 1900 */
            int tm_wday;    /* days since Sunday - [0,6] */
            int tm_yday;    /* days since January 1 - [0,365] */
            int tm_isdst;   /* daylight savings time flag */
            };
    #define _TM_DEFINED
    #endif
  2. The Calendar Time which is represented by the time_t data type. The value of time_t is typically the number of seconds elapsed since some implementation-specific base time.

    If you look into the time header file, you will see that it is simply defined as:

    #ifndef _TIME_T_DEFINED
    typedef long time_t;        /* time value */
    #define _TIME_T_DEFINED     /* avoid multiple def's of time_t */
    #endif

    If you look into the header file, you will notice that there is an array of functions that can be used, all of which use time_t as either their parameters or return values:

    double difftime(time_t time1, time_t time0);
    time_t mktime(struct tm * timeptr);
    time_t time(time_t * timer);
    char * asctime(const struct tm * timeptr);
    char * ctime(const time_t *timer);

    In addition, time.h provides two different ways to convert the calendar time represented by time_t to a broken-down time represented by the tm structure:

    struct tm * gmtime(const time_t *timer);
    //converts the calender time represented by  timer
    //to a UTC time represented by the tm structure.
                                              
    //converts the calendar time to the local time
    struct tm * localtime(const time_t * timer);

According to MSDN, in all versions of Microsoft C/C++ except Microsoft C/C++ version 7.0, and in all versions of Microsoft Visual C++, the time function returns the current time as the number of seconds elapsed since midnight on January 1, 1970 (i.e., epoch). In Microsoft C/C++ version 7.0, time returned the current time as the number of seconds elapsed since midnight on December 31, 1899.

2. Microsoft Implementations

Over years Microsoft has added its own version of date and time functions which this article will now try to cover. We will begin our journey first with the Win32 API functions and then with the MFC functions available.

2.1 Win32 API

With the Win32 API, Microsoft did not provide any direct functions to calculate time differences or provide capability to compare time values other than file times. Instead, it provided three basic structures, two of which will be talked about here and some 20 basic functions to retrieve various time information and convert between various time formats.

At the heart of the Win32 date and time functionalities, are the SYSTEMTIME and FILETIME structures which are defined in winbase.h as shown:

typedef struct _FILETIME {
    DWORD dwLowDateTime;
    DWORD dwHighDateTime;
} FILETIME, *PFILETIME, *LPFILETIME;

//
// System time is represented with the following structure:
//

typedef struct _SYSTEMTIME {
    WORD wYear;
    WORD wMonth;
    WORD wDayOfWeek;
    WORD wDay;
    WORD wHour;
    WORD wMinute;
    WORD wSecond;
    WORD wMilliseconds;
} SYSTEMTIME, *PSYSTEMTIME, *LPSYSTEMTIME;

As you can tell, the SYSTEMTIME structure somewhat resembles the tm structure of the standard library, although it does not have two of the members of tm, but provides a millisecond member.

As for the other structure, Microsoft decided to define the FILETIME structure as a 64-bit value representing the number of 100-nanosecond intervals since January 1, 1601. Thus, the task of using time in Win32 API gets even more tricky.

2.1.1 Getting System Time

Well, this is the easiest part. You can use the SYSTEMTIME structure, together with the GetSystemTime function to retrieve the current system time in UTC format.

2.1.2 Obtaining Relative Times

This is where things get a big trickier. This is essentially what you need to do:

  1. Convert your times from SYSTETIME to FILETIME using SystemTimeToFileTime function
  2. Copy the resulting FILETIME structure to a LARGE_INTEGER structure
  3. Use normal arithmetics on LARGE_INTEGER value
     
SYSTEMTIME st;
FILETIME ft;
LARGE_INTEGER li;
GetSystemTime(&st);
SystemTimeToFileTime(&st, &ft);

li.LowPart = ft.dwLowDateTime;
li.HighPart = ft.dwHighDateTime;

Note: Sometimes some avid programmers try to take a shortcut and directly use a FILETIME instead of a LARGE_INTEGER for their data modifications.

The LARGE_INTEGER structure is defined as follows:

typedef union _LARGE_INTEGER { 
    struct {
        DWORD LowPart; 
        LONG  HighPart; 
    };
    LONGLONG QuadPart;
} LARGE_INTEGER; 

Although both of these have the same binary format, the address of all FILETIME structures must begin on a 32 bit boundary whereas the address of all LARGE_INTEGER must begin on 64 bit boundary. So, do not use these interchangeably.

2.1.3 Other Useful Functions

In the Win32 date and time world, probably one of the more useful functions that you will run into is the LocalFileTimeToFileTime function. This function can be used to convert a local time to a UTC time format which is a pre-requisite for a handful of API functions such as SetWaitableTimer function.

If you are building a treeview showing the file system, you may find the CompareFileTime function also quite handy.

As for the rest of the functions available, here is a list of them from MSDN:

  • CompareFileTime
  • DosDateTimeToFileTime
  • FileTimeToDosDateTime
  • FileTimeToLocalFileTime
  • FileTimeToSystemTime
  • GetFileTime
  • GetLocalTime
  • GetSystemTime
  • GetSystemTimeAdjustment
  • GetSystemTimeAsFileTime
  • GetTickCount
  • GetTimeZoneInformation
  • LocalFileTimeToFileTime
  • SetFileTime
  • SetLocalTime
  • SetSystemTime
  • SetSystemTimeAdjustment
  • SetTimeZoneInformation
  • SystemTimeToFileTime
  • SystemTimeToTzSpecificLocalTime

2.2 MFC datetime Classes

The MFC framework simplified working with times considerably by introducing two wrapper classes, the CTime and COleDatetime classes. In addition, MFC included the CTimeSpan and COleDateTimeSpan, which work in conjunction with CTime and COleDateTime classes.

2.2.1 CTime

A CTime object represents an absolute time and date, based on coordinated universal time (UTC).

Microsoft provides 7 different constructors for the CTime class which amongst others, allows you to do the following:

  1. Create a time class using a Standard Library time_t calender time.
  2. Create a time class using a dos date and time.
  3. Create a time class using a Win32 SYSTEMTIME or FILETIME
  4. Create a time class using individual entries for year, month, day, hour, minute, and second.

By incorporating the ANSI time_t data type, the CTime class provides all the functionalities discussed above in section 1. It also has methods to get the time in SYSTEMTIME or FILETIME or GMT format.

In addition, this class also overloads the +, -, = , ==, <, <<, >> operators to provide many more useful features.

You can find the definition of CTime class in afx.h header file and it is as follows:

class CTime
{
public:

// Constructors
    static CTime PASCAL GetCurrentTime();

    CTime();
    CTime(time_t time);
    CTime(int nYear, int nMonth, int nDay, int nHour, int nMin, int nSec,
        int nDST = -1);
    CTime(WORD wDosDate, WORD wDosTime, int nDST = -1);
    CTime(const CTime& timeSrc);

    CTime(const SYSTEMTIME& sysTime, int nDST = -1);
    CTime(const FILETIME& fileTime, int nDST = -1);
    const CTime& operator=(const CTime& timeSrc);
    const CTime& operator=(time_t t);

// Attributes
    struct tm* GetGmtTm(struct tm* ptm = NULL) const;
    struct tm* GetLocalTm(struct tm* ptm = NULL) const;
    BOOL GetAsSystemTime(SYSTEMTIME& timeDest) const;

    time_t GetTime() const;
    int GetYear() const;
    int GetMonth() const;       // month of year (1 = Jan)
    int GetDay() const;         // day of month
    int GetHour() const;
    int GetMinute() const;
    int GetSecond() const;
    int GetDayOfWeek() const;   // 1=Sun, 2=Mon, ..., 7=Sat

// Operations
    // time math
    CTimeSpan operator-(CTime time) const;
    CTime operator-(CTimeSpan timeSpan) const;
    CTime operator+(CTimeSpan timeSpan) const;
    const CTime& operator+=(CTimeSpan timeSpan);
    const CTime& operator-=(CTimeSpan timeSpan);
    BOOL operator==(CTime time) const;
    BOOL operator!=(CTime time) const;
    BOOL operator<(CTime time) const;
    BOOL operator>(CTime time) const;
    BOOL operator<=(CTime time) const;
    BOOL operator>=(CTime time) const;

    // formatting using "C" strftime
    CString Format(LPCTSTR pFormat) const;
    CString FormatGmt(LPCTSTR pFormat) const;
    CString Format(UINT nFormatID) const;
    CString FormatGmt(UINT nFormatID) const;

#ifdef _UNICODE
    // for compatibility with MFC 3.x
    CString Format(LPCSTR pFormat) const;
    CString FormatGmt(LPCSTR pFormat) const;
#endif

    // serialization
#ifdef _DEBUG
    friend CDumpContext& AFXAPI operator<<(CDumpContext& dc, CTime time);
#endif
    friend CArchive& AFXAPI operator<<(CArchive& ar, CTime time);
    friend CArchive& AFXAPI operator>>(CArchive& ar, CTime& rtime);

private:
    time_t m_time;
};

2.2.2 CTimeSpan

The CTimeSpan class is used in conjunction with CTime to perform subtractions and additions. It represents a relative time span as the name suggests and provides four constructors, one of which incorporates the ANSI time_t data type. CTimeSpan is also defined in afx.h as follows:

class CTimeSpan
{
public:

// Constructors
    CTimeSpan();
    CTimeSpan(time_t time);
    CTimeSpan(LONG lDays, int nHours, int nMins, int nSecs);

    CTimeSpan(const CTimeSpan& timeSpanSrc);
    const CTimeSpan& operator=(const CTimeSpan& timeSpanSrc);

// Attributes
    // extract parts
    LONG GetDays() const;   // total # of days
    LONG GetTotalHours() const;
    int GetHours() const;
    LONG GetTotalMinutes() const;
    int GetMinutes() const;
    LONG GetTotalSeconds() const;
    int GetSeconds() const;

// Operations
    // time math
    CTimeSpan operator-(CTimeSpan timeSpan) const;
    CTimeSpan operator+(CTimeSpan timeSpan) const;
    const CTimeSpan& operator+=(CTimeSpan timeSpan);
    const CTimeSpan& operator-=(CTimeSpan timeSpan);
    BOOL operator==(CTimeSpan timeSpan) const;
    BOOL operator!=(CTimeSpan timeSpan) const;
    BOOL operator<(CTimeSpan timeSpan) const;
    BOOL operator>(CTimeSpan timeSpan) const;
    BOOL operator<=(CTimeSpan timeSpan) const;
    BOOL operator>=(CTimeSpan timeSpan) const;

#ifdef _UNICODE
    // for compatibility with MFC 3.x
    CString Format(LPCSTR pFormat) const;
#endif
    CString Format(LPCTSTR pFormat) const;
    CString Format(UINT nID) const;

    // serialization
#ifdef _DEBUG
    friend CDumpContext& AFXAPI operator<<(CDumpContext& dc,CTimeSpan timeSpan);
#endif
    friend CArchive& AFXAPI operator<<(CArchive& ar, CTimeSpan timeSpan);
    friend CArchive& AFXAPI operator>>(CArchive& ar, CTimeSpan& rtimeSpan);

private:
    time_t m_timeSpan;
    friend class CTime;
};

2.2.3 COleDateTime

The other major date time class incorporated into MFC is the COleDateTime class. The COleDateTime class can hold any date from January 1, 100 to December 31, 9999, but the reason for its existence, as its name may suggest, is to provide Automation support.

Now in the world of COM and Automation, Microsoft decided to define a big union of many different types known as the VARIANT data type which included short, long, float, double and DATE to name a few. And COleDateTime is the one date/time class capable of working with the DATE data type.

The DATE type is implemented using an 8-byte floating-point number. To make things even more complicated, days are represented by whole number increments starting with 30 December 1899, midnight as time zero. Hour values are expressed as the absolute value of the fractional part of the number.

If you look at the list of constructors for COleDateTime, you may notice a striking resemblance with CTime, with the exception of these two constructors:

COleDateTime( const VARIANT& varSrc );
COleDateTime( DATE dtSrc );

These two differences are indeed the reason for its existence.

As of version 4.0 of MFC, the database programming functions of MFC use COleDateTime. It should be noted that although COleDateTime is a great class to work with, it has one big flaw and that is it ignores day light saving times, so depending on your usage of it, you have to find a way to get around this little BIG bug.

COleDateTime is defined in the Afxdisp.h header file as follows:

/////////////////////////////////////////////////////////////////////////////
// COleDateTime class

class COleDateTime
{
// Constructors
public:
    static COleDateTime PASCAL GetCurrentTime();

    COleDateTime();

    COleDateTime(const COleDateTime& dateSrc);
    COleDateTime(const VARIANT& varSrc);
    COleDateTime(DATE dtSrc);

    COleDateTime(time_t timeSrc);
    COleDateTime(const SYSTEMTIME& systimeSrc);
    COleDateTime(const FILETIME& filetimeSrc);

    COleDateTime(int nYear, int nMonth, int nDay,
        int nHour, int nMin, int nSec);
    COleDateTime(WORD wDosDate, WORD wDosTime);

// Attributes
public:
    enum DateTimeStatus
    {
        valid = 0,
        invalid = 1,    // Invalid date (out of range, etc.)
        null = 2,       // Literally has no value
    };

    DATE m_dt;
    DateTimeStatus m_status;

    void SetStatus(DateTimeStatus status);
    DateTimeStatus GetStatus() const;

    BOOL GetAsSystemTime(SYSTEMTIME& sysTime) const;

    int GetYear() const;
    int GetMonth() const;       // month of year (1 = Jan)
    int GetDay() const;         // day of month (0-31)
    int GetHour() const;        // hour in day (0-23)
    int GetMinute() const;      // minute in hour (0-59)
    int GetSecond() const;      // second in minute (0-59)
    int GetDayOfWeek() const;   // 1=Sun, 2=Mon, ..., 7=Sat
    int GetDayOfYear() const;   // days since start of year, Jan 1 = 1

// Operations
public:
    const COleDateTime& operator=(const COleDateTime& dateSrc);
    const COleDateTime& operator=(const VARIANT& varSrc);
    const COleDateTime& operator=(DATE dtSrc);

    const COleDateTime& operator=(const time_t& timeSrc);
    const COleDateTime& operator=(const SYSTEMTIME& systimeSrc);
    const COleDateTime& operator=(const FILETIME& filetimeSrc);

    BOOL operator==(const COleDateTime& date) const;
    BOOL operator!=(const COleDateTime& date) const;
    BOOL operator<(const COleDateTime& date) const;
    BOOL operator>(const COleDateTime& date) const;
    BOOL operator<=(const COleDateTime& date) const;
    BOOL operator>=(const COleDateTime& date) const;

    // DateTime math
    COleDateTime operator+(const COleDateTimeSpan& dateSpan) const;
    COleDateTime operator-(const COleDateTimeSpan& dateSpan) const;
    const COleDateTime& operator+=(const COleDateTimeSpan dateSpan);
    const COleDateTime& operator-=(const COleDateTimeSpan dateSpan);

    // DateTimeSpan math
    COleDateTimeSpan operator-(const COleDateTime& date) const;

    operator DATE() const;

    int SetDateTime(int nYear, int nMonth, int nDay,
        int nHour, int nMin, int nSec);
    int SetDate(int nYear, int nMonth, int nDay);
    int SetTime(int nHour, int nMin, int nSec);
    BOOL ParseDateTime(LPCTSTR lpszDate, DWORD dwFlags = 0,
        LCID lcid = LANG_USER_DEFAULT);

    // formatting
    CString Format(DWORD dwFlags = 0, LCID lcid = LANG_USER_DEFAULT) const;
    CString Format(LPCTSTR lpszFormat) const;
    CString Format(UINT nFormatID) const;

// Implementation
protected:
    void CheckRange();
    friend COleDateTimeSpan;
};

2.2.4 COleDateTimeSpan

The so-called companion class used in conjunction with COleDateTime, is COleDateTimeSpan. And it provides more or less the same functionality that CTimeSpan provides for CTime. The one major exception is with the inclusion of SetStatus and GetStatus functions and you can trace the roots of that back to the fact that COleDateTime was built for automation support. Thus, GetStatus() can tell you whether a CTimeDateSpan value was set to a correct value or not.

As with its companion class, COleDateTimeSpan is also defined in Afxdisp.h.

3. Conclusion

Well, as you can see the date time world in windows is a bit complex. On the one hand, you have a series of datetimes with their base times defined at epoch. On the other hand, you have a few other stuff with their base defined on Jan 1, 1900 while still we have one that goes all the way back to 1601.

We even have a class that doesn't support DayLight Savings Time and we also have the DATE data type defined to start 2 days before Jan 1, 1900 which adds a bit more complication as well.

I am not exactly sure of the reasons behind these decisions, but nevertheless, when working with these various datatypes and classes, you have to take some extra care and spend a few minutes thinking about what you are doing.

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