adding dates in C++, using class - c++

I'm a novice in coding...
I've written a code in C++ to calculate the date after adding new numbers to date, month, and year. However, it's become too long and my computer can't process it if the date goes over 50.
Could you give me a few tips if there was a way to shorten this?
#include <iostream>
class Date{
int year; int month; int day;
}
public:
void set_date(int _year, int _month, int _day){
year=_year;
month=_month;
day=_day;
}
void add_day(int inc){
day+=inc;
}
void add_month(int inc){
month+=inc;
}
void add_year(int inc){
year+=inc;
}
void get_date(){
while (day>31){
if ((year%4)==0 && month==2 && day > 29){
month+=1;day-=29;
}
else if(month==2 && day > 28){
month+=1;day-=28;
}
else if((day>30)&&(month==4||month==6||month==9||month==11)){
month+=1;day-=30;
}
else if((day>31)&&(month==3||month==5||month==7||month==8||month==10||month==12||month==1)){
month+=1;day-=31;
}
}
if (month>12){
year+=month/12;
month=month%12;
}
std::cout<<"year"<<year<<std::endl;
std::cout<<"month"<<month<<std::endl;
std::cout<<"day"<<day<<std::endl;
}
int main(){
Date date;
date.set_data(191,2,10);
date.add_day(60);
date.add_month(10);
date.add_year(3);
date.get_date();
return 0;
}

When I run your code it loops forever with day=39 and month=13, which is a condition you don't check for. You might want to move the whole if (month>12) into your loop, so that the months get corrected while processing the days. You also want to add an else that stops the loop:
bool doneProcessing = false;
do
{
if ((year % 4) == 0 && month == 2 && day > 29)
{
month += 1;
day -= 29;
}
else if (month == 2 && day > 28)
{
month += 1;
day -= 28;
}
else if ((day > 30) && (month == 4 || month == 6 || month == 9 || month == 11))
{
month += 1;
day -= 30;
}
else if ((day > 31) && (month == 3 || month == 5 || month == 7 || month == 8 || month == 10 || month == 12 || month == 1))
{
month += 1;
day -= 31;
}
else
{
doneProcessing = true;
}
if (month > 12)
{
year += month / 12;
month = month % 12;
}
}
while (!doneProcessing);
I didn't check the validity of your semantics, but I did notice one thing: When you have month=2 and day=30, you'd want to process that as well, but you only check day>31, thus I changed the loop to simply run until the else is hit (all days are valid now).
With this change your code properly terminates after printing this result:
year195
month2
day8
PS: With a debugger you can easily and quickly find such issues. See What is a debugger and how can it help me diagnose problems?

Related

How to compare two datetime structures in C++ efficiently?

I have the following DateTime structure:
struct DateTime
{
std::uint16_t year;
std::uint8_t month;
std::uint8_t day;
std::uint8_t hour;
std::uint8_t minute;
std::uint8_t second;
std::uint16_t milisecond;
};
My doubt is about the LessThan and GreaterThan methods. I have implemented as follows in order to avoid a bunch of ifs and elses, but I might have not covered all possible situations:
bool GreaterThan(const DateTime& datetime)
{
bool greater{true};
// When found a different value for the most significant value, the evaluation is interrupted
if ((year <= datetime.year) && (month <= datetime.month || year < datetime.year) &&
(day <= datetime.day || month < datetime.month) && (hour <= datetime.hour || day < datetime.day) &&
(minute <= datetime.minute || hour < datetime.hour) &&
(second <= datetime.second || minute < datetime.minute) &&
(milisecond <= datetime.milisecond || second < datetime.second))
{
greater = false;
}
return greater;
}
bool LessThan(const DateTime& datetime)
{
bool less{true};
// When found a different value for the most significant value, the evaluation is interrupted
if ((year >= datetime.year) && (month >= datetime.month || year > datetime.year) &&
(day >= datetime.day || month > datetime.month) && (hour >= datetime.hour || day > datetime.day) &&
(minute >= datetime.minute || hour > datetime.hour) &&
(second >= datetime.second || minute > datetime.minute) &&
(milisecond >= datetime.milisecond || second > datetime.second))
{
less = false;
}
return less;
}
Please, let me know which possible situation is not covered.
Your implementation looks overly painful. There's a simpler, logical way to acheive this. If you know of the greedy algorithm, its a similar thought process to that.
bool GreaterThan(const datetime& datetime)
{
if(year != datetime.year) return year > datetime.year;
if(month != datetime.month) return month > datetime.month;
//... and so on (omitted)
return false; // they are the same
}
You can implement LessThan similarly.
From all the comments, I think the best solution was proposed by –
Richard Critten:
bool GreaterThan(const DateTime& date_time)
{
return (std::tie(year, month, day, hour, minute, second, milisecond) >
std::tie(date_time.year, date_time.month, date_time.day, date_time.hour, date_time.minute, date_time.second, date_time.milisecond));
}
bool LessThan(const DateTime& date_time)
{
return (std::tie(year, month, day, hour, minute, second, milisecond) <
std::tie(date_time.year, date_time.month, date_time.day, date_time.hour, date_time.minute, date_time.second, date_time.milisecond));
}

In C++, using a loop to find the # of days on any given date?

And yes this is an assignment-- So please don't post solutions, but detailed pseudocodes are extremely helpful!
I already have a program in C++ that accepts a date from the user and will determine if it is a leap year or not.
Here is my leap year function so far (I do hope that this is the correct logic):
bool isLeapYear (int year){
int leapYear = 0;
//leapyear = 1 when true, = 0 when false
if ((year%400 == 0) && (year%100 != 100)) {
leapYear = 1;
}
else if (year%4 == 0) {
leapYear = 1;
}
else {
leapYear = 0;
}
if (leapYear == 1) {
return 1;
}
else {
return 0;
}
}
Here is a paraphrase of what I must do next:
You MUST use a loop that adds months one at a time to an accumulated sum.
Don't use any hardcoded values
Such as 152 for the first 5 months in a leap year
And to clarify, this is my first programming class and have been in this C++ class for just about a month now.
So it would be greatly appreciated if anyone could help me figure out how to do the loop statements to add the number of the months?
(IE: 12/31/2013 should be "365" in a non leap year, and "366" in a leap year).
I know this is wrong but this is what I have so far for a function "dayNumber" that simply return the number of days in the year to the main function (which is calling the dayNumber function):
int dayNumber (int day, int month, int year){
//variable declarations
int sumTotal = 0;
for ( int loop = 1; loop < month; loop++) {
sumTotal = (( month-1 ) * 31);
sumTotal = sumTotal + day + 1;
if ( (loop==4) || (loop=6) || (loop=9) ||
(loop==11) ) {
sumTotal = ( sumTotal - 1 );
}
else if ( isLeapYear(year) == 1 ) {
sumTotal = (sumTotal - 2);
}
else {
sumTotal = (sumTotal - 3);
}
}
return sumTotal;
}
I started to mess around with it to get to a proper value for days I knew but it kind of messed it up more, haha.
If anyone has any guidance on how to appropriately do a loop, I would be extremely greatful!:)
EDIT:
Alright, I think I may have answered my own question.
int dayNumber (int day, int month, int year){
//variable declarations
int sumTotal = 0;
for ( int loop = 1; loop < month; loop++) {
sumTotal = ( sumTotal + 31 );
if ( (loop==4) || (loop==6) || (loop==9) ||
(loop==11) ) {
sumTotal = ( sumTotal - 1 );
}
}
if ((month !=2) && (month > 1)) {
if (isLeapYear(year) ==1) {
sumTotal = ( sumTotal - 2 );
}
else {
sumTotal = ( sumTotal - 3);
}
}
else {
sumTotal = sumTotal;
}
sumTotal = sumTotal + day;
return sumTotal;
}
I definitely need to work on my loops.
I appreciate letting me know that my '=' should have been '=='!
I believe this is an appropriate code using a simple enough loop?
I will test some more dates. I've only tested the few provided on the class site so far.
I can't answer my own posts, I don't have enough reputation.
I know an answer has been accepted, but it took me some time writing my own, let's see if it can adds some informations overall.
Let's review this slowly.
First, your isLeapYear() function isn't completely right.
Without delving into the algorithm part, two or three things can be improved.
You're returning a bool, yet your return statements are returning ints. This isn't wrong in itself, but using the true and false keywords can improve the readability and consistency.
Instead of creating, assigning and returning a variable, you should instantly return your result.
Add spaces around your operators : year%400 should become year % 400.
Now your code.
This condition :
if ((year%400 == 0) && (year%100 != 100))
... especially this part :
(year%100 != 100)
Isn't doing what you expect.
Overall, the algorithm is as follow :
if year is not divisible by 4 then common year
else if year is not divisible by 100 then leap year
else if year is not divisible by 400 then common year
else leap year
Translating it in code:
/**/ if (year % 4 != 0)
return false;
else if (year % 100 != 0)
return true;
else if (year % 400 != 0)
return false;
else
return true;
Now let's simplify this a bit:
/**/ if (year % 4 == 0 && year % 100 != 0)
return true;
else if (year % 400 == 0)
return true;
else
return false;
Again:
if ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0)
return true;
else
return false;
And finally, the whole boolean expression can be directly returned:
return (year % 4 == 0 && year % 100 != 0) || year % 400 == 0;
Now that your function is correct, let's try an algorithm to your dayNumber function :
If the date provided in parameters is considered correct, then the algorithm is quite simple :
Start sum from 0
Loop from 1 to month excluded
If month is Frebruary then add 29 if isLeapYear returns true, else 28
If month is January, March, May, July, August, October or December add 31
Else add 30
Add day to sum.
I know that giving detailed pseudocode helps more than a solution, but I'm not that good of a teacher yet :/ Good luck with this answer, and get some sleep when you're done :)
I'm assuming that dayNumber(8, 3, 1914) is supposed to return the ISO day number of 1914, March 8th.
What you want to do is the following:
procedure dayNumber(theDay, theMonth, theYear):
set answer to 1 // the first day of the year is day 1
for every month 'thisMonth', up to and excluding theMonth:
increment answer by the number of days in thisMonth
increment answer by (theDay - 1) // the first day of the month is monthday 1
return answer
In the iteration statement (or 'loop body'), there are three cases:
February: if it's a leap year, increment thisMonth by 29, otherwise increment by 28.
Short months (April, June, September, November): increment by 30 days.
Otherwise, long months (January, March, May, July, August, October, December): increment by 31 days.
This translates to:
if (thisMonth == 2) {
// February
if (isLeapYear(theYear))
thisMonth += 29;
else
thisMonth += 28;
} else if (thisMonth == 4 || thisMonth == 6 || thisMonth == 9 || thisMonth == 11) {
// Short month
thisMonth += 30;
} else {
// Long month
thisMonth += 31;
}
(Note: X += Y is, or should be, short for X = X + Y.)
The loop logic you use looks mostly correct to me, except for 1) off-by-one errors because you start with day 0, and 2) adding the monthday inside the loop rather than outside of it:
int dayNumber (int theDay, int theMonth, int theYear) {
int result = 1;
for (int thisMonth = 1; thisMonth < theMonth; thisMonth++) {
/* ... see above ... */
}
return result + theDay - 1;
}
Now, about making the iteration statement prettier, there are basically two ways (and if your course hasn't yet covered them, I of course recommend against using them in an answer). One is using a switch statement, but I'm really not a fan of them, and it doesn't provide much benefit over the code I already gave. The other would be to use arrays:
int dayNumber (int theDay, int theMonth, int theYear) {
int monthDays[12] = { 31, 28, 31, 30, 31, 30, 31
, 31, 30, 31, 30, 31 }
int result = 1;
for (int thisMonth = 1; thisMonth < theMonth; thisMonth++) {
if (thisMonth == 2 && isLeapYear(theYear))
// Special case: February in a leap year.
result += 29;
else
/* Take the month length from the array.
* Because C++'s array indices begin at 0,
* but our first month is month 1,
* we have to subtract one from thisMonth.
*/
result += monthDays[thisMonth - 1];
}
return result + theDay - 1;
}
#include <ctime>
static int GetDaysInMonthOfTheDate(std::tm curDate)
{
std::tm date = curDate;
int i = 0;
for (i = 29; i <= 31; i++)
{
date.tm_mday = i;
mktime(&date);
if (date1->tm_mon != curDate.tm_mon)
{
break;
}
}
return i - 1;
}

C++ error C2106: '=' : left operand must be l-value

Okay, so, ignoring my lazy coding (this is just to get the program to work, I'll clean it up after I get it working). I've set up a couple of if statements that will throw exceptions if I don't get the input I'd like.
#include<string>
#include<iostream>
using namespace std;
int main()
{
bool flag = false;
int month, day, year;
void header();
class monthClassException
{
public:
monthClassException()
{
message = "Invalid Month";
}
monthClassException(string str)
{
message = str;
}
string what()
{
return message;
}
private:
string message;
};
class dayClassException
{
};
class yearClassException
{
};
header();
do
{
try
{
cout << "Please enter your date of birth (MM-DD-YYYY): " << endl;
cin >> month;
cin.ignore(10,'-');
cin >> day;
cin.ignore(10,'-');
cin >> year;
if (month > 12 || month < 1)
throw monthClassException("Invalid Month Entry");
if( ((month == 4 || month == 6 || month == 9 || month == 11) && day > 30) || day < 1)
throw dayClassException();
else if ( ((month == 1 || month == 3 || month == 5 || month == 7 || month == 8 || month == 10 || month == 12 ) && day > 31) || day < 1)
throw dayClassException();
else if (month == 2 && year % 4 != 0 && day > 28)
throw dayClassException();
else if((month == 2 && year % 4 = 0) && day > 29)
throw dayClassException();
}
catch(monthClassException mCEO)
{
cout << mCEO.what() << endl;
system("pause");
}
catch(dayClassException)
{
cout << "Invalid Day Entered for Selected Month" << endl;
system("pause");
}
catch(yearClassException yCEO)
{
}
}while(!flag);
return 0;
}
I'm getting my error at that very last exception:
else if((month == 2 && year % 4 = 0) && day > 29)
throw dayClassException();
it's saying that month is an invalid l-value (Why now? At the very end after I've already used it -catastrophically, I will admit.) It could be something really obvious that I fail to see because I'm the one who coded it, or it could be because my really really crazy if statements messed something up somewhere.
Any ideas?
Here is the error:
year % 4 = 0
you probably meant to write ==
= operator as in
year % 4 = 0
means assignment, not comparison. Hence your error.
Fix it to
year % 4 == 0
You have year % 4 = 0.
I think you have a typo: you may want year % 4 == 0.
In addition, I prefer using parentheses to make the code clearer:
...
else if ((month == 2) && (year % 4 == 0) && (day > 29)) {
throw dayClassException();
}
You have a the assignment operator = in your condition instead of the comparison operator ==.
That's pretty clearly a logic error. However, why is it a compiler error? After all, C++ allows assignment inside a condition, and that is something you might legitimately do.
In your case, month == 2 && year % 4 = 0 is processed as ((month == 2) && (year % 4)) = 0 (see C++ Operator Precedence). That expression in the parens evaluates to a temporary. But the left side of an assignment operator must refer to a storage address you can write to (an l-value). So your code is invalid for the same reason that 3 = 3 is invalid. Visual Studio calls this error C2106.
A suggestion in this regard, is to always put the constant on the left hand side of a comparison statement. It helps prevent logical errors. As an example consider the code
if year == 0
and mistakenly you had written:
if year = 0
the result would have been a logical error.
Instead putting the constant 0 on the left side, such that
if 0 = year
would generate a syntax error at compilation, hence preventing you from committing a logical error (that can be harder to debug)

Q&A: How do I figure out what the last day of the month is?

I was trying to write a roll-your-own timezone converter and I needed a way of determining what the last possible day of the month was. Upon some research, I discovered the formulas for finding a leap year.
It's a small contribution, but maybe I'll save someone else the 20 minutes it took me to figure out and apply it.
This code accepts a signed short month, indexed at 0 (0 is January) and an int year that is indexed as 0 as well (2012 is 2012).
It returns a 1 indexed day (the 27th is the 27th, but in SYSTEMTIME structures, etc., you usually need 0 indexed - just a head's up).
short _get_max_day(short month, int year) {
if(month == 0 || month == 2 || month == 4 || month == 6 || month == 7 || month == 9 || month == 11)
return 31;
else if(month == 3 || month == 5 || month == 8 || month == 10)
return 30;
else {
if(year % 4 == 0) {
if(year % 100 == 0) {
if(year % 400 == 0)
return 29;
return 28;
}
return 29;
}
return 28;
}
}
What about
#include <time.h>
#include <iostream>
int LastDay (int iMonth, int iYear)
{
struct tm when;
time_t lastday;
// Set up current month
when.tm_hour = 0;
when.tm_min = 0;
when.tm_sec = 0;
when.tm_mday = 1;
// Next month 0=Jan
if (iMonth == 12)
{
when.tm_mon = 0;
when.tm_year = iYear - 1900 + 1;
}
else
{
when.tm_mon = iMonth;
when.tm_year = iYear - 1900;
}
// Get the first day of the next month
lastday = mktime (&when);
// Subtract 1 day
lastday -= 86400;
// Convert back to date and time
when = *localtime (&lastday);
return when.tm_mday;
}
int _tmain(int argc, _TCHAR* argv[])
{
for (int m = 1; m <= 12; m++)
std::cout << "Last day of " << m << " is " << LastDay (m, 2002) << std::endl;
return 0;
}
It prints out (for year 2002)...
Last day of 1 is 31
Last day of 2 is 28
Last day of 3 is 31
Last day of 4 is 30
Last day of 5 is 31
Last day of 6 is 30
Last day of 7 is 31
Last day of 8 is 31
Last day of 9 is 30
Last day of 10 is 31
Last day of 11 is 30
Last day of 12 is 31
I use a simple function that returns the whole date in the from of a (Standard) COleDateTime. It may not be as fast other options, but it is very effective, works for leap years and pretty fool proof.
This is the Code that I am using:
COleDateTime get_last_day_of_month(UINT month, UINT year)
{
if(month == 2)
{ // if month is feb, take last day of March and then go back one day
COleDateTime date(year, 3, 1, 0, 0, 0); // 1 March for Year
date -= 1; // go back one day (the standard class will take leap years into account)
return date;
}
else if(month == 4 || month == 6 || month == 9 || month == 11) return COleDateTime(year, month, 30, 0, 0, 0);
else return COleDateTime(year, month, 31, 0, 0, 0);
}
import datetime
from datetime import date
from dateutil.relativedelta import relativedelta
year = int((date.today()).strftime("%Y"))
month = list(range(1, 13, 1))
YearMonthDay = [(datetime.datetime(year, x, 1) + relativedelta(day=31)).strftime("%Y%m%d") for x in month]
print(YearMonthDay)
['20220131', '20220228', '20220331', '20220430', '20220531', '20220630', '20220731', '20220831', '20220930', '20221031', '20221130', '20221231']
In C++20:
#include <chrono>
std::chrono::day
get_max_day(std::chrono::month m, std::chrono::year y)
{
return (y/m/std::chrono::last).day();
}
If you really need it with a type-unsafe API:
int
get_max_day(int m, int y)
{
return unsigned{(std::chrono::last/m/y).day()};
}
Word Year, Month, Day;
TDateTime datum_tdatetime = Date();
// first day of actual month
datum_tdatetime.DecodeDate(&year, &month, &day);
day = 1;
datum_tdatetime = EncodeDate(year, month, day);
// last day of previous month
datum_tdatetime -= 1;
// first day of previous month
datum_tdatetime.DecodeDate(&year, &month, &day);
day = 1;
datum_tdatetime = EncodeDate(year, month, day);

Can't seem to get the correct output from my code block

I was just wondering if anyone noticed i was doing something wrong with my code block. Ths program is supposed to be a test program that compares 2 dates. The function that im working on is supposed to return a 1 if the invoking date is greater, a -1 f the invoking date is less than, and a 0 if the invoking date is equal to the date in the parameter. My test Program :
#include <cstdlib>
#include <iostream>
#include <string>
#include "date.h"
using namespace std;
//date is initialized in a month/day/year format.
int main(int argc, char* argv[])
{
string* d;
date d1(4,1,4);
date d4(4,4,4);
int greaterTest = d4.compareTo(d1);
int lessTest = d1.compareTo(d4);
cout << greaterTest << endl; //i believe these two lines are printing out a
cout << lessTest << endl; //location in memory
cout<<&d <<endl;
system("pause");
return EXIT_SUCCESS;
}
The huge compareTo() function :
int date::compareTo (date another_date)
{
if (this->year == another_date.year && this->month == month && this->day < another_date.day) //if both year and month are the same, test to see if day is less
{
return -1;
}
else if (this->year == another_date.year && this->month == month && this->day > another_date.day) //if both year and month are the same, test to see if day is greater
{
return 1;
}
else if (this->year == another_date.year && this->month > month) //if the years are the same, test to see if the invoking month is greater
{
return 1;
}
else if (this->year == another_date.year && this->month < month) //if the years are the same, test to see if the invoking month is less
{
return -1;
}
else if (this->year > another_date.year) //test to see if the invoking year is greater
{
return 1;
}
else if (this->year < another_date.year) //test to see if the invoking year is less
{
return -1;
}
else if(this-> year == another_date.year && this-> month == another_date.month //test if the dates are exactly the same
&& this-> day == another_date.day)
{
return 0;
}
//else{ return 15;} //if none are true, return 15
}
the only problem im getting is when i try to change the day (the second parameter for date).
I'm not sure if this is the problem, since I can't test it... But, your compareTo function has this line:
this->month == month
Shouldn't it be:
this->month == another_date.month
?
In the first if statement and a few below it as well you have:
this->month == month
This is comparing month to itself, I think you meant:
this->month == another_date.month
Also you don't need to use the 'this' pointer all the time,
month == another_date.month
should suffice.
That might benefit from some early exit:
int date::compareTo (date another_date)
{
if (year > another_date.year) {
//the invoking year is greater
return 1;
}
if (year < another_date.year) {
//the invoking year is less
return -1;
}
// if we reached here, the years are the same. Don't need to compare them for the other cases
if (month > another_date.month) {
return 1;
}
if (month < another_date.month) {
return -1;
}
// if we reached here, the year and month are the same
if (day > another_date.day) {
return 1;
}
if (day < another_date.day) {
return -1;
}
// if we reached here, the year and month and day are the same
return 0;
}
Along the way, the cut+paste error just... disappeared, because that test became redundant.
Unless you're really set on doing an element-by-element comparison, I'd put each set of inputs into a struct tm, then use mktime to convert those to a time_t, and the compare the two time_ts directly. In a typical case, those will be a 32- or 64-bit integer of the number of seconds since midnight Jan 1, 1970, so after the conversion, comparison becomes trivial.
I didn't find your bug in your original code because it was too hard for me to read. I suppose that's why you didn't find it either.
This alternative might be easier to read, and easier to prove correct:
// untested
int date::compareTo (date another_date)
{
if (year < another_date.year) return -1;
if (year > another_date.year) return 1;
if (month < another_date.month) return -1;
if (month > another_date.month) return 1;
if (day < another_date.day) return -1;
if (day > another_date.day) return 1;
return 0;
}