Incrementing dates in a loop - c++

This is an exercise; I'm forbidden to use built-in date function of C++ or any readily available library.
I have this code:
#include <iostream>
#include <vector>
using namespace std;
bool IsLeap(unsigned int year)
{
if (year % 4 != 0) return false;
else
if (year % 100 != 0) return true;
else
if (year % 400 != 0) return false;
else
return true;
}
class Date
{
public:
unsigned short int Day;
unsigned short int Month;
unsigned short int Year;
unsigned short int DayOfWeek;
};
int MonthLimit(int Month, int year)
{
switch (Month)
{
case 4:
case 6:
case 9:
case 11:
{
return 30;
break;
}
case 1:
case 3:
case 5:
case 7:
case 8:
case 12:
{
return 31;
break;
}
case 2:
if (IsLeap(year))
{
return 29;
break;
}
else
{
return 28;
break;
}
}
}
const Date FirstDayEver = { 1, 1, 1900, 1 }; //January 1, 1900 was on a Monday.
int main()
{
unsigned int years;
cin >> years;
vector<int> counters(7); //whenever a 13th falls on a day X, we increment the Xth counter. It's that simple
for (Date i = FirstDayEver; !((i.Day == 31) && (i.Month == 12) && (i.Year == 1900 + years - 1)); i.Day++)
{
i.DayOfWeek = (i.DayOfWeek + 1) % 7;
if (i.Month == MonthLimit(i.Month, i.Year))
{
i.Month++;
i.Day = 1;
cout << "Proceeded to " << i.Day << "." << i.Month << "." << i.Year << "\n";
}
if ((i.Day == 31) && (i.Month == 12))
{
i.Year++;
i.Day = 1;
i.Month = 1;
cout << "Proceeded to " << i.Day << "." << i.Month << "." << i.Year << "\n";
}
if (i.Day == 13)
{
counters[i.DayOfWeek]++;
cout << i.Day << "." << i.Month << "." << i.Year << " was a " << i.DayOfWeek << "\n";
}
}
cout << counters[6] << " " << counters[7] << " " << counters[1] << " " << counters[2] << " " << counters[3] << " " << counters[4] << " " << counters[5] << "\n";
exit(0);
}
The debug info (couts) is there to see whether anything happens in the loop.
So far the only thing that happens is a lot of lines saying "13.1.1900 was a " varying number.
I think the desired logic of incrementing a date is rather clear from the code, but I'm trying to increment something wrong.
Where's my mistake?

You're complicating your increment procedure too much.
I'm actually still trying to work out exactly how it all fits together (edge cases etc) but it seems that you tried to do each day/month/year independantly.
You can do all your incrementing in a simple function like this:
void increment_Date(Date &d)
{
d.DayOfWeek = (d.DayOfWeek + 1) % 7; //increase weekday
if (++d.Day > MonthLimit(d.Month, d.Year)//increase day, if larger than month size...
{
d.Day = 1; //reset day to 1
if (++d.Month > 12) //increase month, if larger than year size
{
d.Month = 1; //reset month to 1
d.Year++; //increase year
}
}
}
The logic is simpler because I approached it in a slightly different way than you seem to have done.
In the function above, I increase the smallest unit first (day), check for overflow and then move up to month if needed (and so on).
This works in the same way that a car's mileage dial's digits only tick up when the dial reaches 10 and goes back to 0. Or, adding 1 to 9 and 'overflowing' into the 10's digit of a number.
Otherwise your code is good. The Switch statement for MonthLimit was a good choice.
I'd add a switch statement in a function to return a string for day of week:
std::String printable_DOW(int DOW)
{
switch (DOW)
case 0:
return "Sunday";
case 1:
return "Monday";
case 2:
return "Tuesday";
case 3:
return "Wednesday";
case 4:
return "Thursday";
case 5:
return "Friday";
case 6:
return "Saturday";
}
and a print_date function too:
void print_date(Date d)
{
std::cout<< printable_DOW(d.DayOfWeek)
<< ", " << d.Day << "." << d.Month << "." d.Year;
}
and it's pretty easy to put it all together and use too:
int main
{
Date my_date;
my_date.Day = 1;
my_date.Month = 1;
my_date.Year = 2000;
my_date.DayOfWeek = 0; //assuming 0 == Sunday, 1 = Monday...
for (int daycount = 0; daycount < 5114 /*days since 1/1/2000 to today*/; daycount++)
{
increment_date(my_date);
//debug:
print_date(my_date);
}
//print "today's" date:
print_date(my_date);
}
As a class:
It's mostly here and (mostly) works. Feel free to edit people!
class date
{
private:
unsigned short int day;
unsigned short int month;
unsigned short int year;
unsigned short int day_of_week;
int month_limit();
bool is_leap_year();
public:
date();
date(int in_day, int in_month, int in_year, int in_day_of_week);
date& date::operator=(date rhs)
date& operator++(); //pre-increment (++date)
date operator++(int); //post-increment (date++)
//these are "special" they don't work exactly as you might think and are slightly broken right now,
//but I'm putting them in regardless
//they also don't work on DOW right now.
date& operator+=(const date& rhs)
date& operator-=(const date& rhs)
inline date operator+(date lhs, const date& rhs)
inline date operator-(date lhs, const date& rhs)
}
//phwew
date::date(int in_day, int in_month, int in_year, int in_day_of_week)
{
this->day = in_day;
this->month = in_month;
this->year = in_year;
this->day_of_week = in_doay_of_week;
}
date::month_limit()
{
switch (this->month)
{
case 4:
case 6:
case 9:
case 11:
{
return 30;
break;
}
case 1:
case 3:
case 5:
case 7:
case 8:
case 12:
{
return 31;
break;
}
case 2:
if (is_leap_year(this->year))
{
return 29;
break;
}
else
{
return 28;
break;
}
}
}
bool is_leap_year()
{
if (this->year % 4 != 0) return false;
else
if (this->year % 100 != 0) return true;
else
if (this->year % 400 != 0) return false;
else
return true;
}
date& date::operator=(date rhs)
{
swap(rhs);
return *this;
}
date& date::operator++()
{
this->day_of_week = (this->day_of_week + 1) % 7; //increase weekday
this->day++;
if (++(this->day) > month_limit()) //increase day, if larger than month size...
{
this->day = 1; //reset day to 1
if (++this->month > 12) //increase month, if larger than year size
{
this->month = 1; //reset month to 1
this->year++; //increase year
}
}
return *this;
}
date date::operator++(int)
{
date tmp(*this);
operator++();
return tmp;
}
//adds years on, then months, then days
date& date::operator+=(const date& rhs)
{
this->year += rhs.year;
this->month += rhs.month;
this->day += rhs.day;
if (this->month > 12) //get to the right month
{
this->year = this->month / 12;
this->month = this->month % 12;
}
if (this->day > month_limit())
{
this->month = this->day / month_limit();
this->day = this->day % month_limit();
if (this->month > 12) //recalculate **yes, I know this is currently wrong if more than one month is added on in days**
{
this->year = this->month / 12;
this->month = this->month % 12;
}
}
return *this;
}
inline date date::operator+(date lhs, const date& rhs)
{
lhs += rhs;
return lhs;
}
//subtracts years, then months, then days
date& date::operator-=(const date& rhs)
{
if ((rhs.year < this->year) || ((rhs.year == this->year) && (rhs.month < this->month))
|| (((rhs.year == this->year) && (rhs.month == this->month)) && (rhs.day < this->day)
{
swap(rhs);
}
this->year -= rhs.year;
this->month -= rhs.month;
this->day -= rhs.day;
return *this;
}
inline date date::operator+(date lhs, const date& rhs)
{
lhs += rhs;
return lhs;
}

First of all,
if (i.Month == MonthLimit(i.Month, i.Year))
is comparing the month with the number of days in the month.
Since the first month is 1, and that month has 31 days, you will never increment the month.
Since you never increment the month, you will never increment the year.
(You would probably have spotted this easier if you had named the function "DaysInMonth", or something else that makes it clearer what it returns.)
You want
if (i.Day == MonthLimit(i.Month, i.Year))
Second, MonthLimit is missing a case for October.

Related

Comparing data in array of objects using a member boolean function

First I need to compare the date of births in the array with the operator== function which is in the class Person, and also show the person with the earliest date of birth with the operator< function which is also in class Person. But I have trouble understanding what I'm doing wrong.
I was thinking of showing the earliest date of birth operator< using the same way as I did with operator== but now that it doesn't work, I have no idea.
#include <iostream>
#include <string>
using namespace std;
class Birth_date
{
private:
unsigned int day{};
unsigned int month{};
unsigned int year{};
public:
Birth_date() {}
Birth_date(int day, int month, int year)
{
this->day = day;
this->month = month;
this->year = year;
}
Birth_date(const Birth_date& birth)
{
day = birth.day;
month = birth.month;
year = birth.year;
}
void set_day(int day)
{
this->day = day;
}
void set_month(int month)
{
this->month = month;
}
void set_year(int year)
{
this->year = year;
}
int get_day()
{
return day;
}
int get_month()
{
return month;
}
int get_year()
{
return year;
}
friend bool operator == (const Birth_date& a, const Birth_date& b)
{
return (a == b);
}
bool operator < (const Birth_date& bd)
{
return day < bd.day && month < bd.month && year < bd.year;
}
bool operator > (const Birth_date& bd)
{
return day > bd.day && month > bd.month && year > bd.year;
}
ostream& Output(ostream& out)
{
out << "Date of birth: " << day << "/" << month << "/" << year << endl;
return out;
}
};
class Person
{
private:
string name;
Birth_date dob;
public:
Person() {}
Person(string name, Birth_date dob)
{
this->name = name;
this->dob = dob;
}
Person(const Person& p)
{
name = p.name;
dob = p.dob;
}
string get_name()
{
return name;
}
Birth_date get_dob()
{
return dob;
}
bool operator < (const Person& p)const
{
Birth_date bd1 = p.dob;
Birth_date bd2 = p.dob;
return (bd1 < bd2);
}
bool operator == (const Person& p)const
{
return (dob == p.dob);
}
const ostream& output(ostream& out)
{
out << "Name: " << name << "\t" ;
dob.Output(out);
return out;
}
};
int main()
{
Person students[5] = { Person("Ivan Petkov",Birth_date(10,6,1999)),
Person("Gabe Trent",Birth_date(20,12,1996)),
Person("Maggy Sommer",Birth_date(5,2,2000)),
Person("Cameron Dallas",Birth_date(1,4,2001)),
Person("Catherine Crumb",Birth_date(28,8,2000)) };
for (int i = 0; i < 5; i++)
{
students[i].output(cout);
}
cout << "\n";
for (int i = 0; i < 5; i++)
{
for (int x = i + 1; x < 5; x++)
{
if (students[i].operator==(students[x]))
{
cout << "Students " << students[i].get_name()
<< "and " << students[x].get_name()
<< "have the same date of birth." << endl;
}
else
cout << "Nobody has the same date of birth." << endl;
}
}
return 0;
}
Right now the only output is the array and then the program just exits.
When you're comparing dates there's the possibility that the year and the month are the same but the day is different.
21/4/2003 > 20/4/2003
however in your code you check that the month and year are greater and can't be equal.
return day < bd.day && month < bd.month && year < bd.year;
by your code if the dates are: 29/1/2000 and 1/3/2000 it would return false.
your functions should first check if the years are not the same, if they are then you next check the months and then the days.
Here's an example of a code I once wrote:
bool MyDate::operator >(const MyDate& d)const {
if (year < d.year)
return false;
else if (year > d.year)
return true;
else if (year == d.year) {
if (month < d.month)
return false;
else if (month > d.month)
return true;
if (month == d.month) {
if (day < d.day)
return false;
else if (day > d.day)
return true;
else
return false;
}
}
return true;
}

addition assignment overloading dynamically allocated pointer to array of class objects c++

first time posting. Thanks in advance for your correspondence and patience.
I need to prevent the copy of an object, if that object already exists in my array.
I am beyond my depth as how to compare these values and prevent copy.
My next step is trying: unique_ptr or shared_ptr or compare() or iterator.
NO VECTORS.... I NEED TO ALLOCATE AND DE-ALLOCATE EVERYTHING.
ConfirmationSender.h
class ConfirmationSender {
Reservation** ppCon;
size_t cCnt;
public:
ConfirmationSender();
~ConfirmationSender();
ConfirmationSender& operator+=(const Reservation& res);
// ConfirmationSender& operator-=(const Reservation& res);
void display(std::ostream& os) const;
};
std::ostream& operator<<(std::ostream& os, const ConfirmationSender& obj);
ConfirmationSender.cpp
ConfirmationSender& ConfirmationSender::operator+=(const Reservation& res) {
if (cCnt == 0) {
++cCnt;
ppCon = new Reservation* [cCnt];
ppCon[0] = new Reservation(res);
}
else {
Reservation** temp1 = nullptr;
temp1 = new Reservation * [cCnt];
for (size_t i = 0; i < cCnt; i++) {
temp1[i] = ppCon[i];
}
++cCnt;
delete[] ppCon;
ppCon = new Reservation * [cCnt];
for (size_t i = 0; i < cCnt - 1; i++) {
ppCon[i] = temp1[i];
}
ppCon[cCnt - 1] = new Reservation(res);
delete[] temp1;
temp1 = nullptr;
/*
NEED TO IMPLEMENT CONDITION
SO THE VALUE I COPY
CANNOT BE A VALUE ALREADY EXISTING IN ARRAY
*/
}
return *this;
}
class Reservation {
std::string id, name, email;
int seats, day, hour;
public:
Reservation();
Reservation(const std::string& m_res);
void display(std::ostream& os) const;
};
std::ostream& operator<<(std::ostream& os, const Reservation& obj);
Reservation::Reservation() {
seats = 0;
day = 0;
hour = 0;
};
Reservation::Reservation(const std::string& m_res) {
std::string t_str;
t_str = m_res;
t_str.erase(std::remove(t_str.begin(), t_str.end(), ','), t_str.end());
istringstream iss(t_str);
std::string buffer = {};
static int count = 0;
while (iss >> buffer) {
switch (count % 6) {
case 0: id = buffer; break;
case 1: name = buffer; break;
case 2: email = buffer; break;
case 3: seats = atoi(buffer.c_str()); break;
case 4: day = atoi(buffer.c_str()); break;
case 5: hour = atoi(buffer.c_str()); break;
}
count++;
}
buffer.clear();
iss.str(std::string());
}
void Reservation::display(std::ostream& os) const {
string menu_type;
if (hour >= 6 && hour <= 9) { menu_type = "Breakfast"; }
else if (hour >= 11 && hour <= 15) { menu_type = "Lunch"; }
else if (hour >= 17 && hour <= 21) { menu_type = "Dinner"; }
else { menu_type = "Drinks"; }
os << "Reservation "
<< id << " "
<< name << " <"
<< email << "> "
<< menu_type << " on day "
<< day << " # "
<< hour << ":00 for "
<< seats << " people." << '\n';
}
std::ostream& operator<<(std::ostream& os, const Reservation& obj) {
obj.display(os);
return os;
}
main.cpp
// Confirmation Sender
{
std::cout << "CS: Testing Operators\n";
std::cout << "==========================\n";
sender1 += *ppReservations[5];
sender1 += *ppReservations[16];
sender1 += *ppReservations[16];
// THE SECOND [16] SHOULD NOT BE COPIED BY ADDITION OPERATOR
std::cout << sender1;
std::cout << "==========================\n\n";
}

Parsing a string : my code apparentely works after testing, but is inelegant

This is a c++ question. I want the code of the desired c++ function (see below) to be (as much as possible) in c-style and using c string library functions because I think that this will lead the quickest code in terms of execution time. (Am I wrong ? If so, how much ?) Yes, I value performance more than readability for this question because the desired function will be called a lot (millions) of times.
I am receiving const char *'s of the form "25Dec2016" that represent dates and I am parsing them to back out from them three int's (one for the day, the second for the month and the last for the year (that I assumed to be a number between 0 and 9999)) thanks to a function
Parse(const char * cDate, int & day, int & month, int & year)
I coded such a function and tested it : it works on correct const char*s (those that indeed represent date in my format), but I feel that my use of c functions (atoi for instance) is incorrect, even if I don't see why. There are also other problems :
the code is inelegant (the big if ... else if ... if) : one cannot do a switch statement on a string, but is there an elegant way to do this without resorting the std::map and c++11 ?
surely problematic from a c string point of view (I am not an expert) : for instance, I am really not happy with the way I extract the three substring into "buffers" ... Plus it appears I have problems with not null terminated char arrays that I'd like to correct. I could force a \0 at the end of _day and _year as I did for _month but I find that doing so is awful, so that I suspect a bad "design" of my parsing function
quite bad from an error handling point of view : the function is not a constructor for now, but could finally be, this is the reason why I throw.
I am open to any comments !
Here is the initial code :
Parse(const char * cDate, int & day, int & month, int & year)
{
if (0 == cDate)
{
throw "Error : null string pointer";
}
else
{
if (strlen(cDate) < 8)
{
throw "Error : invalid string format";
}
else
{
char _day[2];
char _month[4];
char _year[5]; // implictely the biggest year we authorize is 99999 ; it should suffice
for (int i = 0; i < 2; ++i)
{
_day[i] = cDate[i];
}
day = atoi(_day); // if fail, Undefined behaviour, see strtol for a more robust cross-platform alternative
char c;
for (int i = 2; i < 5; ++i)
{
c = cDate[i];
_month[i-2] = toupper(c);
}
_month[3] = '\0';
if (0 == strcmp("JAN", _month))
{
month = 1;
}
else if (0 == strcmp("FEB", _month))
{
month = 2;
}
else if (0 == strcmp("MAR", _month))
{
month = 3;
}
else if (0 == strcmp("APR",_month))
{
month = 4;
}
else if (0 == strcmp("MAY", _month))
{
month = 5;
}
else if (0 == strcmp("JUN", _month))
{
month = 6;
}
else if (0 == strcmp("JUL", _month))
{
month = 7;
}
else if (0 == strcmp("AUG", _month))
{
month = 8;
}
else if (0 == strcmp("SEP", _month))
{
month = 9;
}
else if (0 == strcmp("OCT",_month))
{
month = 10;
}
else if (0 == strcmp("NOV", _month))
{
month = 11;
}
else if (0 == strcmp("DEC", _month))
{
month = 12;
}
else
{
throw "Error : invalid month string";
}
for (int i = 5; i < 10; ++i)
{
_year[i-5] = cDate[i];
}
year = atoi(_year);
}
}
}
I finally opted for the function to be a constructor of a Date class, and inspired myself from rici's answer also using strtol as I intended initially (see comment in my initial code) instead of atoi:
#include <cstring> // for strlen
#include <ctype.h> // for toppuer
#include <stdlib.h>
int up(char c)
{
return toupper((unsigned char)(c));
}
Date::Date(const char * cDate)
{
if (0 == cDate)
{
throw "Error : null string pointer";
}
else
{
if (strlen(cDate) < 8)
{
throw "Error : invalid string format. String format is DDMMMYYYY with M's in upper or lower case"; // for now, valid format is 24Oct1979
}
else
{
char * ppEnd;
int day = strtol(cDate, &ppEnd, 10);
if (0 == day)
throw "Error : invalid string format. String format is DDMMMYYYY with M's in upper or lower case";
m_Day = day;
char cMonth[4];
int month;
memcpy(cMonth, &ppEnd[0], 3);
switch (up(cMonth[0]))
{
case 'A':
{
switch (up(cMonth[1]))
{
case 'P': if (up(cMonth[2]) == 'R') month = 4;
break;
case 'U': if (up(cMonth[2]) == 'G') month = 8;
break;
}
break;
}
case 'D':
{
if (up(cMonth[1]) == 'E' && up(cMonth[2]) == 'C')
month = 12;
break;
}
case 'F':
{
if (up(cMonth[1]) == 'E' && up(cMonth[2]) == 'B')
month = 2;
break;
}
case 'J':
{
switch (up(cMonth[1]))
{
case 'A': if (up(cMonth[2]) == 'N')
month = 1;
break;
case 'U': switch (up(cMonth[2]))
{
case 'N': month = 6;
case 'L': month = 7;
}
break;
}
break;
}
case 'M':
{
if (up(cMonth[1]) == 'A')
{
switch (up(cMonth[2]))
{
case 'R': month = 3;
case 'Y': month = 5;
}
}
break;
}
case 'N':
{
if (up(cMonth[1]) == 'O' && up(cMonth[2]) == 'V') month = 11;
break;
}
case 'O':
{
if (up(cMonth[1]) == 'C' && up(cMonth[2]) == 'T') month = 10;
break;
}
case 'S':
{
if (up(cMonth[1]) == 'E' && up(cMonth[2]) == 'P') month = 9;
break;
}
}
m_Month = (Month)month;
int year = strtol(ppEnd + 3, &ppEnd, 10);
if (0 == year)
throw "Error : invalid string format. String format is DDMMMYYYY with M's in upper or lower case";
m_Year = year;
updateSerial();
}
}
}
Remark. Being lazy, I did not throw everywhere I should in the "month" part of the code.
If your system is Posix-compatible, then you could just use strptime with the format %d%b%Y:
bool Parse(const char* date, int& day, int& month, int& year) {
struct tm components;
const char* rest = strptime(date, "%d%b%Y", &components);
if (rest == NULL || *rest != '\0') return false;
day = components.tm_mday;
month = components.tm_mon;
year = components.tm_year + 1900;
return true;
}
That is likely to be as fast as a naive parser, and it is certainly a lot easier to write :)
Otherwise, you should use strtol rather than atoi, since it will let you know both whether the parse was successful and where the next character is. And if you want to parse the month names quickly, you'll want to build a trie, either as a table or directly in code (the table is probably faster, fwiw):
static int up(char c) { return toupper((unsigned char)(c)); }
int parseMonth(const char* p) {
switch (up(p[0])) {
case 'A': {
switch (up(p[1])) {
case 'P': if (up(p[2]) == 'R') return 4;
break;
case 'U': if (up(p[2]) == 'G') return 8;
break;
}
break;
}
case 'D': {
if (up(p[1]) == 'E' && up(p[2]) == 'C') return 12;
break;
}
case 'F': {
if (up(p[1]) == 'E' && up(p[2]) == 'B') return 2;
break;
}
case 'J': {
switch (up(p[1])) {
case 'A': if (up(p[2]) == 'N') return 1;
break;
case 'U': switch (up(p[2])) {
case 'N': return 6;
case 'L': return 7;
}
break;
}
break;
}
case 'M': {
if (up(p[1]) == 'A') {
switch (up(p[2])) {
case 'R': return 3;
case 'Y': return 5;
}
}
break;
}
case 'N': {
if (up(p[1]) == 'O' && up(p[2]) == 'V') return 11;
break;
}
case 'O': {
if (up(p[1]) == 'C' && up(p[2]) == 'T') return 10;
break;
}
case 'S': {
if (up(p[1]) == 'E' && up(p[2]) == 'P') return 9;
break;
}
}
return -1;
}
Solution based on boost Spirit X3
#include <iostream>
#include <memory>
#include <string.h>
#include <boost/spirit/home/x3.hpp>
#include <boost/fusion/adapted/std_tuple.hpp>
namespace x3 = boost::spirit::x3;
struct Month_ : x3::symbols<std::uint8_t>
{
Month_()
{
add
("Jan", 1)
("Feb", 2)
("Mar", 3)
("Apr", 4)
("May", 5)
("Jun", 6)
("Jul", 7)
("Aug", 8)
("Sep", 9)
("Oct", 10)
("Nov", 11)
("Dec", 12);
}
}month_;
using ResultType = std::tuple<int, int, int>;
namespace grammar
{
using namespace x3;
auto const start_ = rule<struct start_, ResultType>{"start"}
= int_ >> month_ >> int_;
};
int main(int athc, char* argv[])
{
std::string str{"25Dec2016"};
auto beg = std::begin(str);
auto end = std::end(str);
ResultType res;
auto ret = x3::parse(beg,end, grammar::start_, res);
if(ret && (beg==end) )
{
std::cout << "parse done :" << std::get<0>(res) << " " << std::get<1>(res) << " " << std::get<2>(res) << "\n";
}
else
{
std::cout << "parse failed '" << std::string(beg, std::next(beg, 10)) << "'\n";
}
return 0;
}
Note, this works also with char*
Live On Coliru
Here are remarks on your code and some possible simplifications:
You can remove the else branches from the error handling if tests that throw an exception. As a matter of fact, you could just return a completion status instead of throwing exceptions.
Structured exceptions would be more precise than just throwing strings, but I'm not a C++ expert.
If the input string is null-terminated after the year part, there is no need to extract the number fields, but you might want to verify correct formatting.
Using an array to match the month part would greatly simplify that part.
Here is a simpler version:
typedef enum ParseStatus {
Parse_OK = 0,
Parse_NullStringPointer,
Parse_InvalidStringFormat,
Parse_InvalidDayNumber,
Parse_InvalidMonthName,
Parse_InvalidYearNumber
} ParseStatus;
ParseStatus Parse(const char *cDate, int &day, int &month, int &year) {
static const char months[4][12] = {
"JAN", "FEB", "MAR", "APR", "MAY", "JUN",
"JUL", "AUG", "SEP", "OCT", "NOV", "DEC",
};
static const int maxday[12] = {
31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
};
char mon[4];
unsigned int dd, mm, yyyy;
char c;
if (!cDate) {
return Parse_NullStringPointer;
}
/* Using sscanf for a simple solution.
* If the string has the correct form, `sscanf` will convert 3 fields.
* extra spaces will be accepted and ignored.
*/
if (sscanf(cDate, "%u%3s%u%c", &dd, mon, &yyyy, &c) != 3) {
return Parse_InvalidStringFormat;
}
/* If you have `strupr(char *);`, you could use it here */
mon[0] = toupper((unsigned char)mon[0];
mon[1] = toupper((unsigned char)mon[1];
mon[2] = toupper((unsigned char)mon[2];
for (mm = 0; mm < 12; mm++) {
if (!strcmp(mon, months[mm]))
break;
}
if (mm >= 12) {
return Parse_InvalidMonthName;
}
/* Further validation on the day number */
if (dd < 1 || dd > 31 || dd > maxday[mm]) {
return Parse_InvalidDayNumber;
}
/* check for leap year, assuming gregorian calendar */
if (dd == 29 && mm == 1 &&
(yyyy % 4 != 0 || (yyyy % 100 == 0 && yyyy % 400 != 0)) {
return Parse_InvalidDayNumber;
}
/* check some limits for the year */
if (yyyy < 1 || yyyy > 9999) {
return Parse_InvalidYearNumber;
}
day = dd;
month = mm + 1;
year = yyyy;
return Parse_OK;
}
If you do not want to use sscanf(), you can use strtol() this way, but it is more cumbersome:
char *p;
int i;
dd = strtol(cDate, &p, 10);
for (i = 0; i < 3 && isalpha((unsigned char)p[i]); i++) {
mon[i] = toupper((unsigned char)p[i]);
}
mon[i] = '\0';
yyyy = strtol(p, &p, 10);
if (*p != '\0') {
return Parse_InvalidStringFormat;
}
When it comes to low level parsing I think std::strtol is your friend because it keeps track of the current pointer position in the string you are parsing. Not that that is critical in this type of string with fixed length components. Also std::strtol has easy error checking.
Also using iterators and algorithms helps to keep things neat and tidy.
This is about as "elegant" as I can make this using low level constructs:
#include <cctype>
#include <cstdlib>
#include <cstring>
#include <iostream>
void parse(const char* cDate, int& day, int& month, int& year)
{
static const char* months[] =
{
"JAN", "FEB", "MAR", "APR", "MAY", "JUN", "JUL", "AUG", "SEP", "OCT", "NOV", "DEC"
};
char* pos;
day = std::strtol(cDate, &pos, 10);
if(std::distance(cDate, static_cast<const char*>(pos)) != 2)
throw std::runtime_error("bad date format");
char mon[4] = {};
for(auto m = std::begin(mon); m != std::end(mon) - 1; ++m)
*m = std::toupper(*pos++);
auto found = std::find_if(std::begin(months), std::end(months),
[&](const char* m){ return !std::strcmp(mon, m); });
if(found == std::end(months))
throw std::runtime_error("bad month format");
month = std::distance(months, found) + 1;
char* end;
year = std::strtol(pos, &end, 10);
if(std::distance(pos, end) != 4)
throw std::runtime_error("bad year format");
}
int main()
{
try
{
auto s = "25Dec2016";
int d, m, y;
parse(s, d, m, y);
std::cout << d << "/" << m << "/" << y << '\n';
}
catch(const std::exception& e)
{
std::cerr << e.what() << '\n';
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
N.B. Not heavily tested may contain bugs.

How to compare day/month/year in C or C++?

I have a program that asks the user to input to dates then it displays which one is more recent I've done it like this
if (year1>year2 || month1>month2 || day1>day2)
return -1;
if (year1<year2 || month1<month2 || day1<day2)
return +1;
but the output is not quite correct.
Here's a clean way to do it:
#include <tuple> // for std::tie
auto date1 = std::tie(year1, month1, day1);
auto date2 = std::tie(year2, month2, day2);
if (date1 == date2)
return 0;
return (date1 < date2) ? -1 : 1;
The comparisons of std::tie objects are lexicographical, so this returns -1 if date1 is less than date2, 0 if they are the same, and 1 if date1 is greater than date2.
You might be better off defining your own date type (or use boost::datetime).
struct Date
{
unsigned year;
unsigned month;
unsigned day;
};
bool operator<(const Date& lhs, const Date& rhs)
{
return std::tie(lhs.year, lhs.month, lhs.day) <
std::tie(rhs.year, rhs.month, rhs.day);
}
bool operator>(const Date& lhs, const Date& rhs) { .... }
bool operator==(const Date& lhs, const Date& rhs) { .... }
int date_cmp(const Date& lhs, const Date& rhs)
{
// use operators above to return -1, 0, 1 accordingly
}
You need a much more complicated check than that:
if (year1 > year2)
return -1;
else if (year1 < year2)
return +1;
if (month1 > month2)
return -1;
else if (month1 < month2)
return +1;
if (day1 > day2)
return -1;
else if (day1 < day2)
return +1;
return 0;
NOTE: Returning -1 for first is greater than second seems counter-intuititive to me, however I have followed the semantics provided by the OP.
This statement
if (year1>year2 || month1>month2 || day1>day2)
return -1;
tests if any one of the three conditions is true. So, if year1 is higher than year 2, or month1 is higher than month2. Lets stop there. Consider
year1 = 2013, month1 = 12, day1 = 31;
year2 = 2014, month2 = 1, day1 = 1;
We know that, infact, year2 is a higher value, but what happens is
is year1 > year2? no
ok, but is month1 > month2? yes
This makes it look like the first year is a higher value, but it's not, it just a higher month value.
As you get further into C++ you'll find that it's a good idea to try and adopt a convention of making all your comparisons use a single operator (< or >), when you reach a point where you are working with operators you'll understand why.
if (year2 < year1)
return 1;
// we reach this line when year1 <= year2
if (year1 < year2) // elimnate the < case
return -1;
// having eliminated both non-matches,
// we know that by reaching point that both
// dates have the same year. Now repeat for
// the month value.
if (month2 < month1)
return 1;
if (month1 < month2)
return -1;
// year and month must be the same, repeat for day.
if (day2 < day1)
return 1;
if (day1 < day2)
return -1;
return 0; // exact match
//You can try this
int lday,lmonth,lyear;
int nday,nmonth,nyear;
int lhour,lminute;
int nhour,nminute;
sscanf(New_Time,"%d-%d",&nhour,&nminute); //reads the numbers
sscanf(Last_Time,"%d-%d",&lhour,&lminute); //from the string
sscanf(New_Date,"%d-%d-%d",&nday,&nmonth,&nyear);
sscanf(Last_Date,"%d-%d-%d",&lday,&lmonth,&lyear);
//cout << "Last date: " << lday << "-" << lmonth << "-" << lyear <<endl;
//cout << "New date: " << nday << "-" << nmonth << "-" << nyear <<endl;
if(nyear>lyear)
return 0;
if(nyear==lyear) {
if(nmonth > lmonth)
return 0;
if (nmonth == lmonth) {
if(nday > lday)
return 0;
if (nday == lday) {
if( nhour > lhour)
return 0;
if( nhour == lhour) {
if(nminute>lminute) {
//cout << "new time >= last time" << endl <<endl;
return 0;
}
else return 1;
}
else return 1;
}
else return 1;
}
else return 1;
}
else return 1;
struct Day
{
int value;
explicit Day(int value)
{
this->value = value;
}
};
struct Month
{
int value;
explicit Month(int value)
{
this->value = value;
}
};
struct Year
{
int value;
explicit Year(int value)
{
this->value = value;
}
};
class Date {
public:
Date(Day newDay, Month newMonth, Year newYear)
{
_day = newDay.value;
_month = newMonth.value;
_year = newYear.value;
}
int GetYear() const {
return _year;
};
int GetMonth() const {
return _month;
};
int GetDay() const {
return _day;
};
private:
int _year;
int _month;
int _day;
};
bool operator < (const Date& lhs, const Date& rhs)
{
if (lhs.GetYear() == rhs.GetYear()) {
if (lhs.GetMonth() == rhs.GetMonth()) {
if (lhs.GetDay() == rhs.GetDay()) {
return false;
}
return lhs.GetDay() < rhs.GetDay();
}
return lhs.GetMonth() < rhs.GetMonth();
}
return lhs.GetYear() < rhs.GetYear();
};

Overloading the '-' operator

Currently I'm writing a program that has a section to determine the difference in days between two dates, but by overloading the minus operator.
I'm currently staring at my screen drawing a complete blank. I have some fleeting thoughts in my head but they are exactly that, fleeting.
What's to happen in the main.cpp is that there are going to be two variables, for instance beethovenDeathDate and beethovenBirthDate that will be subtracted to determine how long he lived for. Which is something around 22000 days if I recall correctly.
So without further ado, here is my code:
Date.cpp
const std::string Date::MONTH_STRINGS[] =
{
"", //one based indexing
"January",
"February",
"March",
"April",
"May",
"June",
"July",
"August",
"September",
"October",
"November",
"December"
};
const int Date::DAYS_PER_MONTH[] =
{
0, //one based indexing
31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
};
Date::Date(int day, int month, int year) : _year(year), _month(month), _day(day)
{
isValid();
}
Date::Date()
{
time_t t = time(0); // get time now
struct tm * now = localtime( & t );
_year = now -> tm_year + 1900;
_month = now -> tm_mon + 1;
_day = now -> tm_mday;
}
int Date::maxDay(int month, int year)
{
int ret = DAYS_PER_MONTH[month];
if(isLeapYear(year) == true && month == 2)
{
++ret;
}
return ret;
}
void Date::addDay(bool forward)
{
if(forward)
{
if(_day < maxDay(_month, _year))
{
++_day;
}
else
{
_day = MIN_DAY;
++_month;
if(_month > MAX_MONTH)
{
_month = MIN_MONTH;
++_year;
}
}
}
else
{
if(_day <= MIN_DAY)
{
--_month;
if(_month < MIN_MONTH)
{
_month = MAX_MONTH;
--_year;
}
_day = maxDay(_month, _year);
}
else
{
--_day;
}
}
}
std::string Date::toString() const
{
if(isValid() == false)
{
return std::string();
}
std::stringstream ss;
ss << MONTH_STRINGS[_month] << " " << _day << ", " << _year;
return ss.str();
}
bool Date::isValid() const
{
if(_month < MIN_MONTH || _month > MAX_MONTH)
{
std::cerr << "Invalid date " << std::endl;
return false;
}
int daysThisMonth = maxDay(_month, _year);
if(_day < MIN_DAY || _day > daysThisMonth)
{
std::cerr << "Invalid date " << std::endl;
return false;
}
return true;
}
bool Date::isLeapYear(int year)
{
if(!(year % 4))
{
if(!(year % 100))
{
if(!(year % 400))
{
return true;
}
else
{
return false;
}
}
else
{
return true;
}
}
else
{
return false;
}
}
bool Date::isLeapYear() const
{
return isLeapYear(_year);
}
bool Date::isLeapDay() const
{
return isLeapDay(_day, _month, _year);
}
bool Date::isLeapDay(int day, int month, int year)
{
if(day == 29 && month == 2 && isLeapYear(year) == true)
{
return true;
}
else
{
return false;
}
}
void Date::addYears(int years)
{
if(years == 0)
{
return;
}
if(isLeapDay() && !isLeapDay(_day, _month, _year + years))
{
_day = Date::DAYS_PER_MONTH[_month];
}
_year += years;
}
void Date::addMonths(int months)
{
if(months == 0)
{
return;
}
int deltayears = months / MAX_MONTH;
int deltamonths = months % MAX_MONTH;
int newMonth = 0;
if(months > 0)
{
newMonth = (_month + deltamonths) % MAX_MONTH;
if((_month + deltamonths) > MAX_MONTH)
{
++deltayears;
}
}
else
{
if((_month + deltamonths) < MIN_MONTH)
{
--deltayears;
newMonth = _month + deltamonths + MAX_MONTH;
}
else
{
newMonth = _month + deltamonths;
}
}
if(_day > maxDay(newMonth, _year + deltayears))
{
_day = maxDay(newMonth, _year + deltayears);
}
_year += deltayears;
_month = newMonth;
}
void Date::addDays(int days)
{
if(days == 0)
{
return;
}
if(days < 0)
{
for(int i = 0; i > days; --i)
{
addDay(false);
}
return;
}
for(int i = 0; i < days; ++i)
{
addDay(true);
}
}
std::ostream& operator<<(std::ostream& os, const Date& date)
{
os << date.toString();
return os;
}
Date Date::operator+(int days) const
{
Date ret = *this;
ret.addDays(days);
return ret;
}
Date& Date::operator+=(int days)
{
addDays(days);
return *this;
}
//This is where I get stumped (the parameters was just one of my failed experiments
Date& Date::operator-(int day, int month, int year)
{
}
The function can be written either as a member, or as a free function. The member function signature would look like this:
TimeDuration Date::operator-(Date const & rhs) const
The free function would look like this:
TimeDuration operator-(Date const & lhs, Date const & rhs)
TimeDuration here is a completely seperate type representing a length of time. If you want, you could just make it an int signifying the number of days, but it would be better, in my opinion, to have a more expressive type for this purpose. Whatever you decide regarding the return type, it doesn't make any sense for the type to be Date (and certainly not Date&).
A possible (albeit not incredibly efficient) implementation, given that you've already written a function to add a day to a date, would be something like this:
if lhs_date comes before rhs_date
add days to (a copy of) lhs_date until lhs_date == rhs_date
return the negative of number of days added
if rhs_date comes before lhs_date
add days to (a copy of) rhs_date until rhs_date == lhs_date
return the number of days added
else
return 0
Another function you might want (or maybe this is what you actually wanted originally, but your wording doesn't indicate it) is a function which can subtract a length of time from a Date. In that case, the return value would be another Date object (but not Date&), and the possible signatures would look something like this:
Date Date::operator-(TimeDuration rhs) const // member version
Date operator-(Date const & lhs, TimeDuration const & rhs) // non-member version
You should do this:
//This is where I get stumped (the parameters was just one of my failed experiments
TimeDuration& Date::operator-(Date const & d1)
{
// ... processing ...
// this - d1;
}
and call it as:
Date d1 = new Date(20, 01, 2013);
TimeDuration duration = d1 - (new const Date(20, 01, 1922));
// Calculate no. of days or years using duration
The logic is as follows:
Pass two Date objects (first could be implicit) to the overloading function and return TimeDuration
To invoke this operator, you may create a Date object with the data that you have, instead of passing each value separately.
Please check for exact syntax.