c++cdatetime-tdate-math

easy way to add 1 month to a time_t in C/C++


I have some code that uses the Oracle function add_months to increment a Date by X number of months.

I now need to re-implement the same logic in a C / C++ function. For reasons I don't want/need to go into I can't simply issue a query to oracle to get the new date.

Does anyone know of a simple and reliable way of adding X number of months to a time_t? Some examples of the types of calculations are shown below.

30/01/2009 + 1 month = 28/02/2009
31/01/2009 + 1 month = 28/02/2009
27/02/2009 + 1 month = 27/03/2009
28/02/2009 + 1 month = 31/03/2009
31/01/2009 + 50 months = 31/03/2013


Solution

  • Method AddMonths_OracleStyle does what you need.

    Perhaps you would want to replace IsLeapYear and GetDaysInMonth to some librarian methods.

    #include <ctime>
    #include <assert.h>
    
    bool IsLeapYear(int year) 
    {
        if (year % 4 != 0) return false;
        if (year % 400 == 0) return true;
        if (year % 100 == 0) return false;
        return true;
    }
    
    int daysInMonths[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
    
    int GetDaysInMonth(int year, int month)
    {
        assert(month >= 0);
        assert(month < 12);
    
        int days = daysInMonths[month];
    
        if (month == 1 && IsLeapYear(year)) // February of a leap year
            days += 1;
    
        return days;
    }
    
    tm AddMonths_OracleStyle(const tm &d, int months)
    {
        bool isLastDayInMonth = d.tm_mday == GetDaysInMonth(d.tm_year, d.tm_mon);
    
        int year = d.tm_year + months / 12;
        int month = d.tm_mon + months % 12;
    
        if (month > 11)
        {
            year += 1;
            month -= 12;
        }
    
        int day;
    
        if (isLastDayInMonth)
            day = GetDaysInMonth(year, month); // Last day of month maps to last day of result month
        else
            day = std::min(d.tm_mday, GetDaysInMonth(year, month));
    
        tm result = tm();
    
        result.tm_year = year;
        result.tm_mon = month;
        result.tm_mday = day;
    
        result.tm_hour = d.tm_hour;
        result.tm_min = d.tm_min;
        result.tm_sec = d.tm_sec;
    
        return result;
    }
    
    time_t AddMonths_OracleStyle(const time_t &date, int months)
    {
        tm d = tm();
    
        localtime_s(&d, &date);
    
        tm result = AddMonths_OracleStyle(d, months);
    
        return mktime(&result);
    }