Gle*_*len 6 c c++ date time-t date-math
我有一些代码使用Oracle函数add_months将Date增加X个月.
我现在需要在C/C++函数中重新实现相同的逻辑.由于我不想/需要进入的原因,我不能简单地向oracle发出查询以获取新日期.
有没有人知道在time_t中添加X个月的简单可靠的方法?计算类型的一些示例如下所示.
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
你可以使用Boost.GregorianDate.
更具体地说,通过添加正确的确定月份date_duration,然后使用end_of_month_day()日期算法
对一个非常老的问题的真正新答案!
使用这个免费开源库和 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, "");
}
Run Code Online (Sandbox Code Playgroud)
它可以编译。
请注意实际代码和 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
另请注意,编译时信息in会导致编译时信息out。
<chrono>更新:您的 C++ 供应商开始发布 C++20 <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);
}
Run Code Online (Sandbox Code Playgroud)
转换time_t为struct tm,将X添加到月份,添加月份> 12到年,转换回来.tm.tm_mon是一个int,添加32000+个月应该不是问题.
[edit]你可能会发现,一旦你遇到更难的情况,匹配甲骨文就很棘手,比如在2002年2月29日增加12个月.2009年1月3日和28/02/2008都是合理的.
方法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);
}
Run Code Online (Sandbox Code Playgroud)