простой способ добавить 1 месяц к time_t в C / C ++ - PullRequest
6 голосов
/ 08 января 2009

У меня есть некоторый код, который использует функцию Oracle add_months для увеличения даты на число месяцев X.

Теперь мне нужно заново реализовать ту же логику в функции C / C ++. По причинам, по которым я не хочу / не хочу углубляться, я не могу просто отправить запрос в oracle, чтобы получить новую дату.

Кто-нибудь знает простой и надежный способ добавления X числа месяцев к time_t? Некоторые примеры типов расчетов приведены ниже.

30.01.2009 + 1 месяц = ​​28.02.2009
31.01.2009 + 1 месяц = ​​28.02.2009
27.02.2009 + 1 месяц = ​​27.03.2009
28.02.2009 + 1 месяц = ​​31.03.2009
31.01.2009 + 50 месяцев = 31.03.2013

Ответы [ 4 ]

6 голосов
/ 08 января 2009

Для этого вы можете использовать Boost.GregorianDate .

В частности, определите месяц, добавив правильное значение date_duration, а затем используйте end_of_month_day() из алгоритмов даты

5 голосов
/ 08 января 2009

Преобразование time_t в struct tm, добавление X к месяцу, добавление месяцев> 12 к годам, преобразование обратно. tm.tm_mon - это int, добавление более 32 000 месяцев не должно быть проблемой.

[править] Вы можете обнаружить, что подобрать Oracle сложно, как только вы дойдете до более сложных случаев, таких как добавление 12 месяцев к 29.02.2008. И 01/03/2009, и 28/02/2008 разумны.

4 голосов
/ 20 июля 2015

Действительно новый ответ на действительно старый вопрос!

Используя эту бесплатную библиотеку с открытым исходным кодом и компилятор C ++ 14 (например, clang), теперь я могу написать это:

#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, "");
}

И он компилируется.

Обратите внимание на удивительное сходство фактического кода и псевдокода OP:

30.01.2009 + 1 месяц = ​​28.02.2009
31.01.2009 + 1 месяц = ​​28.02.2009
27.02.2009 + 1 месяц = ​​27.03.2009
28.02.2009 + 1 месяц = ​​31.03.2009
31.01.2009 + 50 месяцев = 31.03.2013

Также обратите внимание, что информация времени компиляции в приводит к информации времени компиляции out .

3 голосов
/ 08 января 2009

Метод AddMonths_OracleStyle делает то, что вам нужно.

Возможно, вы захотите заменить IsLeapYear и GetDaysInMonth на некоторые библиотечные методы.

#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);
}
...