طريقة سهلة لإضافة شهر واحد إلى time_t في C/C++
سؤال
لدي بعض التعليمات البرمجية التي تستخدم وظيفة Oracle add_months لزيادة التاريخ بمقدار X عدد الأشهر.
أحتاج الآن إلى إعادة تنفيذ نفس المنطق في وظيفة C/C++.لأسباب لا أريد/أحتاج إلى الخوض فيها، لا يمكنني ببساطة إصدار استعلام إلى Oracle للحصول على التاريخ الجديد.
هل يعرف أحد طريقة بسيطة وموثوقة لإضافة عدد X من الأشهر إلى time_t؟بعض الأمثلة على أنواع الحسابات موضحة أدناه.
30/01/2009 + شهر واحد = 28/02/2009
31/01/2009 + شهر واحد = 28/02/2009
27/02/2009 + شهر واحد = 27/03/2009
28/02/2009 + شهر واحد = 31/03/2009
31/01/2009 + 50 شهر = 31/03/2013
المحلول
طريقة 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 لذلك.
وبشكل أكثر تحديدا، وتحديد شهر بإضافة الصحيح <لأ href = "http://www.boost.org/doc/libs/1_37_0/doc/html/date_time/gregorian.html#date_time.gregorian.date_duration" يختلط = "noreferrer"> date_duration
، ومن ثم استخدام <لأ href = "http://www.boost.org/doc/libs/1_37_0/doc/html/date_time/gregorian.html#date_time.gregorian. date_algorithms "يختلط =" noreferrer "> end_of_month_day()
من خوارزميات تاريخ
وتحويل time_t
إلى struct tm
، إضافة X إلى الشهر، إضافة أشهر> 12 عاما، وتحويل الظهر. tm.tm_mon هو عدد صحيح، مضيفا أن 32000+ أشهر لن يكون مشكلة.
[عدل] قد تجد أن مطابقة أوراكل هي صعبة مرة واحدة تحصل على حالات أصعب، مثل إضافة 12 شهرا حتى 29/02/2008. كلا 01/03/2009 و28/02/2008 معقولة.
حقا م> إجابة جديدة ل<م> حقا م> السؤال القديم!
هذه حرة ومفتوحة المصدر مكتبة ، ومترجم C ++ 14 (مثل رنة) أستطيع أن أكتب الآن هذا:
#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
لاحظ أيضا أن المعلومات تجميع الوقت في م> يؤدي إلى تجميع المعلومات في الوقت <م> من م>.