I'm trying to calculate the difference between two UNIX timestamps (i.e. seconds since 1970). I want to say e.g. "3 years, 4 months, 6 days, etc" but I don't know how to account for leap years and months of different durations. Surely this is a solved problem..?
This is different to other questions which want to express an approximate duration in one unit that has a fixed/homogeneous duration (e.g. 3 hours, or 7 weeks, etc.). Results for 1. Jan to 1. Feb would be "1 month" and results for 1. Feb to 1. Mar would also be "1 month" even though the number of days are different.
I want to express the complete duration precisely but in years/months/days/hours/minutes. Solutions in C++ would be appreciated!
For the older date, use the Leap Year formula to start counting the number of days from Unix start date, Jan1st 1970, to your first timestamp
(For leap seconds, dunno if you need to get that precise, hopefully will be out-of-scope?)
Leap year calculated by constraining date to after 1600AD and the algorithm for the Gregorian Calendar from: http://en.wikipedia.org/wiki/Leap_year of
if year modulo 400 is 0
then is_leap_year
else if year modulo 100 is 0
then not_leap_year
else if year modulo 4 is 0
then is_leap_year
else
not_leap_year
If a year is a Leap Year, then there are 29days in Feb, else 28days
Now you know the month, day_of_month, year for the 1st variable
Next, another set of counts of days to the 2nd timestamp, using the Leap Year formula till you get to the 2nd timestamp.
typedef struct {
int year;
int month;
int dayOfMonth;
} date_struct;
static int days_in_month[2][13] = {
{0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
{0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
};
int isLeapYear(int year) {
int value;
value = year;
//if year modulo 400 is 0
// then is_leap_year
value = year % 400;
if(value == 0) return 1;
//else if year modulo 100 is 0
// then not_leap_year
value = year % 100;
if(value == 0) return 0;
//else if year modulo 4 is 0
// then is_leap_year
value = year % 4;
if(value == 0) return 1;
//else
// not_leap_year
return 0;
}
date_struct addOneDay(date_struct ds, int isLeapYear){
int daysInMonth;
ds.dayOfMonth++;
//If the month is February test for leap year and adjust daysInMonth
if(ds.month == 2) {
daysInMonth = days_in_month[isLeapYear][ds.month];
} else {
daysInMonth = days_in_month[0][ds.month];
}
if(ds.dayOfMonth > daysInMonth) {
ds.month++;
ds.dayOfMonth = 1;
if(ds.month > 12) {
ds.year += 1;
ds.month = 1;
}
}
return ds;
}
long daysBetween(date_struct date1, date_struct date2){
long result = 0l;
date_struct minDate = min(date1, date2);
date_struct maxDate = max(date1, date2);
date_struct countingDate;
countingDate.year = minDate.year;
countingDate.month = minDate.month;
countingDate.dayOfMonth = minDate.dayOfMonth;
int leapYear = isLeapYear(countingDate.year);
int countingYear = countingDate.year;
while(isLeftDateSmaller(countingDate,maxDate)) {
countingDate = addOneDay(countingDate,leapYear);
//if the year changes while counting, check to see if
//it is a new year
if(countingYear != countingDate.year) {
countingYear = countingDate.year;
leapYear = isLeapYear(countingDate.year);
}
result++;
}
return result;
}
(I wrote an open source program that gives the difference between two calendar dates, in C/C++. Its source code, some that I posed above, might help give you inspiration for your own solution, or maybe you can adapt some of it, too http://mrflash818.geophile.net/software/timediff/ )