Is there any system defined function to compare two dates in C/C++?
Thanks
Here is my favorite date class (C++ only, not C):
http://howardhinnant.github.io/date.html
With this you can write programs like:
#include "date.h"
#include <cassert>
int main()
{
using namespace gregorian;
date d1 = thu[last]/mar/2011; // last Thursday in March 2011
date d2 = mar/31/2011; // March 31, 2011
assert(d1 == d2); // The last Thursday in March 2011 is 3/31/2011
d1 += month(1); // last Thursday in April 2011
assert(d1 > d2); // d1 is later than d2
assert(d1 == month(4)/28/2011); // d1 is now Apr. 28, 2011
}
The software is free to use. You don't even have to credit anyone. 1 header, 1 source.
Update
Latest version of this software is here: https://howardhinnant.github.io/date/date.html
convert the date (you pick the format) to seconds since the start of the era. use strptime and mktime.
compare the two time_t (seconds) values.
example using MON-DD-YYYY format:
CODE:
#include <time.h>
time_t to_seconds(const char *date)
{
struct tm storage={0,0,0,0,0,0,0,0,0};
char *p=NULL;
time_t retval=0;
p=(char *)strptime(date,"%d-%b-%Y",&storage);
if(p==NULL)
{
retval=0;
}
else
{
retval=mktime(&storage);
}
return retval;
}
int main()
{
char *date1="20-JUN-2006";
char *date2="21-JUN-2006";
time_t d1=to_seconds(date1);
time_t d2=to_seconds(date2);
printf("date comparison: %s %s ",date1,date2);
if(d1==d2) printf("equal\n");
if(d2>d1) printf("second date is later\n");
if(d2<d1) printf("seocnd date is earlier\n");
return 0;
}
Inspired by Bo Persson's comment:
bool compare_DD_MM_YYYY_dates(std::string date1, std::string date2)
{
assert(date1.size()>=10); assert(date2.size()>=10);
// ISO-fy dates
date1 = date1.substr(6,4) + date1.substr(3,2) + date1.substr(0,2) + date1.substr(10);
date2 = date2.substr(6,4) + date2.substr(3,2) + date2.substr(0,2) + date2.substr(10);
return date1 < date2;
}
If not fast enough, don't concatenate the substrings, but compare them directly.
Related
For example, given 2019, October, return 12 since the second Tuesday on October 2019 is 12th. I know how to do it in java. I want to implement it in C++, but I do not know what libraries I should use.
public int date(int year) {
LocalDate start = LocalDate.of(year, 10, 1);
if (start.getDayOfWeek() == DayOfWeek.TUESDAY)
return start.plusWeeks(1).getDayOfMonth();
return start.with(TemporalAdjusters.next(DayOfWeek.TUESDAY)).plusWeeks(1).getDayOfMonth();
}
It returns the second Tuesday on October.
If you have access to boost, this becomes very easy and somewhat similar to the Java code:
#include <iostream>
#include <boost/date_time/gregorian/gregorian.hpp>
auto date(const int year, const int month) {
namespace gr = boost::gregorian;
gr::date d{year, month, 1};
while(d.day_of_week() != gr::Tuesday) {
d += gr::days(1);
}
d += gr::weeks(1);
return d.day();
}
int main() {
std::cout << date(2019, 3); // second tuesday of march 2019 -> 12
}
We first obtain a boost::gregorian::date object initialised with the first day of the specified month and go forward as long as we don't end up on the first Tuesday. Afterwards, we add one week do our date. Lastly, we return boost::gregorian::date::day, which represents the day of the month of the specified date.
Much simpler than that messy Java code:
auto d = year_month_day{Tuesday[2]/October/year{y}}.day();
This uses the date library that became part of standard C++ in time for C++20.
PS: second Tuesday in October 2019 is the 8th, not the 12th.
I tried to get the date of first Sunday in this year
int getFristSunday () {
time_t rawtime;
struct tm * timeinfo;
time( &rawtime );
timeinfo = localtime( &rawtime );
timeinfo->tm_mon = 0;
timeinfo->tm_wday = 0;
mktime( timeinfo );
return timeinfo->tm_yday ;
}
but I get the first Thursday
Result
From this mktime reference:
time->tm_wday and time->tm_yday are ignored.
You have to set timeinfo->tm_mday to 1 and then check what day it is after calling mktime, and count forward from there.
Using this free, open-source, header-only C++11/14 library:
#include "date.h"
#include <iostream>
int
main()
{
using namespace date;
std::cout << year_month_day{sun[1]/jan/2016} << '\n';
}
which outputs:
2016-01-03
There are year(), month() and day() accessors for the year_month_day object. And the algorithms are highly optimized (containing no iterative loops).
If you would prefer to write your own date computations, here are the public domain calendrical algorithms used in the aforementioned date library. The link goes straight to the section describing how to find the Nth day of the week of the month/year combination.
Before calling mktime() all fields, except tm_yday and tm_wday need to be set. Obviously we need to set tm_mon and tm_mday for Jan 1.
Important to set the tm_hour to midday (12) and/or tm_isdst to -1 to insure the recalculated time is not impacted by daylight saving time. Consider what would happen if the current time was near midnight and the DST setting for now was different than Jan 1. The re-calculation could push time out of Jan 1 to Jan 2 or Dec 31.
int getFirstSunday(void) {
time_t rawtime;
struct tm * timeinfo;
time(&rawtime);
timeinfo = localtime(&rawtime);
timeinfo->tm_mon = 0; // set to January which is 0 "Months since January"
timeinfo->tm_mday = 1; // Set to the 1st of the month
timeinfo->tm_hour = 12; // Set to avoid getting the wrong DST setting for Jan 1.
timeinfo->tm_isdst = -1; // Set to avoid getting the wrong DST setting for Jan 1.
if (mktime(timeinfo) == -1) return -1;
int DaysSinceSundayForJan1 = timeinfo->tm_wday; // days since Sunday — [0, 6]
int DaysAfterJan1toNextSunday = 7 - DaysSinceSundayForJan1;
int DaysAfterJan1toFirstSunday = DaysAfterJan1toNextSunday%7;
// Convert to "day of the month"
return DaysAfterJan1toFirstSunday + 1;
}
I was wondering what is the best thing to do here. I was given an assignment and I have to update a Date class that uses Ctime.
The class must now work for date before 1970. I was looking around and I didn't find much...
So I guess my question is what would be the best way to achieve that?
Date Range from [1970-2037] to [1900-2037]
Assuming you mean CTime from MFC, if you enable OLE you can use COleDateTime as a drop-in replacement. Per the documentation, it supports dates "from January 1, 100, through December 31, 9999".
You could store the date as a signed integer number of days since 1970-01-01 or any other epoch. Negative dates would be the date before 1970.
Or you could use Boost.Date_Time library.
How about a date class that stores an internal std::tm datetime object and the total seconds since (or before) Jan 1, 1970 as a time_t. The members of tm are all ints and time_t should be 64bit everywhere (I hope), so in theory that should cover all the times you could consider.
In the constructor of your class you'd need to compute those total seconds and the only standard library function that seems to do that is mktime. Unfortunately this one only works for dates after Jan 1, 1970.
One possible workaround... add a really big number to the year and work with that internally.
#include <ctime>
#include <iostream>
class CustomDateTime {
const int MKTIME_DELTA = 100000;
std::tm _datetime;
std::time_t _total_seconds;
public:
CustomDateTime(int year, int mon, int day, int hour, int min, int sec) {
_datetime.tm_year = year - 1900 + MKTIME_DELTA;
_datetime.tm_mon = mon - 1;
// copy day, hour, min, sec
_total_seconds = std::mktime(&_datetime);
}
bool operator==(const CustomDateTime& rhs) {
return _total_seconds == rhs._total_seconds;
}
void print() {
std::cout << _datetime.tm_year + 1900 - MKTIME_DELTA << ':'
<< _datetime.tm_mon + 1 << _datetime.tm_mday << '\n';
}
};
That should cover all years between 1970 - MKTIME_DELTA = -98030 and the far far future.
Running dateon my server results in the correct time. But using localtime() in C(++) I'm getting the wrong time.
Running date: Fr 30. Nov 12:15:36 CET 2012
Using localtime(): Fr 30 Nov 2012 11:15:36 CET
What's wrong here?
OS: Debian 5.0.10
Some code:
struct tm* today;
today = localtime(...);
strftime(timeBuffer,50,myConnection.getMetaData().getDateFormat().c_str(),today);
disclaimer : This answer was written before any mention of strftime was added, and was a gut reaction to the 1 hour difference in the timestamps. Looking back at it now, that 1 hour difference couldn't have been due to DST (because the dates are not in summer), but is likely showing a UTC timestamp (1 hour difference between UTC and CET).
Unfortunately, the answer was accepted, and so I can't delete it. Even more unfortunate, is that the question as it stands is not answerable without additional information.
Leaving the original answer here for full transparency, but know that it does not address the question as asked :
The struct tm returned by localtime has a tm_isdst field that indicates whether daylight saving time (DST) is in effect. You need to take that field into account when formatting the time.
Try using asctime to format the time eg. :
puts(asctime(today));
I have experienced the same problem while writing a date adjustment routine. Adding 86400 seconds (= 1 day) to any given datetime value should result in incrementing the datetime value by one day. However in testing, the output value invariably added exactly one hour to the expected output. For instance, '2019-03-20 00:00:00' incremented by 86400 seconds resulted in '2019-03-21 01:00:00'. The reverse also occurred: '2019-03-21 00:00:00' decremented by -86400 resulted in '2019-03-20 01:00:00'.
The solution (inexplicably) was to subtract 3600 seconds (one hour) from the final interval before applying it to the input datetime.
The solution (thanks to helpful comments from #Lightness-Races-in-Orbit) was to set tm_isdst to -1 before calling mktime(). This tells mktime() that the DST status for the input datetime value is unknown, and that mktime() should use the system timezone databases to determine the correct timezone for the input datetime value.
The function (as corrected below) allows for any integer adjustment of days and now produces consistently correct results:
#include <stdio.h>
#include <string.h>
#include <time.h>
/*******************************************************************************
* \fn adjust_date()
*******************************************************************************/
int adjust_date(
char *original_date,
char *adjusted_date,
char *pattern_in,
char *pattern_out,
int adjustment,
size_t out_size)
{
/*
struct tm {
int tm_sec; // seconds 0-59
int tm_min; // minutes 0-59
int tm_hour; // hours 0-23
int tm_mday; // day of the month 1-31
int tm_mon; // month 0-11
int tm_year; // year minus 1900
int tm_wday; // day of the week 0-6
int tm_yday; // day in the year 0-365
int tm_isdst; // daylight saving time
};
*/
struct tm day;
time_t one_day = 86400;
// time_t interval = (one_day * adjustment) - 3600;
time_t interval = (one_day * adjustment);
strptime(original_date, pattern_in, &day);
day.tm_isdst = -1;
time_t t1 = mktime(&day);
if (t1 == -1) {
printf("The mktime() function failed");
return -1;
}
time_t t2 = t1 + interval;
struct tm *ptm = localtime(&t2);
if (ptm == NULL) {
printf("The localtime() function failed");
return -1;
}
strftime(adjusted_date, out_size, pattern_out, ptm);
return 0;
}
/*******************************************************************************
* \fn main()
*******************************************************************************/
int main()
{
char in_date[64] = "20190321000000" ,
out_date[64],
pattern_in[64] = "%Y%m%d%H%M%S",
pattern_out[64] = "%Y-%m-%d %H:%M:%S";
int day_diff = -1,
ret = 0;
size_t out_size = 64;
memset(out_date, 0, sizeof(out_date));
ret = adjust_date(in_date, out_date, pattern_in, pattern_out, day_diff, out_size);
if (ret == 0)
{
printf("Adjusted date: '%s'\n", out_date);
}
return ret;
}
Hopefully, this will be of some help to somebody. Your constructive comments are greatly appreciated.
handling date time is very error prone and usually badly tested. i always recommend using boost::date_time http://www.boost.org/doc/libs/1_52_0/doc/html/date_time.html
here are nice examples http://en.highscore.de/cpp/boost/datetime.html
Did you try this ? :
time_t rawtime;
struct tm * today;
time ( &rawtime );
today= localtime ( &rawtime );
puts(asctime (today));
I have the following integers:
int y, mon, d, h, min, s;
Their values are: 2012, 06, 27, 12, 47, 53 respectively. I want to represent the date time of "2012/06/27 12:47:53 UTC" if I have selected 'UTC' somewhere else in my application, or "2012/06/27 12:47:53 AEST" if I have selected 'AEST' somewhere else in my application.
I want to convert this into a time_t, and here's the code that I am current using to do so:
struct tm timeinfo;
timeinfo.tm_year = year - 1900;
timeinfo.tm_mon = mon - 1;
timeinfo.tm_mday = day;
timeinfo.tm_hour = hour;
timeinfo.tm_min = min;
timeinfo.tm_sec = sec;
//timeinfo.tm_isdst = 0; //TODO should this be set?
//TODO find POSIX or C standard way to do covert tm to time_t without in UTC instead of local time
#ifdef UNIX
return timegm(&timeinfo);
#else
return mktime(&timeinfo); //FIXME Still incorrect
#endif
So I am using a tm struct and mktime, however this is not working well, because it is always assuming my local time-zone.
What is the correct way of doing this?
So below is the solution that I have come up with so far.
It basically does one of three things:
If UNIX, simply use timegm
If not UNIX
Either, do math using the difference between UTC epoch and local epoch as an offset
Reservation: Math may be incorrect
Or, set the "TZ" environment variable to UTC temporarily
Reservation: will trip up if/ when this code needs to be multithreaded
namespace tmUtil
{
int const tm_yearCorrection = -1900;
int const tm_monthCorrection = -1;
int const tm_isdst_dontKnow = -1;
#if !defined(DEBUG_DATETIME_TIMEGM_ENVVARTZ) && !(defined(UNIX) && !defined(DEBUG_DATETIME_TIMEGM))
static bool isLeap(int year)
{
return
(year % 4) ? false
: (year % 100) ? true
: (year % 400) ? false
: true;
}
static int daysIn(int year)
{
return isLeap(year) ? 366 : 365;
}
#endif
}
time_t utc(int year, int mon, int day, int hour, int min, int sec)
{
struct tm time = {0};
time.tm_year = year + tmUtil::tm_yearCorrection;
time.tm_mon = mon + tmUtil::tm_monthCorrection;
time.tm_mday = day;
time.tm_hour = hour;
time.tm_min = min;
time.tm_sec = sec;
time.tm_isdst = tmUtil::tm_isdst_dontKnow;
#if defined(UNIX) && !defined(DEBUG_DATETIME_TIMEGM) //TODO remove && 00
time_t result;
result = timegm(&time);
return result;
#else
#if !defined(DEBUG_DATETIME_TIMEGM_ENVVARTZ)
//TODO check that math is correct
time_t fromEpochUtc = mktime(&time);
struct tm localData;
struct tm utcData;
struct tm* loc = localtime_r (&fromEpochUtc, &localData);
struct tm* utc = gmtime_r (&fromEpochUtc, &utcData);
int utcYear = utc->tm_year - tmUtil::tm_yearCorrection;
int gmtOff =
(loc-> tm_sec - utc-> tm_sec)
+ (loc-> tm_min - utc-> tm_min) * 60
+ (loc->tm_hour - utc->tm_hour) * 60 * 60
+ (loc->tm_yday - utc->tm_yday) * 60 * 60 * 24
+ (loc->tm_year - utc->tm_year) * 60 * 60 * 24 * tmUtil::daysIn(utcYear);
#ifdef UNIX
if (loc->tm_gmtoff != gmtOff)
{
StringBuilder err("loc->tm_gmtoff=", StringBuilder((int)(loc->tm_gmtoff)), " but gmtOff=", StringBuilder(gmtOff));
THROWEXCEPTION(err);
}
#endif
int resultInt = fromEpochUtc + gmtOff;
time_t result;
result = (time_t)resultInt;
return result;
#else
//TODO Find a way to do this without manipulating environment variables
time_t result;
char *tz;
tz = getenv("TZ");
setenv("TZ", "", 1);
tzset();
result = mktime(&time);
if (tz)
setenv("TZ", tz, 1);
else
unsetenv("TZ");
tzset();
return result;
#endif
#endif
}
N.B. StringBuilder is an internal class, it doesn't matter for the purposes of this question.
More info:
I know that this can be done easily using boost, et al. But this is NOT and option. I need it to be done mathematically, or using a c or c++ standard function, or combinations thereof.
timegm appears to solve this problem, however, it doesn't appear to part of the C / POSIX standard. This code currently is compiled on multiple platforms (Linux, OSX, WIndows, iOS, Android (NDK)), so I need to find a way to make it work across all of these platforms, even if the solution involves #ifdef $PLATFORM type things.
It makes me want to throw up in my mouth a little bit, but you could convert it to a string with strftime(), replace the timezone in the string and then convert it back with strptime() and into a time_t with mktime(). In detail:
#ifdef UGLY_HACK_VOIDS_WARRANTY
time_t convert_time(const struct tm* tm)
{
const size_t BUF_SIZE=256;
char buffer[BUF_SIZE];
strftime(buffer,256,"%F %H:%M:%S %z", tm);
strncpy(&buffer[20], "+0001", 5); // +0001 is the time-zone offset from UTC in hours
struct tm newtime = {0};
strptime(buffer, "%F %H:%M:%S %z", &newtime);
return mktime(&newtime);
}
#endif
However, I would highly recommend you convince the powers that be that boost is an option after all. Boost has great support for custom timezones. There are other libraries that do this elegantly as well.
If all you want is to convert a struct tm given in UTC to a time_t then you can do it like this:
#include <time.h>
time_t utc_to_time_t(struct tm* timeinfo)
{
tzset(); // load timezone information (this can be called just once)
time_t t = mktime(timeinfo);
return t - timezone;
}
This basically converts the UTC time to time_t as if the given time was local, then applies a timezone correction to the result to bring it back to UTC.
Tested on gcc/cygwin and Visual Studio 2010.
I hope this helps!
Update: As you very well pointed out, my solution above may return time_t value that is one hour off when the daylight time savings state of the queried date is different than the one for the current time.
The solution for that problem is to have an additional function that can tell you if a date falls in the DST region or not, and use that and the current DST flag to adjust the time returned by mktime. This is actually easy to do. When you call mktime() you just have to set the tm_dst member to -1 and then the system will do its best to figure out the DST at the given time for you. Assuming we trust the system on this, then you can use this information to apply a correction:
#include <time.h>
time_t utc_to_time_t(struct tm* timeinfo)
{
tzset(); // load timezone information (this can be called just once)
timeinfo->tm_isdst = -1; // let the system figure this out for us
time_t t = mktime(timeinfo) - timezone;
if (daylight == 0 && timeinfo->tm_isdst != 0)
t += 3600;
else if (daylight != 0 && timeinfo->tm_isdst == 0)
t -= 3600;
return t;
}
If you are on Linux or other UNIx or UNIX-like system then you might have a timegm function that does what you want. The linked manual page have a portable implementation so you can make it yourself. On Windows I know of no such function.
After beating my head against this for days trying to get a timegm(1) function that works on Android (which does not ship with one), I finally discovered this simple and elegant solution, which works beautifully:
time_t timegm( struct tm *tm ) {
time_t t = mktime( tm );
return t + localtime( &t )->tm_gmtoff;
}
I don't see why this wouldn't be a suitable cross-platform solution.
I hope this helps!
time_t my_timegm2(struct tm *tm)
{
time_t ret = tm->tm_sec + tm->tm_min*60 + tm->tm_hour*3600 + tm->tm_yday*86400;
ret += ((time_t)31536000) * (tm->tm_year-70);
ret += ((tm->tm_year-69)/4)*86400 - ((tm->tm_year-1)/100)*86400 + ((tm->tm_year+299)/400)*86400;
return ret;
}
There seems to be a simpler solution:
#include <time64.h>
time_t timegm(struct tm* const t)
{
return (time_t)timegm64(t);
}
Actually I have not testet yet if really it works, because I still have a bit of porting to do, but it compiles.
Here's my solution:
#ifdef WIN32
# define timegm _mkgmtime
#endif
struct tm timeinfo;
timeinfo.tm_year = year - 1900;
timeinfo.tm_mon = mon - 1;
timeinfo.tm_mday = day;
timeinfo.tm_hour = hour;
timeinfo.tm_min = min;
timeinfo.tm_sec = sec;
return timegm(&timeinfo);
This should work both for unix and windows