I was wondering if there is any relatively easy and short date comparison functions in C++.
My dates are of type char*
, and have the following format: DD
How about an efficient solution? Your fixed size dates only require 8 chars if you ignore the slashes. So with a little shifting and byte swapping you can compare them as 64 bit ints. This is faster than comparing as strings.
using std::cout;
using std::endl;
typedef unsigned __int16 U2;
typedef unsigned __int32 U4;
typedef unsigned __int64 U8;
#define bswap2 _byteswap_ushort
#define bswap4 _byteswap_ulong
#define bswap8 _byteswap_uint64
const int YYYYMMDD = 0;
const int YYYY_MM_DD = 1;
const int DDMMYYYY = 2;
const int DD_MM_YYYY = 3;
// compiler will optimize the if's out.
template <int FMT>
U8 DateToInt(char* sz) {
if (FMT == YYYYMMDD) {
return bswap8(*(U8*)sz);
}
if (FMT == YYYY_MM_DD) {
U4 y = *(U4*)sz, m = *(U2*)(sz + 5), d = *(U2*)(sz + 8);
return ((U8)bswap4(y) << 32) | (bswap2(m) << 16) | bswap2(d);
}
if (FMT == DD_MM_YYYY) {
U4 y = *(U4*)(sz + 6), m = *(U2*)(sz + 3), d = *(U2*)sz;
return ((U8)bswap4(y) << 32) | (bswap2(m) << 16) | bswap2(d);
}
}
template<int FMT1, int FMT2 = FMT1>
__int64 CompareDate(char* sz1, char* sz2) {
return DateToInt<FMT1>(sz1) - DateToInt<FMT2>(sz2);
}
void main() {
cout << CompareDate<YYYYMMDD>("20151025", "20151026") << endl;
cout << CompareDate<YYYYMMDD>("20151025", "20151024") << endl;
cout << CompareDate<YYYYMMDD, YYYY_MM_DD>("20151025", "2015/10/26") << endl;
cout << CompareDate<YYYYMMDD, YYYY_MM_DD>("20151025", "2015/10/24") << endl;
cout << CompareDate<YYYYMMDD, DD_MM_YYYY>("20151025", "26/10/2015") << endl;
cout << CompareDate<YYYYMMDD, DD_MM_YYYY>("20151025", "24/10/2015") << endl;
}
output
-1
1
-1
1
-1
1
Parsing is usually done on streams, not strings, but you can use a stringstream
.
std::istringstream date_s( "04\\10\\1984" );
struct tm date_c;
date_s >> std::get_time( &date_c, "%d\\%m\\%Y" );
std::time_t seconds = std::mktime( & date_c );
Now you can compare seconds using <
to determine which was earlier.
Note, std::get_time is new in C++11. It is defined in terms of strptime
, which is from POSIX but not part of the C99 standard. You can use strptime if a C++11 library is not available. If you're brave, you can also use the std::time_get
facet… it's ugly though.
If you don't want to know anything about the dates other than which is earlier, you can use std::lexicographical_compare
. It would be a one-liner but the function name is so long.
// return true if the date string at lhs is earlier than rhs
bool date_less_ddmmyyyy( char const *lhs, char const *rhs ) {
// compare year
if ( std::lexicographical_compare( lhs + 6, lhs + 10, rhs + 6, rhs + 10 ) )
return true;
if ( ! std::equal( lhs + 6, lhs + 10, rhs + 6 ) )
return false;
// if years equal, compare month
if ( std::lexicographical_compare( lhs + 3, lhs + 5, rhs + 3, rhs + 5 ) )
return true;
if ( ! std::equal( lhs + 3, lhs + 5, rhs + 3 ) )
return false;
// if months equal, compare days
return std::lexicographical_compare( lhs, lhs + 2, rhs, rhs+2 );
}
See also how to convert datetime to unix timestamp in c? .
If this is really a fixed format, you can do it with simple C string comparison
int date_cmp(const char *d1, const char *d2)
{
int rc;
// compare years
rc = strncmp(d1 + 6, d2 + 6, 4);
if (rc != 0)
return rc;
// compare months
rc = strncmp(d1 + 3, d2 + 3, 2);
if (rc != 0)
return rc;
// compare days
return strncmp(d1, d2, 2);
}
This works like strncmp. It returns a value less than 0, if d1
is earlier than d2
, 0 if both are the same date, and a value greater than 0, if d1
is later than d2
.
Another approach would be to convert it with strptime and mktime to time_t
and compare these with difftime
struct tm tm;
time_t t1, t2;
strptime(d1, "%d\\%m\\%Y", &tm);
t1 = mktime(&tm);
// do the same with d2
double diff = difftime(t1, t2);
You need to extract the numeric data from the string. Worst case scenario is a bunch of loops and string to integer conversion functions.
You can do it easily with sscanf and sprintf. If you're used to printf and scanf then this is simple to understand, and you can easily adapt it to other cases. There are no secret magic function calls.
#include <stdio.h>
void main()
{
char* date1 = "9\\12\\2012";
char* date2 = "6\\11\\2013";
int day1,month1,year1;
int day2,month2,year2;
sscanf(date1,"%d\\%d\\%d",&day1,&month1,&year1); //reads the numbers
sscanf(date2,"%d\\%d\\%d",&day2,&month2,&year2); //from the string
if (year1<year2 || month1<month2 || day1<day2) //compares 2 dates
{
printf("date1 < date2\n");
}
else
{
printf("date1 >= date2\n");
}
char newdate[15];
sprintf(newdate,"%d\\%d\\%d",13,2,1998); //make a date string from numbers
printf("%s\n",newdate);
}