在C/C++中给time_t加上1个月的简单方法

xwmevbvl  于 8个月前  发布在  C/C++
关注(0)|答案(4)|浏览(146)

我有一些代码,使用Oracle函数add_months将Date增加X个月。
我现在需要在C / C++函数中重新实现相同的逻辑。由于我不想/不需要进入的原因,我不能简单地向Oracle发出查询来获得新的日期。
有没有人知道一个简单可靠的方法来将X个月数添加到time_t?计算类型的一些示例如下所示。
30/01/2009 + 1个月= 28/02/2009
31/01/2009 + 1个月= 28/02/2009
2009年2月27日+ 1个月= 2009年3月27日
2009年2月28日+ 1个月= 2009年3月31日
31/01/2009 + 50个月= 31/03/2013

rpppsulh

rpppsulh1#

你可以使用Boost.GregorianDate来实现。
更具体地说,通过添加正确的date_duration来确定月份,然后从日期算法中使用end_of_month_day()

kiz8lqtg

kiz8lqtg2#

  • 真正 * 新的答案一个 * 真正 * 老问题!

使用this free and open source library和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
2009年2月27日+ 1个月= 2009年3月27日
2009年2月28日+ 1个月= 2009年3月31日
31/01/2009 + 50个月= 31/03/2013
还要注意,编译时信息 in 会导致编译时信息 out

C++20 <chrono>更新:

你的C供应商开始发布C20 <chrono>,它可以在没有第三方库的情况下使用几乎相同的语法做到这一点。
还要注意OP添加月份所需的一些不寻常的规则,这在<chrono>中很容易实现:
如果结果月份溢出日期字段(如果输入月份是该月的最后一天),则将结果捕捉到该月的末尾。

#include <chrono>

constexpr
std::chrono::year_month_day
add(std::chrono::year_month_day ymd, std::chrono::months m) noexcept
{
    using namespace std::chrono;
    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 std::chrono;
    static_assert(add(30d/01/2009, months{ 1}) == 28d/02/2009);
    static_assert(add(31d/01/2009, months{ 1}) == 28d/02/2009);
    static_assert(add(27d/02/2009, months{ 1}) == 27d/03/2009);
    static_assert(add(28d/02/2009, months{ 1}) == 31d/03/2009);
    static_assert(add(31d/01/2009, months{50}) == 31d/03/2013);
}

Demo.

dsekswqp

dsekswqp3#

time_t转换为struct tm,将X添加到月份,将月份> 12添加到年份,然后转换回来。tm.tm_mon是一个int,添加32000+个月应该没有问题。
[编辑]你可能会发现匹配Oracle是棘手的,一旦你到了更难的情况下,如添加12个月到29/02/2008。01/03/2009和28/02/2008均合理。

clj7thdc

clj7thdc4#

方法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);
}

相关问题