在 C/C++ 中向 time_t 添加 1 个月的简单方法

发布于 2024-07-11 05:27:10 字数 434 浏览 7 评论 0原文

我有一些代码使用 Oracle 函数 add_months 将日期增加 X 个月。

我现在需要在 C / C++ 函数中重新实现相同的逻辑。 由于我不想/不需要进入的原因,我不能简单地向 Oracle 发出查询来获取新日期。

有谁知道向 time_t 添加 X 个月的简单可靠的方法? 下面显示了一些计算类型的示例。

2009 年 1 月 30 日 + 1 个月 = 2009 年 2 月 28 日
2009 年 1 月 31 日 + 1 个月 = 2009 年 2 月 28 日
2009 年 2 月 27 日 + 1 个月 = 2009 年 3 月 27 日
2009 年 2 月 28 日 + 1 个月 = 2009 年 3 月 31 日
2009 年 1 月 31 日 + 50 个月 = 2013 年 3 月 31 日

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

清醇 2024-07-18 05:27:10

您可以使用 Boost.GregorianDate 来实现此目的。

更具体地说,通过添加正确的 date_duration,然后使用 end_of_month_day() 来自日期算法

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

葬花如无物 2024-07-18 05:27:10

time_t转换为struct tm,将X添加到月份,添加月份> 12年换算回来。 tm.tm_mon 是一个 int,添加 32000+ 个月应该不是问题。

[编辑] 一旦遇到更困难的情况,例如将 12 个月添加到 29/02/2008,您可能会发现匹配 Oracle 很棘手。 01/03/2009 和 28/02/2008 都是合理的。

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.

绝影如岚 2024-07-18 05:27:10

真正真正老问题的新答案!

使用这个免费开源库和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 伪代码之间的显着相似之处:

2009年1月30日 + 1个月 = 2009年2月28日
2009 年 1 月 31 日 + 1 个月 = 2009 年 2 月 28 日
2009 年 2 月 27 日 + 1 个月 = 2009 年 3 月 27 日
2009 年 2 月 28 日 + 1 个月 = 2009 年 3 月 31 日
2009 年 1 月 31 日 + 50 个月 = 2013 年 3 月 31 日

另请注意,编译时信息输入会导致编译时信息输出

C++20 更新:

您的 C++ 供应商开始提供 C++20 ,无需第三方库即可完成此操作相同的语法。

另请注意 OP 添加月份所需的有些不寻常的规则,这可以在 中轻松实现:

如果结果月份溢出日期字段如果输入月份是该月的最后一天,则将结果捕捉到该月的月底。

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

演示。

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.

C++20 <chrono> update:

Your C++ vendors are starting to ship C++20 <chrono> which can do this without a 3rd party library with nearly identical syntax.

Also note the somewhat unusual rules the OP requires for adding months, which is easily implementable in <chrono>:

If the resultant month overflows the day field or if the input month is the last day of the month, then snap the result to the end of the month.

#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.

回忆那么伤 2024-07-18 05:27:10

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

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);
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文