I have written a program that should find the days between two dates, but it has some hiccups. The logic makes perfect sense in my head when I read through it, so
There are multiple problems in your code snippet.. but I must say it is a very good attempt. There are many short cuts to what you're try to achieve.
I have written the following program which finds the number of days between two given dates. You may use this as a reference.
#include <stdio.h>
#include <stdlib.h>
char *month[13] = {"None", "Jan", "Feb", "Mar",
"Apr", "May", "June", "July",
"Aug", "Sept", "Oct",
"Nov", "Dec"};
/*
daysPerMonth[0] = non leap year
daysPerMonth[1] = leap year
*/
int daysPerMonth[2][13] = {{-1, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
{-1, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}};
typedef struct _d {
int day; /* 1 to 31 */
int month; /* 1 to 12 */
int year; /* any */
}dt;
void print_dt(dt d)
{
printf("%d %s %d \n", d.day, month[d.month], d.year);
return;
}
int leap(int year)
{
return ((year % 4 == 0 && year % 100 != 0) || year % 400 ==0) ? 1 : 0;
}
int minus(dt d1, dt d2)
{
int d1_l = leap(d1.year), d2_l = leap(d2.year);
int y, m;
int total_days = 0;
for (y = d1.year; y >= d2.year ; y--) {
if (y == d1.year) {
for (m = d1.month ; m >= 1 ; m--) {
if (m == d1.month) total_days += d1.day;
else total_days += daysPerMonth[leap(y)][m];
// printf("%d - %5s - %d - %d \n", y, month[m], daysPerMonth[leap(y)][m], total_days);
}
} else if (y == d2.year) {
for (m = 12 ; m >= d2.month ; m--) {
if (m == d2.month) total_days += daysPerMonth[leap(y)][m] - d2.day;
else total_days += daysPerMonth[leap(y)][m];
// printf("%d - %5s - %d - %d \n", y, month[m], daysPerMonth[leap(y)][m], total_days);
}
} else {
for (m = 12 ; m >= 1 ; m--) {
total_days += daysPerMonth[leap(y)][m];
// printf("%d - %5s - %d - %d \n", y, month[m], daysPerMonth[leap(y)][m], total_days);
}
}
}
return total_days;
}
int main(void)
{
/* 28 Oct 2018 */
dt d2 = {28, 10, 2018};
/* 30 June 2006 */
dt d1 = {30, 6, 2006};
int days;
int d1_pt = 0, d2_pt = 0;
if (d1.year > d2.year) d1_pt += 100;
else d2_pt += 100;
if (d1.month > d2.month) d1_pt += 10;
else d2_pt += 10;
if (d1.day > d2.day) d1_pt += 1;
else d2_pt += 1;
days = (d1_pt > d2_pt) ? minus(d1, d2) : minus(d2, d1);
print_dt(d1);
print_dt(d2);
printf("number of days: %d \n", days);
return 0;
}
The output is as follows:
$ gcc dates.c
$ ./a.out
30 June 2006
28 Oct 2018
number of days: 4503
$
Note: this is not a complete program. It lacks input validation.
Hope it helps!
Reduce all month indexes by 1.
What I mean to say is January will correspond to daysPerMonth[0]
or daysPerMonthLeap[0]
and not daysPerMonth[1]
or daysPerMonthLeap[1]
.
The reason for this being array indexes start from 0.
So, wherever you are using month1
, month2
insidedaysPerMonth[]
or daysPerMonthLeap[]
, use month1-1
and month2-1
instead.
I hope this is clear enough. Otherwise, feel free to comment.
//Difference/Duration between two dates
//No need to calculate leap year offset or anything
// Author: Vinay Kaple
# include <iostream>
using namespace std;
int main(int argc, char const *argv[])
{
int days_add, days_sub, c_date, c_month, b_date, b_month, c_year, b_year;
cout<<"Current Date(dd mm yyyy): ";
cin>>c_date>>c_month>>c_year;
cout<<"Birth Date(dd mm yyyy): ";
cin>>b_date>>b_month>>b_year;
int offset_month[12] = {0,31,59,90,120,151,181,212,243,273,304,334};
days_add = c_date + offset_month[c_month-1];
days_sub = b_date + offset_month[b_month-1];
int total_days = (c_year-b_year)*365.2422 + days_add - days_sub+1;
cout<<"Total days: "<<total_days<<"\n";
int total_seconds = total_days*24*60*60;
cout<<"Total seconds: "<<total_seconds<<"\n";
return 0;
}
First, that leap
function feels overly complicated; you don't need to do both dates in one function call, and I'm sure that can be written more succinctly so that it is more obviously correct. Here's a version I've got laying around that isn't succinct but I'm confident it is easy to check the logic:
int is_leap_year(int year) {
if (year % 400 == 0) {
return 1;
} else if (year % 100 == 0) {
return 0;
} else if (year % 4 == 0) {
return 1;
} else {
return 0;
}
}
You could call it like this:
int year1, year2, leap1, leap2;
year1 = get_input();
year2 = get_input();
leap1 = is_leap_year(year1);
leap2 = is_leap_year(year2);
No pointers and significantly less code duplication. Yes, I know that is_leap_year()
can be reduced to a single if(...)
statement, but this is easy for me to read.
Second, I think you're got a mismatch between 0-indexed arrays and 1-indexed human months:
if(*month1 < 1 || *month1 > 12)
vs
int daysPerMonth[] = {31,28,31,30,31,30,31,31,30,31,30,31};
Third, I think that days per month can be calculated slightly nicer:
int days_in_month(int month, int year) {
int leap = is_leap_year(year);
/* J F M A M J J A S O N D */
int days[2][12] = {{31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
{31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}};
if (month < 0 || month > 11 || year < 1753)
return -1;
return days[leap][month];
}
Here, I assume January is 0; you would need to force the rest of the code to match. (I learned this double-array trick from The Elements of Programming Style (page 54).) The best part of using a routine like this is that it removes the leap condition from the difference calculation.
Fourth, you're indexing arrays outside their bounds:
for(i = month1 + 1; i <= 12; i++)
{
if(leap1 == 1)
total += daysPerMonthLeap[i];
This is just another instance of the problem with 0-indexed arrays and 1-indexed months -- but be sure that you fix this, too, when you fix the months.
I have a fear that I haven't yet found all the issues -- you may find it easier to sort the first and the second date after input and remove all that validation code -- and then use names before
and after
or something to give names that are easier to think through in the complicated core of the calculation.
This is not a complete answer. I just wanted to mention a better way to calculate leap year (this is taken from The C Programming Language
- Page #41)
if ((year % 4 == 0 && year % 100 != 0) || year % 400 ==0)
printf("%d is a leap year \n", year);
else
printf("%d is not a leap year \n", year);
Change
int daysPerMonth[] = {31,28,31,30,31,30,31,31,30,31,30,31};
int daysPerMonthLeap[] = {31,29,31,30,31,30,31,31,30,31,30,31};
to
int daysPerMonth[] = {0,31,28,31,30,31,30,31,31,30,31,30,31};
int daysPerMonthLeap[] = {0,31,29,31,30,31,30,31,31,30,31,30,31};
i.e. pad the arrays at the beginning since all the code relies on the array values to start at element 1 rather than element 0.
That will get rid of the error you complained of.
The other problem is an off-by-one error when you add day2
to the total. In both cases you should add day2 - 1
rather than day2
. This is also due to the date indexes starting at 1 instead of 0.
After I made these changes (plus a couple just to get the code to compile), it works properly.