einfache Möglichkeit, 1 Monat bis zu einem time_t in C / C ++ hinzufügen
Frage
Ich habe einige Code, der die Oracle-Funktion ADD_MONTHS verwendet ein Datum von X Anzahl der Monate zu erhöhen.
Ich brauche jetzt die gleiche Logik in einer C / C ++ Funktion neu zu implementieren. Aus Gründen möchte ich nicht / muss gehen in ich nicht einfach eine Abfrage an Oracle ausgeben kann das neue Datum zu erhalten.
Kennt jemand eine einfache und zuverlässige Art und Weise X Anzahl von Monaten zu einem time_t hinzuzufügen? Einige Beispiele für die Arten von Berechnungen weiter unten.
30.01.2009 + 1 Monat = 28.02.2009
31.01.2009 + 1 Monat = 28.02.2009
27.02.2009 + 1 Monat = 27.03.2009
28.02.2009 + 1 Monat = 31.03.2009
31.01.2009 + 50 Monate = 31.03.2013
Lösung
Methode AddMonths_OracleStyle das tut, was Sie brauchen.
Vielleicht möchten Sie IsLeapYear und GetDaysInMonth einigen Bibliothekar Methoden ersetzen.
#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);
}
Andere Tipps
Sie können mit Boost.GregorianDate hierfür.
Insbesondere bestimmen den Monat, indem die korrekte Zugabe von date_duration
, und verwenden Sie dann end_of_month_day()
von dem Zeitpunkt Algorithmen
Konvertieren time_t
struct tm
, fügen X zu Monat, fügen Monate> 12 Jahre zurück zu konvertieren. tm.tm_mon ist ein int, das Hinzufügen 32000+ Monate sollte kein Problem sein.
[Bearbeiten] Vielleicht finden Sie, dass Oracle passende heikel ist, wenn Sie auf die härteren Fälle zu bekommen, wie das Hinzufügen von 12 Monaten 29.02.2008. Sowohl 01.03.2009 und 28.02.2008 sind angemessen.
wirklich neue Antwort auf eine wirklich alte Frage!
Mit dieser freien und Open-Source-Bibliothek und ein C ++ 14-Compiler (wie Klappern) das kann ich jetzt schreiben:
#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, "");
}
Und es kompiliert wird.
Beachten Sie die bemerkenswerte Ähnlichkeit zwischen dem Ist-Code und die der OP Pseudo-Code:
30.01.2009 + 1 Monat = 28.02.2009
31.01.2009 + 1 Monat = 28.02.2009
27.02.2009 + 1 Monat = 27.03.2009
28.02.2009 + 1 Monat = 31.03.2009
31.01.2009 + 50 Monate = 31.03.2013
Beachten Sie auch, dass die Compiler-Informationen in führt Informationen zu Zeit zu kompilieren aus .