Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Mobile

Alternative Timestamp Class with no MFC

3.91/5 (4 votes)
10 Aug 2007CPOL18 min read 1   316  
This article describes a timestamp class that can be used as an alternative to the standard C date time functions.

Introduction

This is a timestamp class that stores dates and times from year 0 to year 9999 and beyond. It stores timestamps up to an accuracy of hundredths of a second. It can be constructed using a range of different formats, it can output individual components as well as strings in various formats, and it includes comparison operators as well as some basic date arithmetic (add day, add month, etc.). This class is aware of leap years, but not leap seconds.

Background

My current main project involves a backend/frontend architecture, and a requirement is that the backend can be compiled on systems other than Windows MFC. When I started the project, I hunted around for a class to hold dates and times that wasn't an MFC class, and I ended up using time_t (http://en.wikipedia.org/wiki/Time_t). Unfortunately, I am now having lots of problems with my class concerning people with dates of birth before 1970, and after some research, I have found out that time_t only works from 1970-2038. This isn't good enough for my application.

My backend application uses SQLite for storing data, and dates and times are stored in timestamp fields. I have been unable to find a class that will store timestamp data in C++, and I have ended up writing my own, which is presented here.

Using the code

This is a simple class with one header file and one CPP file. You should compile these as part of your application.

I have provided full documentation for each individual function later on in this article. This is a quick demonstration of using the class in your project.

The download contains many files, the most important are:

  • RJMFTime.cpp
  • RJMFTime.h

You should add RJMFTimp.cpp and RJMFTime.h to your project. The complete download is a Visual Studio 6.0 project that contains all the files necessary to compile TimeTest. TimeTest is a program designed to fully test the class. Once you have added RJMFTime.cpp and RJMFTime.h to your project, you need to include RJMFTime.h in every file that uses the class. You can then use the class as shown in the following code snippets.

What's date for tomorrow?

C++
#include <iostream>
#include <math.h>
#include "RJMFTime.h"

int main() {
    RJMF_TimeStamp TSO;
    TSO.AddDay(1);
    printf("%s\n",TSO.c_str());
    return 0;
};

This example shows the default constructor, which initialises the class to the current date and time. It then uses the AddDay function to add one day to the current date. You can also use this function with a negative parameter to subtract days. You can also find year, month, hour, minute, second, and millisecond versions of this function. Finally, it uses the c_str function to output the result to a string. The c_str function is provided as an alternative to strftime, and is equivalent to strftime(buf, "%d-%b-%Y %H:%M:%!"). It's really for lazy programmers who haven't got round to working out the format string.

Is this date valid?

C++
#include <iostream>
#include <math.h>
#include "RJMFTime.h"
#define SAFE_DELETE(a) if (a!=NULL) {delete a; a=NULL;};

int main() {
    char *sDates[] = {"29-FEB-1104", 
         "29-FEB-1999",
         "29-FEB-2000",
         "29-FEB-2002",
         "29-FEB-2004",
         "29-FEB-2100",
         "29-FEB-2101" };
    RJMF_TimeStamp* pTS1 = NULL;
    for (int c=0;c<7;c++) {
        pTS1 = new RJMF_TimeStamp(sDates[c],"DD-MON-YYYY");
        if (pTS1->valid()) {
            printf("%s is valid (%s)\n",sDates[c],pTS1->c_str());
        } else {
            printf("%s is NOT valid\n",sDates[c],pTS1->c_str());
        };
        SAFE_DELETE(pTS1);
    };
    return 0;
};

This example uses seven dates, and checks to see if they are valid dates or not. (It also happens to test that the class obeys the leap year rules.) It loops seven times, creating a RJMF_Timestamp object from a string. It then uses the valid function to determine if the date is a valid date. You can use a similar method to test if dates that the user has input are valid dates.

How many days until Christmas?

C++
#include <iostream>
#include <math.h>
#include "RJMFTime.h"

int main() {
    RJMF_TimeStamp dToday;
    RJMF_TimeStamp dChristmass(0,0,0,0,31,12,dToday.GetYear());
    char buf[100];
    dToday.strftime(buf, "%A %d-%b-%Y %H:%M:%!");
    printf("Today is %s\n",buf);
    unsigned int d = dChristmass.DiffDay(&dToday);
    printf("There are %u days until christmass\n",d);
    return 0;
};

This example creates an object referring to today, and then uses the GetYear function to find out what year it is. There are similar GetMonth, GetDay, etc. functions. It then shows an alternative method for creating the class specifying the exact date to initialise the class to. In this case, 31-Dec of the current year. (The function also takes the time, which in this case is 0 hdth seconds, 0 seconds, 0 minutes, and 0 hours.) Rather than use the c_str function, it creates a buffer and uses the strftime function. This gives us more control over the output and allows us to include the day of the week. Finally, the DiffDay function is used to calculate the number of days between two dates. You will also find the DiffYear, DiffMonth, etc. functions included in the class.

List the dates between Joe Blogg's birthday and my birthday

C++
#include <iostream>
#include <math.h>
#include "RJMFTime.h"

int main() {
    RJMF_TimeStamp dMe("22-NOV-1979","DD-MON-YYYY");
    RJMF_TimeStamp dBlogs("05-NOV-1979 15:31:11.25", 
                          "DD-MON-YYYY hh:mm:ss.ss");
    RJMF_TimeStamp dBase;
    dBase = dBlogs;
    dBase.AddDay(1);
    while (dBase<dMe) {
        printf("%s\n",dBase.c_str());
        dBase.AddDay(1);
    };
    return 0;
};

This example starts by showing a third way to create the object. This method uses a string in a particular format. This is useful with getting dates from, say, SQLite. There are a set number of format strings coded in. See later in this article for exact documentation. It then uses the assignment operator (=) and the less than operator (<). The class includes a range or other operators (<= >= > ==, etc.). As well as showing the c_str and AddDay functions we have seen before, this example shows a neat way of looping through dates.

Hang on a minute (or 10 seconds if you would prefer)

C++
#include <iostream>
#include <math.h>
#include "RJMFTime.h"
#define SAFE_DELETE(a) if (a!=NULL) {delete a; a=NULL;};

int main() {
    printf("About to wait for 10 secs\n");
    RJMF_TimeStamp* pCT = NULL;
    RJMF_TimeStamp* pET = new RJMF_TimeStamp();
    pET->AddSecond(10);
    pCT = new RJMF_TimeStamp();
    while ((*pCT)<(*pET)) {
        SAFE_DELETE(pCT);
        pCT = new RJMF_TimeStamp();
    };
    SAFE_DELETE(pCT);
    SAFE_DELETE(pET);
    printf("10 seconds is up");
};

This final example shows how to use the class to make your program pause for 10 seconds. There are probably lots of better ways of doing this, and I wouldn't recommend using this method. This shows the fact that the class also holds the time as well as the date. It works by creating an object with the current time and date and incrementing it by 10 seconds. It then continually creates and destroys objects with the current time and date, and uses the comparison operator to determine if the time we are waiting for has passed. This method should work even over year boundaries. One note is that the constructor with no parameters still relies on the <codec_time> data type. This means that this will only work between 1970 and 2038. See later in the article for more explanation.

Basic design

Most date/time classes convert the date to a number of seconds after a pre-defined start time. This is why the time_t structure can't work before 1970. The start time was 1970. The reason for this is because it makes date calculations far simpler. Rather than use this design, I have developed the class to store the year and then the number of hundredth of a second that have elapsed since the start of the year. This makes all the calculations more complicated, but it means it will work with a much wider range of dates.

The class has three private member variables:

C++
unsigned int m_year;
unsigned int m_hdth_secs; //hdthseconds since start of year
bool m_valid;

The year is held as an unsigned int.

m_valid simply holds true if the class has been properly constructed, false otherwise. This can be useful to check if it has been set to a valid date.

m_year holds the current year. This is unsigned, but the class may work with signed values. (I don't have a need for storing dates before the year 0.)

One interesting side effect of this method is that the date 10-Jan-2000 has the same m_hdth_secs value as 10-Jan-2001. However, 10-Dec-2000 doesn't have the same m_hdth_secs value as 10-Dec-2001. This is because the year 2000 is a leap year and the m_hdth_secs value for 10-Dec-2000 has one extra day (24 * 60 * 60 * 100 = 8,640,000 hdthseconds) included.

Individual function descriptions

Each individual function is described in this section. An example of the use of each function is also given.

RJMF_TimeStamp();

When the constructor is called with no parameters, the standard C localtime function is used to initialise the object with the current date and time. This means that this will only work from 1970-2038 as explained in http://en.wikipedia.org/wiki/Time_t. This should always produce a valid date. This only populates down to the nearest second.

C++
#include <iostream>
#include <math.h>
#include "RJMFTime.h"

int main() {
    RJMF_TimeStamp TSO;
    printf("%s\n",TSO.c_str());
    return 0;
};

RJMF_TimeStamp(unsigned int hdth, unsigned int sec, unsigned int minutes, unsigned int hours, unsigned int mday, unsigned int month, unsigned int year);

This constructor is used when you want to initialise the object to a specific date. The following should be passed as parameters:

FieldAllowed ValuesNotes
hdth0-99
sec0-59
minutes0-59
hours0-23In 24 hour format
mday1-31Day of month
month1-12
year0-9999Higher years may still work

Once this function has been called, you should use the valid function to check if the date you entered was a valid date.

C++
#include <iostream>
#include <math.h>
#include "RJMFTime.h"

int main() {
    RJMF_TimeStamp TSO(55, 31, 31, 20, 30, 11, 2007);
    if (TSO.valid()) {
        printf("%s\n",TSO.c_str());        
    } else {
        printf("This date is not valid\n");    
    };
    return 0;
};

RJMF_TimeStamp(char *pTime, char*pFormat = NULL);

This constructor initialises the object based on a string. It will only work with the following hard coded format strings:

Format StringExample
"DD-MON-YYYY30-NOV-2007
"DD-MON-YY"30-NOV-07
"DD/MM/YYYY"30/11/2007
"DD/MM/YY"30/11/07
"DD-MON-YYYY hh:mm:ss.ss"30-NOV-2007 20:31:31.55
"DD-MON-YY hh:mm:ss.ss"30-NOV-07 20:31:31.55
"DD/MM/YYYY hh:mm:ss.ss"30/11/2007 20:31:31.55
"DD/MM/YY hh:mm:ss.ss"30/11/07 20:31:31.55
"DD-MON-YYYY hh:mm:ss"30-NOV-2007 20:31:31
"DD-MON-YY hh:mm:ss"30-NOV-07 20:31:31
"DD/MM/YYYY hh:mm:ss"30/11/2007 20:31:31
"DD/MM/YY hh:mm:ss"30/11/07 20:31:31
"YYYY-MM-DD"2007-11-30
"YYYY-MM-DD HH:MM"2007-11-30 20:31
"YYYY-MM-DD HH:MM:SS"2007-11-30 20:31:31
"YYYY-MM-DD HH:MM:SS.SSS"2007-11-30 20:31:31.550
"YYYY-MM-DDTHH:MM"2007-11-30T:20:31
"YYYY-MM-DDTHH:MM:SS"2007-11-30T20:31:31
"YYYY-MM-DDTHH:MM:SS.SSS"2007-11-30T20:31:31.550
"HH:MM"20:31
"HH:MM:SS"20:31:31
"HH:MM:SS.SSS"20:31:31.550

Note: I have included format strings from http://www.sqlite.org/cvstrac/wiki?p=DateAndTimeFunctions. The format strings are case sensitive. Two digit years are assumed to be in the range 1990-2069.

Once this function has been called, you should use the valid function to check if the date you entered was a valid date.

C++
#include <iostream>
#include <math.h>
#include "RJMFTime.h"

int main() {
    RJMF_TimeStamp TSO("30-NOV-2007 20:31:31.55", 
                       "DD-MON-YYYY hh:mm:ss.ss");
    if (TSO.valid()) {
        printf("%s\n",TSO.c_str());        
    } else {
        printf("This date is not valid\n");    
    };
    return 0;
};

RJMF_TimeStamp& operator= (const RJMF_TimeStamp& o);

This is the assignment operator.

C++
#include <iostream>
#include <math.h>
#include "RJMFTime.h"

int main() {
    RJMF_TimeStamp TSO("30-NOV-2007 20:31:31.55", 
                       "DD-MON-YYYY hh:mm:ss.ss");
    RJMF_TimeStamp TS1;
    TS1 = TSO;
    printf("%s\n",TS1.c_str());
    return 0;
};

void AddYear(int num);

This function adds a year to the current date. It can be used with a negative number of years. In most cases, the month and the day of month will not change as a result. However, in a small number of cases, this is not possible. If the date is 29 February, the function checks to see if the year it lands on is a leap year. In the case where it is a leap year, it proceeds as normal; however, if it is not a leap year, it will set the date to the first of March. This leads to the unusual case where adding a year and then subtracting a year to a date results in different dates. This case is shown in the example.

C++
#include <iostream>
#include <math.h>
#include "RJMFTime.h"

int main() {
    RJMF_TimeStamp TSO("29-FEB-2000","DD-MON-YYYY");
    printf("Start - %s\n",TSO.c_str());
    TSO.AddYear(1);
    printf("+1 - %s\n",TSO.c_str());
    TSO.AddYear(1);
    printf("-1 - %s - Unusal as it is not where we started\n",TSO.c_str());
    RJMF_TimeStamp TS1("29-FEB-2000","DD-MON-YYYY");
    printf("\nStart - %s\n",TS1.c_str());
    TS1.AddYear(4);
    printf("+4 - %s\n",TS1.c_str());
    TS1.AddYear(4);
    printf("-4 - %s - still where we started because we added 4 years\n",TS1.c_str());
    return 0;
};

void AddMonth(int num);

This function adds a month to the current date. This usually is as simple as incrementing the month number, but is not always the case as some months have more days than others. If the day of the month is not a valid day in the new month, the last day of the new month is used. This is shown in the example. To subtract months, simply pass this function a negative value.

C++
#include <iostream>
#include <math.h>
#include "RJMFTime.h"

int main() {
    RJMF_TimeStamp TSO("30-DEC-1999","DD-MON-YYYY");
    printf("Start - %s\n",TSO.c_str());
    TSO.AddMonth(2);
    printf("+2 - %s\n",TSO.c_str());
    TSO.AddMonth(-2);
    printf("-2 - %s - Unusal as it is not where we started\n",TSO.c_str());
    return 0;
};

void AddDay(int num);

This function adds a number of days to the currently held date. To subtract days, simply pass a negative value.

C++
#include <iostream>
#include <math.h>
#include "RJMFTime.h"

int main() {
    RJMF_TimeStamp TSO("30-DEC-1999","DD-MON-YYYY");
    printf("Start - %s\n",TSO.c_str());
    TSO.AddDay(365);
    printf("365 days later - %s\n",TSO.c_str());
    TSO.AddDay(-365);
    printf("back to start - %s\n",TSO.c_str());
    return 0;
};

void AddHour(int num);

This function adds a number of hours to the currently held date. To subtract hours, simply pass a negative value. It is possible to give it a number of hours above 24. This function uses the AddDay function where necessary.

C++
#include <iostream>
#include <math.h>
#include "RJMFTime.h"

int main() {
    RJMF_TimeStamp TSO("30-DEC-1999","DD-MON-YYYY");
    printf("Start - %s\n",TSO.c_str());
    TSO.AddHour(48);
    printf("%s\n",TSO.c_str());
    TSO.AddHour(-48);
    printf("back to start - %s\n",TSO.c_str());
    return 0;
};

void AddMinute(int num);

This function adds a number of minutes to the currently held date. To subtract minutes, simply pass a negative value. It is possible to give it a number of minutes above 59. This function uses the AddHour function where necessary.

C++
#include <iostream>
#include <math.h>
#include "RJMFTime.h"

int main() {
    RJMF_TimeStamp TSO("30-DEC-1999","DD-MON-YYYY");
    printf("Start - %s\n",TSO.c_str());
    TSO.AddMinute(121);
    printf("%s\n",TSO.c_str());
    TSO.AddMinute(-121);
    printf("back to start - %s\n",TSO.c_str());
    return 0;
};

void AddSecond(int num);

This function adds a number of seconds to the currently held date. To subtract seconds, simply pass a negative value. It is possible to give it a number of seconds above 59. This function uses the AddMinute function where necessary.

C++
#include <iostream>
#include <math.h>
#include "RJMFTime.h"

int main() {
    RJMF_TimeStamp TSO("30-DEC-1999","DD-MON-YYYY");
    printf("Start - %s\n",TSO.c_str());
    TSO.AddSecond(95);
    printf("%s\n",TSO.c_str());
    TSO.AddSecond(-95);
    printf("back to start - %s\n",TSO.c_str());
    return 0;
};

void AddHdthsecond(int num);

This function adds a number of hdthseconds to the currently held date. To subtract hdthseconds, simply pass a negative value. It is possible to give it a number of hdthseconds above 99. This function uses the AddSecond function where necessary.

C++
#include <iostream>
#include <math.h>
#include "RJMFTime.h"

int main() {
    RJMF_TimeStamp TSO("30-DEC-1999","DD-MON-YYYY");
    printf("Start - %s\n",TSO.c_str());
    TSO.AddHdthsecond(295);
    printf("%s\n",TSO.c_str());
    TSO.AddHdthsecond(-295);
    printf("back to start - %s\n",TSO.c_str());
    return 0;
};

float DiffYear(RJMF_TimeStamp* pO);

This function returns the number of years between the date it is called on and the date it is passed. The whole number returned is the number of years. You can use int(ans) to just get the number as an integer, like in the example. The fractional part represents the fractional part of the year. I have included it mainly for sorting purposes (it enables me to sort all the 14 year olds so the next one to turn 15 is at the top of the list). In the fractional part, one day is worth either 1/365 or 1/366 depending on if the day is in a leap year or not.

C++
#include <iostream>
#include <math.h>
#include "RJMFTime.h"

int main() {
    RJMF_TimeStamp dMe("22-NOV-1979","DD-MON-YYYY");
    RJMF_TimeStamp dO;
    float ans = dO.DiffYear(&dMe);
    printf("I was %f years old when this program ran, "
           "making me %d years old.\n",ans,int(ans));
    return 0;
};

unsigned int DiffMonth(RJMF_TimeStamp* pO);

This function returns the number of months between the date it is called on and the date it is passed. It works in a different way to the DiffYear and DiffDay functions since it runs a loop and literally counts the months. This enables it to deal with varying number of days in each month as well as leap years etc. It is sometimes useful to split the number of months into years and months, and this can be done using int(ans/12) and (ans % 12) as in the example.

C++
#include <iostream>
#include <math.h>
#include "RJMFTime.h"

int main() {
    RJMF_TimeStamp dMe("22-NOV-1979","DD-MON-YYYY");
    RJMF_TimeStamp dO;
    unsigned ans = dO.DiffMonth(&dMe);
    printf("I have been alive for %u months (%d years and %d months)\n", 
           ans, int(ans/12), ans % 12);
    return 0;
};

unsigned int DiffDay(RJMF_TimeStamp* pO);

This function returns the number of days between the date it is called on and the date it is passed.

C++
#include <iostream>
#include <math.h>
#include "RJMFTime.h"

int main() {
    RJMF_TimeStamp dMe("22-NOV-1979","DD-MON-YYYY");
    RJMF_TimeStamp dO;
    unsigned ans = dO.DiffDay(&dMe);
    printf("I have been alive for %u days\n",ans);
    return 0;
};

unsigned int DiffSecond(RJMF_TimeStamp* pO);

This function returns the number of seconds between the date it is called on and the date it is passed. This function has been included since the number of milliseconds is sometimes too huge to hold the correct answer.

C++
#include <iostream>
#include <math.h>
#include "RJMFTime.h"

int main() {
    RJMF_TimeStamp dMe("22-NOV-1979","DD-MON-YYYY");
    RJMF_TimeStamp dO;
    unsigned int ans = dO.DiffSecond(&dMe);
    printf("I have been alive for %u Seconds\n",ans);
    return 0;
};

unsigned int DiffHdthSecond(RJMF_TimeStamp* pO);

This function returns the number of hdthseconds between the date it is called on and the date it is passed. An unsigned int value can store numbers up to 4294967295. This means that if dates are more than 400 odd days apart, you will get integer role over causing the value to be wrong. In these cases, use the second function which works up to a difference of about 40,000 days (about 100 years).

C++
#include <iostream>
#include <math.h>
#include "RJMFTime.h"

int main() {
    RJMF_TimeStamp dMe("22-NOV-1979","DD-MON-YYYY");
    RJMF_TimeStamp dMe2("22-NOV-2000","DD-MON-YYYY");
    RJMF_TimeStamp dMe3("22-NOV-2007","DD-MON-YYYY");
    RJMF_TimeStamp dO;
    unsigned int ans = dO.DiffHdthSecond(&dMe);
    printf("%u - Seconds since 1979\n",ans);
    ans = dO.DiffHdthSecond(&dMe2);
    printf("%u - Hang on, from 2000 it is more\n",ans);
    ans = dO.DiffHdthSecond(&dMe3);
    printf("%u - This is due to integer role over.");
    printf("It's right for 2006, only works for about 400 days\n",ans);
    return 0;
};

bool operator== (const RJMF_TimeStamp& o) const;

The comparison operator compares two objects, and returns true if they are exactly equal, and false otherwise. This comparison is done down to the millisecond. When using comparison operators, it is always good to keep times in mind.

C++
#include <iostream>
#include <math.h>
#include "RJMFTime.h"

int main() {
    RJMF_TimeStamp dMe("22-NOV-1979","DD-MON-YYYY");
    RJMF_TimeStamp dNow;
    if (dMe==dNow) {
    //note will probally never work 
    //as dNow has the current time in it as well!

        printf("I am being born right now\n");
    } else {
        printf("I am not being born right now\n");
    };
    RJMF_TimeStamp dBDayThisYear(0,0,0,0,22,11,dNow.GetYear());
    RJMF_TimeStamp dNowDateOnly(0,0,0,0,dNow.GetDay(), 
                   dNow.GetMonth(),dNow.GetYear());
    if (dBDayThisYear==dNowDateOnly) {
        printf("It's my birthday\n");
    } else {
        printf("It's not my birthday\n");
    };
    if (dMe==dMe) {
        printf("Just checking...\n");
    } else {
        printf("Something is wrong\n");
    };
    return 0;
};

bool operator!= (const RJMF_TimeStamp& o) const;

This operator returns false if the classes hold exactly the same date and time down to the millisecond, true otherwise. When using comparison operators, it is always good to keep times in mind.

C++
#include <iostream>
#include <math.h>
#include "RJMFTime.h"

int main() {
    RJMF_TimeStamp dMe("22-NOV-1979","DD-MON-YYYY");
    RJMF_TimeStamp dNow;
    if (dMe!=dNow) {
    //note will probally never work as dNow
    // has the current time in it as well!
        printf("I am not being born right now\n");
    } else {
        printf("I am being born right now\n");
    };
    RJMF_TimeStamp dBDayThisYear(0,0,0,0,22,11,dNow.GetYear());
    RJMF_TimeStamp dNowDateOnly(0,0,0,0,dNow.GetDay(), 
                   dNow.GetMonth(),dNow.GetYear());
    if (dBDayThisYear!=dNowDateOnly) {
        printf("It's not my birthday\n");
    } else {
        printf("It's my birthday\n");
    };
    if (dMe!=dMe) {
        printf("Something is wrong\n");
    } else {
        printf("Just checking...\n");
    };
    return 0;
};

bool operator< (const RJMF_TimeStamp& rhs ) const;

This operator returns true if the left hand timestamp is earlier than the right hand timestamp. When using comparison operators, it is always good to keep times in mind.

C++
#include <iostream>
#include <math.h>
#include "RJMFTime.h"

int main() {
    RJMF_TimeStamp dMe("22-NOV-1979","DD-MON-YYYY");
    RJMF_TimeStamp dNow;
    if (dNow<dMe) {
        printf("I don't exist yet so this code can't have been written\n");
    } else {
        printf("I exist - phew that's a relief");
    };
    return 0;
};

bool operator<= (const RJMF_TimeStamp& rhs ) const;

The less than or equal operator will return true if the left hand value is earlier than the right hand value; true if they are equal, and false otherwise.

C++
#include <iostream>
#include <math.h>
#include "RJMFTime.h"

int main() {
    RJMF_TimeStamp dMe("22-NOV-1979","DD-MON-YYYY");
    RJMF_TimeStamp dMe2("22-NOV-1979 13:34","DD-MON-YYYY hh:mm");
    if (dMe<=dMe2) {
        printf("Shows you need to check times as well as dates.\n");
        printf("%s<=%s\n",dMe.c_str(),dMe2.c_str());
    } else {
    };
    if (dMe<=dMe) {
        printf("Sanity check passed\n");
    } else {
        printf("Something is wrong\n");
    };
    return 0;
};

bool operator> (const RJMF_TimeStamp& rhs ) const;

This operator returns true if the left hand timestamp is later than the right hand timestamp. When using comparison operators, it is always good to keep times in mind.

C++
#include <iostream>
#include <math.h>
#include "RJMFTime.h"

int main() {
    RJMF_TimeStamp dMe("22-NOV-1979","DD-MON-YYYY");
    RJMF_TimeStamp dNow;
    if (dNow>dMe) {
        printf("It is after my birthday\n");
    } else {
        printf("It is before the time I am being born (or it is the time I am being born)\n");
    };
    return 0;
};

bool operator>= (const RJMF_TimeStamp& rhs ) const;

The greater than or equal operator returns true if both arguments are equal, false if the left hand argument is greater than the right hand argument, and true otherwise. When using comparison operators, it is always good to keep times in mind.

C++
#include <iostream>
#include <math.h>
#include "RJMFTime.h"

int main() {
    RJMF_TimeStamp dMe("22-NOV-1979","DD-MON-YYYY");
    RJMF_TimeStamp dNow;
    if (dNow>=dMe) {
        printf("%s>=%s\n",dNow.c_str(),dMe.c_str());
    } else {
        printf("Huh - have u been playing with the dates on your computer?\n");
    };

    return 0;
};

bool valid();

This function is used to determine if the class has been properly installed or not. If the class is set up with an invalid date, then it will return false. This is useful for checking user input. Note: The strftime function (and hence the c_str function) will return the string INVALID if it is called on an invalid date.

C++
#include <iostream>
#include <math.h>
#include "RJMFTime.h"

int main() {
    RJMF_TimeStamp dSomeDate(0,0,0,0,31,31,2007);
    if (dSomeDate.valid()) {
        printf("This is a valid date %s\n",dSomeDate.c_str());
    } else {
        printf("This date is not valid %s\n",dSomeDate.c_str());
    };
    return 0;
};

unsigned int GetYear();

The GetYear function returns the year part of the date as an unsigned integer.

C++
#include <iostream>
#include <math.h>
#include "RJMFTime.h"

int main() {
    RJMF_TimeStamp dNow;
    printf("The current year is %u\n",dNow.GetYear());
    return 0;
};

unsigned int GetMonth();

The GetMonth function returns the month part of the date as an unsigned integer. See the strftime function for information on how to output a textual representation of this data.

C++
#include <iostream>
#include <math.h>
#include "RJMFTime.h"

int main() {
    RJMF_TimeStamp dNow;
    printf("The current year is %u\n",dNow.GetYear());
    printf("The current month is %u\n",dNow.GetMonth());
    return 0;
};

unsigned int GetDay();

The GetDay function returns the day of the month (1-31) as an unsigned integer.

C++
#include <iostream>
#include <math.h>
#include "RJMFTime.h"

int main() {
    RJMF_TimeStamp dNow;
    printf("The current year is %u\n",dNow.GetYear());
    printf("The current month is %u\n",dNow.GetMonth());
    printf("The current day of month is %u\n",dNow.GetDay());
    return 0;
};

unsigned int GetDOW();

The GetDOW function returns the day of the week as an unsigned integer. See the strftime function for information on how to output a textual representation of this data.

DayReturn Value
Sunday0
Monday1
Tuesday2
Wednesday3
Thursday4
Friday5
Saturday6
C++
#include <iostream>
#include <math.h>
#include "RJMFTime.h"

int main() {
    RJMF_TimeStamp dNow;
    printf("The current year is %u\n",dNow.GetYear());
    printf("The current month is %u\n",dNow.GetMonth());
    printf("The current day of month is %u\n",dNow.GetDay());
    printf("The current day of week is %u\n",dNow.GetDOW());
    return 0;
};

unsigned int GetDOY();

The GetDOY function returns the day of year (1-366) as an unsigned integer.

C++
#include <iostream>
#include <math.h>
#include "RJMFTime.h"

int main() {
    RJMF_TimeStamp dNow;
    printf("The current year is %u\n",dNow.GetYear());
    printf("The current month is %u\n",dNow.GetMonth());
    printf("The current day of month is %u\n",dNow.GetDay());
    printf("The current day of week is %u\n",dNow.GetDOW());
    printf("The current day of year is %u\n",dNow.GetDOY());
    return 0;
};

unsigned int GetHour();

The GetHour function returns the hour part of the time as an unsigned integer.

C++
#include <iostream>
#include <math.h>
#include "RJMFTime.h"

int main() {
    RJMF_TimeStamp dNow;
    printf("The current hour is %u\n",dNow.GetHour());
    return 0;
};

unsigned int GetMinute();

The GetMinute function returns the minute part of the time as an unsigned integer.

C++
#include <iostream>
#include <math.h>
#include "RJMFTime.h"

int main() {
    RJMF_TimeStamp dNow;
    printf("The current hour is %u\n",dNow.GetHour());
    printf("The current minute is %u\n",dNow.GetMinute());
    return 0;
};

unsigned int GetSecond();

The GetSecond function returns the second part of the time as an unsigned integer.

C++
#include <iostream>
#include <math.h>
#include "RJMFTime.h"

int main() {
    RJMF_TimeStamp dNow;
    printf("The current hour is %u\n",dNow.GetHour());
    printf("The current minute is %u\n",dNow.GetMinute());
    printf("The current Second is %u\n",dNow.GetSecond());
    return 0;
};

unsigned int GetHdthsecond();

The GetHdthsecond function returns the hdthsecond part of the time as an unsigned integer. The default constructor doesn't populate the millisecond part of the time.

C++
#include <iostream>
#include <math.h>
#include "RJMFTime.h"

int main() {
    RJMF_TimeStamp dNow;
    printf("The current hour is %u\n",dNow.GetHour());
    printf("The current minute is %u\n",dNow.GetMinute());
    printf("The current Second is %u\n",dNow.GetSecond());
    printf("The Hdthsecond is %u\n",dNow.GetHdthsecond());
    printf("Note: The default constructor dosn't ");
    printf("populate down to the nearest hdthsecond\n");
    return 0;
};

unsigned int GetHdthsecondsRAW();

I created this function mainly for testing, but left it in as it may be useful. It returns the number of milliseconds that have elapsed since the start of the year. (Midnight between 31-Dec and 1-Jan). This goes up to a maximum of 100 * 60 * 60 * 24 * 366 = 31622400000, which is just inside the limit for unsigned int.

C++
#include <iostream>
#include <math.h>
#include "RJMFTime.h"

int main() {
    RJMF_TimeStamp dNow;
    printf("There have been %u hdthseconds since the start of the year.\n",
dNow.GetHdthsecondsRAW());
    return 0;
};

void strftime(char* pOutput, const char* pFormat);

This function returns the current date and time as a formatted string. I have tried to follow the format strings set out at http://uk3.php.net/strftime, but I have added my own to incorporate milliseconds. The valid format strings are listed below:

StringMeaningExample
%aShort DayFri
%ALong DayFriday
%bShort MonthAug
%BLong MonthAugust
%dDay of month03
%HHour (24)14
%IHour (12)02
%jDay of Year215
%mMonth as num08
%MMinute43
%SSecond13
%tTab\t
%wDay of week as decimal, Sunday being 05
%yShort Year07
%YLong Year2007
%%Percent%
%!Hdth Dec22.11
%£Hdth Only11
C++
#include <iostream>
#include <math.h>
#include "RJMFTime.h"

int main() {
    RJMF_TimeStamp dNow;
    char buf[255];
    dNow.strftime(buf, "%a %A %b %B %c %d %H %I %j %m %M %S %U %W %w %x %X %y %Y %% %! %£");
    printf("Today:%s\n",buf);
    return 0;
};

bool IsLeapYear();

This function returns true if the current year is a leap year, false otherwise.

C++
#include <iostream>
#include <math.h>
#include "RJMFTime.h"

int main() {
    RJMF_TimeStamp dNow;
    if (dNow.IsLeapYear()) {
        printf("%d is a leap year\n",dNow.GetYear());
    } else {
        printf("%d is not a leap year\n",dNow.GetYear());
    };
    return 0;
};

static bool IsLeapYear(unsigned int Year);

This function will return true if the year passed to it is a leap year, and false otherwise. It is a static function, and it doesn't work with the current date and time loaded into an object, so it can be used as needed. One application would be to count the leap years between two dates.

C++
#include <iostream>
#include <math.h>
#include "RJMFTime.h"

int main() {
    unsigned int num_leap_years = 0;
    for (int c=0;c<3000;c++) {
        if (RJMF_TimeStamp::IsLeapYear(c)) num_leap_years++;
    };
    printf("There are %u leap years in the first 3000 years\n",num_leap_years);
    return 0;
};

Finding and correcting errors

As with any large piece of code that is not trivially simple, there is always the possibility of bugs existing in the code. One of the reasons I didn't want to develop my own timestamp class was because classes like time_t and CTime are used by many programmers and most of the bugs will have been located and ironed out. Unfortunately, I have been forced to roll my own, and I am using this class in my programs despite being worried that bugs may still exist.

In order to test this class, I created TimeTest.cpp. (TimeTest is included in the download.) This program runs through many tests and ensures that the class gives the expected output. Whenever I make any changes to the class, I always check to ensure TimeTest continues to work, and this gives me confidence that I haven't broken anything else. You can compile and run TimeTest to ensure the class is working on your system.

If anyone wishes to use this class in their own programs, they may do so. No payment is required and you also do not need to give me any credit or even tell me that you are doing so. I would ask that if you find a bug in the code, you follow the process outlined below. This will enable me to fix the bug and share the fix with others.

  1. Identify specific cases where the class will give an incorrect output.
  2. Create a function in TimeTest.cpp that will cause TimeTest to fail due to the error you have discovered, but will pass when the error has been fixed.
  3. Email me the function and an explanation of the error. If you can suggest a fix, that may help.

If it is a genuine problem, I will respond by incorporating your test into TimeTest and altering the class so the problem is fixed. Doing this will increase the value of this class.

Algorithm improvements

Nothing's perfect and the algorithms I have used may pass the tests I have created, but they may not be the best way of achieving the desired outcome. One additional advantage of having a complete suite of tests is that when I change the way something works, I can instantly have confidence in the new way of doing things. (As long as it passes the tests!)

If anyone has any comments on how particular functions work and can suggest better ways of doing the same thing, those will be very welcome. Again, this will improve all my programs that use the class! I have included some rudimentary benchmarking into TimeTest. As part of improving the speed of the algorithms, I could alter TimeTest so it also stress tests the function in question and use the benchmark result to compare the efficiency of different algorithms.

I wish it did x

It is possible that this class could be improved by adding extra functionality. I would welcome any ideas of what else may be useful.

History

  • Article created between 6-Aug and 10-Aug 2007.
  • 11-Aug-2007 - Two main changes made. The first is I have changed the source files so that TimeTest complies with Visual Studio 6.0. (I had written it using DJGPP, and some minor changes were required to make it work with Visual Studio.) The second change was due to the fact that I made a rudimentary error of terminology and called hundredth of seconds, milliseconds. I have now been through the source and the article replacing milliseconds with hundredth of a second. This was not a code change, I just got the terminology wrong.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)