16

I'm trying to figure out a way for my program to take a date (like February 2nd, 2003) and show the difference between the two with another date (like April 2nd, 2012), excluding leap years. So far I've only been able to figure it out if the dates are in the same month, just by subtracting the "day". In this program I use 2 sets of "month", "day" and "year" integers. I'm pretty much at a loss from where to go from here. This is a completely optional part of my assignment but I'd like to get an idea on how to get it to work. It seems like a hassle to me, but maybe there's a simple math formula I'm not thinking about?

Sorry, I don't have any pre-existing code for this part because the rest of the assignment just deals with having the user enter dates and then adding and subtracting a single day.

Hydlide
  • 361
  • 2
  • 5
  • 18

9 Answers9

21

Using just the standard library, you can convert a moderately insane date structure into a count of seconds since an arbitrary zero point; then subtract and convert into days:

#include <ctime>

// Make a tm structure representing this date
std::tm make_tm(int year, int month, int day)
{
    std::tm tm = {0};
    tm.tm_year = year - 1900; // years count from 1900
    tm.tm_mon = month - 1;    // months count from January=0
    tm.tm_mday = day;         // days count from 1
    return tm;
}

// Structures representing the two dates
std::tm tm1 = make_tm(2012,4,2);    // April 2nd, 2012
std::tm tm2 = make_tm(2003,2,2);    // February 2nd, 2003

// Arithmetic time values.
// On a posix system, these are seconds since 1970-01-01 00:00:00 UTC
std::time_t time1 = std::mktime(&tm1);
std::time_t time2 = std::mktime(&tm2);

// Divide by the number of seconds in a day
const int seconds_per_day = 60*60*24;
std::time_t difference = (time1 - time2) / seconds_per_day;    

// To be fully portable, we shouldn't assume that these are Unix time;
// instead, we should use "difftime" to give the difference in seconds:
double portable_difference = std::difftime(time1, time2) / seconds_per_day;

Using Boost.Date_Time is a little less weird:

#include "boost/date_time/gregorian/gregorian_types.hpp"

using namespace boost::gregorian;
date date1(2012, Apr, 2);
date date2(2003, Feb, 2);
long difference = (date1 - date2).days();

It seems like a hassle to me, but maybe there's a simple math formula I'm not thinking about?

It is indeed a hassle, but there is a formula, if you want to do the calculation yourself.

Mike Seymour
  • 249,747
  • 28
  • 448
  • 644
  • 1
    Just a nit (since I don't know of any platform where it will fail), but the standard doesn't say anything about the representation of time in a `time_t`, or what subtracting `time_t` will give you. You're supposed to used `difftime` (which returns a `double`, which introduces its own set of problems). – James Kanze Apr 03 '12 at 07:32
  • @JamesKanze: Good point; POSIX specifies that it's a count of seconds, but C leaves it implementation-defined. – Mike Seymour Apr 03 '12 at 07:53
  • It might be worth mentioning that this solution works properly even across DST boundaries because `make_tm()` does not set `tm_isdst` so `mktime()` will act as if DST doesn't exist and a day will always be `60*60*24` seconds. – AndreKR Nov 15 '21 at 00:27
  • Not a great answer, because not all days have `60*60*24` seconds. Every couple of years or so a day is assigned an [extra second](https://en.wikipedia.org/wiki/Leap_second). Maybe the formula is the way to go, or the [Rata Die](https://en.wikipedia.org/wiki/Rata_Die) equivalent. – EML Feb 10 '23 at 10:30
8

New answer for an old question:

chrono-Compatible Low-Level Date Algorithms

has formulas for converting a {year, month, day} triple to a serial count of days and back. You can use it to calculate the number of days between two dates like this:

std::cout << days_from_civil(2012, 4, 2) - days_from_civil(2003, 2, 2) << '\n';

which outputs:

3347

The paper is a how-to manual, not a library. It uses C++14 to demonstrate the formulas. Each formula comes with a detailed description and derivation, that you only have to read if you care about knowing how the formula works.

The formulas are very efficient, and valid over an extremely large range. For example using 32 bit arithmetic, +/- 5 million years (more than enough).

The serial day count is a count of days since (or prior to for negative values) New Years 1970, making the formulas compatible with Unix Time and all known implementations of std::chrono::system_clock.

The days_from_civil algorithm is not novel, and it should look very similar to other algorithms for doing the same thing. But going the other way, from a count of days back to a {year, month, day} triple is trickier. This is the formula documented by civil_from_days and I have not seen other formulations that are as compact as this one.

The paper includes example uses showing typical computations, std::chrono interoperability, and extensive unit tests demonstrating the correctness over +/- 1 million years (using a proleptic Gregorian calendar).

All of the formulas and software are in the public domain.

Howard Hinnant
  • 206,506
  • 52
  • 449
  • 577
8

Here is a complete code to calculating date difference in y/m/d.

Assuming that to and from are date types, and that months and days start from 1 (similar to Qt):

static int increment[12] = { 1, -2, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1 };

int daysInc = 0;
if (to.day() - from.day() < 0)
{
    int month = to.month() - 2; // -1 from zero, -1 previous month.
    if (month < 0)
        month = 11; // Previous month is December.
    daysInc = increment[month];
    if ( (month == 1) && (to.year()%4 == 0) )
        daysInc++; // Increment days for leap year.
}

int total1 = from.year()*360 + from.month()*30 + from.day();
int total2 = to.year()*360 + to.month()*30 + to.day();
int diff = total2 - total1;
int years = diff/360;
int months = (diff - years*360)/30;
int days = diff - years*360 - months*30 + daysInc;

// Extra calculation when we can pass one month instead of 30 days.
if (from.day() == 1 && to.day() == 31) {
    months--;
    days = 30;
}

I tried this algorithm and it is working okay. Let me know if you have troubles using/understanding it.

Borzh
  • 5,069
  • 2
  • 48
  • 64
8

Since you are looking for mathematical formula , it will help you to find a solution to your problem. Let Y be the year,M be the month and D be the day. Do this calculation for both the dates.

Total = Y* 365 + M*30 + D ,then find the difference between 2 totals of the corresponding dates.

While multiplying 30 with M value ,you have to give the number of days in that month. You can do it with #define value or if loop. Similarly you can do for leap year too by multiplying 366 with Y .

Hope this will help u....

Chetter Hummin
  • 6,687
  • 8
  • 32
  • 44
Revathy
  • 103
  • 5
2

There is another way round...

  • Given two dates, take the year of the earlier date as the reference year.
  • Then calculate no. of days between each of the two given dates and that 1/1/<that year>
  • Keep a separate function that tells the number of days elapsed till a specific month.
  • The absolute difference of those two no. of days will give the difference between the two given dates.
  • Also, do not forget to consider leap years!

The code:

#‎include‬<stdio.h>
#include<math.h>
typedef struct
{
    int d, m, y;
} Date;
int isLeap (int y)
{
    return (y % 4 == 0) && ( y % 100 != 0) || (y % 400 == 0);
}
int diff (Date d1, Date d2)                         //logic here!
{
    int dd1 = 0, dd2 = 0, y, yref;                  //dd1 and dd2 store the <i>no. of days</i> between d1, d2 and the reference year
    yref = (d1.y < d2.y)? d1.y: d2.y;               //that <b>reference year</b>
    for (y = yref; y < d1.y; y++)
        if (isLeap(y))                              //check if there is any leap year between the reference year and d1's year (exclusive)
            dd1++;
    if (isLeap(d1.y) && d1.m > 2) dd1++;                //add another day if the date is past a leap year's February
    dd1 += daysTill(d1.m) + d1.d + (d1.y - yref) * 365;     //sum up all the tiny bits (days)
    for (y = yref; y < d2.y; y++)                       //repeat for d2
        if(isLeap(y))
            dd2++;
    if (isLeap(y) && d2.m > 2) dd2++;
    dd2 += daysTill(d2.m) + d2.d + (d2.y - yref) * 365;
    return abs(dd2 - dd1);                          //return the absolute difference between the two <i>no. of days elapsed past the reference year</i>
}
int daysTill (int month)                            //some logic here too!!
{
    int days = 0;
    switch (month)
    {
        case 1: days = 0;
        break;
        case 2: days = 31;
        break;
        case 3: days = 59;
        break;
        case 4: days = 90;      //number of days elapsed before April in a non-leap year
        break;
        case 5: days = 120;
        break;
        case 6: days = 151;
        break;
        case 7: days = 181;
        break;
        case 8: days = 212;
        break;
        case 9: days = 243;
        break;
        case 10:days = 273;
        break;
        case 11:days = 304;
        break;
        case 12:days = 334;
        break;
    }
    return days;
}
main()
{
    int t;          //no. of test cases
    Date d1, d2;    //d1 is the first date, d2 is the second one! obvious, duh!?
    scanf ("%d", &t);
    while (t--)
    {
        scanf ("%d %d %d", &d1.d, &d1.m, &d1.y);
        scanf ("%d %d %d", &d2.d, &d2.m, &d2.y);
        printf ("%d\n", diff(d1, d2));
    }
}

Standard Input:

1
23 9 1960
11 3 2015

Standard Output:

19892

Code in action: https://ideone.com/RrADFR

Better algorithms, optimizations and edits are always welcome!

skrtbhtngr
  • 2,223
  • 23
  • 29
2

I'm not sure what platform are you on? Windows, Linux? But let us pretend that you would like to have a platform independent solution and the langugage is standard C++.

If you can use libraries you can use the Boost::Date_Time library (http://www.boost.org/doc/libs/1_49_0/doc/html/date_time.html)

If you cannot use libraries to solve your assignment, you will need to find a common simple ground. Maybe you could convert all the dates to seconds, or days substract them and then convert that back to the data again. Substracting days or months as integers will not help as it will lead to incorrect results unless you do not take into account the rest. Hope that helps.

Like dbrank0 pointed it out. :)

ervinbosenbacher
  • 1,720
  • 13
  • 16
1

If you need to do it yourself, then one way to do this pretty easy is by converting dates into a Julian Day. You get formulas at that link, and from conversion on, you only work with floats, where each day is 1 unit.

dbrank0
  • 9,026
  • 2
  • 37
  • 55
0

I've made similar program but it count only days in border of one year or couple years

PS I'm in c++ programming only about two month

    #include<iostream>

    int calculateDaysInYears(int intYear, int endYear);
    int checkYear(int intYear);
    int checkMonth(int i, int intYear);

    int getUserData()
{
    int dayOrMonthOrYear;
    std::cin >> dayOrMonthOrYear;
    return dayOrMonthOrYear;
}
    int calculateMonthInYears(int initialMonth, int endMonth, int initialYear)
{
    //Подсчет дней начальной даты для варианта с несколькими годами
    int x(0);

    initialMonth++;

    for (int i = initialMonth; i <= endMonth; i++)
        x += checkMonth(i, initialYear);

    return x;
}
    int calculateMonth(int startMonth, int endMonth, int initialYear)
{
    //Формула для подсчета кол-вa дней промежуточных месяцев
    //Расчет в пределах года
    
    startMonth++;

    int x(0);

    for (int i = startMonth; i < endMonth; i++)

        x += checkMonth(i, initialYear);

    return x;
}
    int calculateMonthForEndYear(int endMonth, int endYear)
{
    //Подсчет дней в конечном году
    int x(0);

    //Декремент кол-ва конечных месяцев для компенсации дней последнего месяца
    --endMonth;

    for (int i = 1; i <= endMonth; i++)
        x += checkMonth(i, endYear);

    return x;
}
    int checkMonth(int i, int intYear)
{
        if (i == 1 || i == 3 || i == 5 || i == 7 || i == 8 || i == 10 || i == 12)
            return 31;
        else 
            if (i == 2)
            {
                //Если год високосный, то делится на 4 и 400 без остатка, а на 100 с остатком
                if ((intYear % 4 == 0) && (intYear % 100 != 0 ) || (intYear % 400 == 0))
                    return 29;                                                  
                else
                    return 28;
            }
        else
            return 30;

}
    int calculateAmountOfDays(int initialDay, int initialMonth, int initialYear)
{
    //Подсчет дней до конца стартового месяца
    int month = checkMonth(initialMonth, initialYear);
    int days = month - initialDay;
    return days;
}
    int allDays(int initilDays, int endDays, int midleMonth)
{
    int totalDays;
    
    //Подсчет всех дней от начала до конца
    totalDays = midleMonth + initilDays + endDays;
    return totalDays;
}
    int checkYear(int intYear)
{
    if ((intYear % 4 == 0) && (intYear % 100 != 0) || (intYear % 400 == 0))
        return 366;//Високосный год
    else
        return 365;//Невисокосный год
}
    int calculateDaysInYears(int intYear, int endYear)
{
    //Начальное кол-во дней. Необходимо для запуска счетчика
    int amountDays(0);

    //Инкремент начального года для компенсации кол-ва дней промежуточных годов
    intYear++;

    for (int i = intYear; i < endYear; i++)
        amountDays += checkYear(i);

    return amountDays;
}
    int main()
{
    int initialDay;
    int initialMonth;
    int initialYear;
    int endDay;
    int endMonth;
    int endYear;

    std::cout << "Hello! I'm your calendar calculator."            << std::endl <<
                 "Here some rules: "                               << std::endl << 
                 "you should enter a data like(d.m.y): 23.8.2020." << std::endl <<
                 "Also you can ask me to calculate for couple years or in border of one year. Good luck! " << std::endl;

    std::cout << "" << std::endl;

    //Начальная дата
    std::cout << "Enter an initial day: ";
    initialDay = getUserData();

    std::cout << "Enter an initial month: ";
    initialMonth = getUserData();

    std::cout << "Enter an initial year: ";
    initialYear = getUserData();

    std::cout << "" << std::endl;//Пропуск строки

    //Конечная дата
    std::cout << "Enter an end day: ";
    endDay = getUserData();

    std::cout << "Enter an end month: ";
    endMonth = getUserData();

    std::cout << "Enter an end year: ";
    endYear = getUserData();

    //Проверка кол-ва годов
    if ((endYear - initialYear) >= 1)
    {
        //Подсчет дней до конца начального года
        int daysToTheEndOfStartYear = calculateMonthInYears(initialMonth, 12, initialYear) + calculateAmountOfDays(initialDay, initialMonth, initialYear);

        //Подсчет дней до конца конечного месяца
        int daysToTheEndOfEndYear = calculateMonthForEndYear(endMonth, endYear) + endDay;

        //Подсчет дней между годами
        int whalDays = calculateDaysInYears(initialYear, endYear);

        //Подсчет конечной цыфры
        int allDay = whalDays + daysToTheEndOfEndYear + daysToTheEndOfStartYear;

        //Вывод в консоль
        std::cout << allDay;
    }
    else
    {
        //Дни месяцев между начальным и конечным месяцами
        int daysInMonths = calculateMonth(initialMonth, endMonth, initialYear);

        //Подсчет дней до конца начального месяца
        int daysInFirstMonth = calculateAmountOfDays(initialDay, initialMonth, initialYear);

        //Подсчет конечной цыфры
        int allDay = daysInMonths + daysInFirstMonth + endDay;

        //Вывод в консоль
        std::cout << allDay;
    }
    return 0;
}
Dima
  • 1
  • 1
-3

You should look at the DateTime class.

Also the msdn reference for C++ syntax.

nikhil
  • 8,925
  • 21
  • 62
  • 102