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

后端 未结 4 1557
小蘑菇
小蘑菇 2021-01-13 01:51

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.

相关标签:
4条回答
  • 2021-01-13 02:24

    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);
    }
    
    0 讨论(0)
  • 2021-01-13 02:28

    You can use Boost.GregorianDate for this.

    More specifically, determine the month by adding the correct date_duration, and then use end_of_month_day() from the date algorithms

    0 讨论(0)
  • 2021-01-13 02:28

    Really new answer to a really old question!

    Using this free and open source library, and a C++14 compiler (such as clang) I can now write this:

    #include "date.h"
    
    constexpr
    date::year_month_day
    add(date::year_month_day ymd, date::months m) noexcept
    {
        using namespace date;
        auto was_last = ymd == ymd.year()/ymd.month()/last;
        ymd = ymd + m;
        if (!ymd.ok() || was_last)
            ymd = ymd.year()/ymd.month()/last;
        return ymd;
    }
    
    int
    main()
    {
        using namespace date;
        static_assert(add(30_d/01/2009, months{ 1}) == 28_d/02/2009, "");
        static_assert(add(31_d/01/2009, months{ 1}) == 28_d/02/2009, "");
        static_assert(add(27_d/02/2009, months{ 1}) == 27_d/03/2009, "");
        static_assert(add(28_d/02/2009, months{ 1}) == 31_d/03/2009, "");
        static_assert(add(31_d/01/2009, months{50}) == 31_d/03/2013, "");
    }
    

    And it compiles.

    Note the remarkable similarity between the actual code, and the OP's pseudo-code:

    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

    Also note that compile-time information in leads to compile-time information out.

    0 讨论(0)
  • 2021-01-13 02:31

    Convert time_t to struct tm, add X to month, add months > 12 to years, convert back. tm.tm_mon is an int, adding 32000+ months shouldn't be a problem.

    [edit] You might find that matching Oracle is tricky once you get to the harder cases, like adding 12 months to 29/02/2008. Both 01/03/2009 and 28/02/2008 are reasonable.

    0 讨论(0)
提交回复
热议问题