C / C ++のtime_tに1か月を追加する簡単な方法
質問
Oracle関数add_monthsを使用してXの月数で日付をインクリメントするコードがあります。
C / C ++関数で同じロジックを再実装する必要があります。入りたくない/必要のない理由から、新しい日付を取得するためにOracleにクエリを発行することはできません。
time_tにXか月を追加する簡単で信頼できる方法を知っている人はいますか 計算の種類の例を以下に示します。
30/01/2009 + 1か月= 28/02/2009
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日
解決
メソッド 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);
}
他のヒント
Boost.GregorianDate を使用できますこのため。
具体的には、正しい date_duration
を使用し、 end_of_month_day()
日付アルゴリズムから
time_t
を struct tm
に変換し、Xを月に追加し、月を追加&gt; 12年から数年、元に戻します。 tm.tm_monはintであり、32000か月以上追加しても問題はありません。
[編集] 2008年2月29日に12か月を追加するなど、より困難なケースに到達すると、一致するOracleがトリッキーになることがあります。 2009年1月3日と2008年2月28日の両方が妥当です。
本当に新しい答え!
この無料のオープンソースライブラリ、および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
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日
また、コンパイル時情報 in はコンパイル時情報 out につながることに注意してください。