Boost.DateTime unterstützt von Haus aus lediglich Kalenderdaten basierend auf dem Gregorianischen Kalender. Das ist in der Praxis insofern kein Problem, als dass der Gregorianische Kalender der heute weltweit am weitesten verbreitete Kalender ist. So können Sie davon ausgehen, dass Sie, wenn Sie sich mit jemandem zum Beispiel für den 12. Mai 2014 verabreden, ihm nicht zusätzlich mitteilen müssen, dass sich das Datum auf den Gregorianischen Kalender bezieht.
Der Gregorianische Kalender wurde von Papst Gregor XIII. im Jahr 1582 eingeführt. Das heißt, dass erst seit diesem Jahr mit dem Gregorianischen Kalender gerechnet wird. Das ist insofern wichtig, als dass Boost.DateTime wie erwähnt ausschließlich den Gregorianischen Kalender unterstützt. Genaugenommen werden Datumsangaben für die Jahre 1400 bis 9999 unterstützt. Die Unterstützung reicht demnach über das Jahr 1582 hinaus bis zurück ins Jahr 1400. Falls Sie mit Datumsangaben vor 1582 arbeiten, können Sie Boost.DateTime verwenden, wenn Sie die Datumsangaben in den Gregorianischen Kalender umrechnen und nur bis ins Jahr 1400 zurückgehen müssen. Es besteht jedoch auch die Möglichkeit, Boost.DateTime um einen neuen Kalendar zu erweitern.
Der Namensraum, in dem Boost.DateTime Klassen und Funktionen zur Verarbeitung von Kalenderdaten zur Verfügung stellt, ist boost::gregorian
. Sie können alle Klassen und Funktionen aus diesem Namensraum einsetzen, wenn Sie die Headerdatei boost/date_time/gregorian/gregorian.hpp
einbinden. So können Sie dann auf die Klasse boost::gregorian::date
zugreifen, um ein Datum zu erstellen.
boost::gregorian::date
erstellen#include <boost/date_time/gregorian/gregorian.hpp>
#include <iostream>
int main()
{
boost::gregorian::date d{2014, 1, 31};
std::cout << d.year() << '\n';
std::cout << d.month() << '\n';
std::cout << d.day() << '\n';
std::cout << d.day_of_week() << '\n';
std::cout << d.end_of_month() << '\n';
}
Die Klasse boost::gregorian::date
bietet mehrere Konstruktoren an, um ein Datum zu erstellen. Dem einfachsten Konstruktor wird ein Jahr, Monat und Tag übergeben. Wird ein ungültiger Wert angegeben, wird eine Ausnahme vom Typ boost::gregorian::bad_year
, boost::gregorian::bad_month
oder boost::gregorian::bad_day_of_month
geworfen, die alle von std::out_of_range
abgeleitet sind.
Wie Sie am Beispiel 36.1 sehen, stehen zahlreiche Methoden zur Verfügung, um auf ein Datum zuzugreifen. Während über Methoden wie year()
, month()
und day()
auf die Werte zugegriffen werden kann, mit denen das Objekt initialisiert wurde, können mit Methoden wie day_of_week()
und end_of_month()
Werte erhalten werden, die selbst zu errechnen aufwändig wäre.
Während Sie dem Konstruktor von boost::gregorian::date
wie im Beispiel 36.1 Zahlen übergeben können, um ein Datum zu setzen, wird beim Aufruf von month()
Jan
und beim Aufruf von day_of_week()
Fri
ausgegeben. Es handelt sich dabei um Werte vom Typ boost::gregorian::date::month_type
und boost::gregorian::date::day_of_week_type
. Wie Sie später in diesem Kapitel noch sehen werden, bietet Boost.DateTime eine umfangreiche Unterstützung zur formatierten Ein- und Ausgabe an, so dass Sie zum Beispiel die Ausgabe so anpassen können, dass anstatt von Jan
der Wert 1
ausgegeben wird.
Beachten Sie, dass der Standardkonstruktor von boost::gregorian::date
ein ungültiges Datum erstellt. Sie können ein derart ungültiges Datum explizit erstellen, wenn Sie boost::date_time::not_a_date_time
als einzigen Parameter an den Konstruktor von boost::gregorian::date
übergeben.
Neben dem direkten Aufruf eines Konstruktors kann ein Objekt vom Typ boost::gregorian::date
auch über freistehende Funktionen und Methoden anderer Objekte erstellt werden.
#include <boost/date_time/gregorian/gregorian.hpp>
#include <iostream>
using namespace boost::gregorian;
int main()
{
date d = day_clock::universal_day();
std::cout << d.year() << '\n';
std::cout << d.month() << '\n';
std::cout << d.day() << '\n';
d = date_from_iso_string("20140131");
std::cout << d.year() << '\n';
std::cout << d.month() << '\n';
std::cout << d.day() << '\n';
}
Im Beispiel 36.2 wird die Klasse boost::gregorian::day_clock
verwendet. Es handelt sich dabei um eine Uhr, die das aktuelle Datum zurückgibt. Die Methode universal_day()
gibt ein UTC-Datum zurück, das unabhängig von Zeitzonen oder Sommerzeit ist. UTC ist die internationale Abkürzung für die Weltzeit, der Mitteleuropa eine Stunde voraus ist. Neben universal_day()
bietet boost::gregorian::day_clock
auch eine Methode local_day()
an, die die Systemeinstellungen bezüglich Zeitzone und Sommerzeit berücksichtigt. Sie verwenden local_day()
, wenn Sie ein aktuelles Datum in der Zeitzone erhalten wollen, in der Sie sich befinden.
Im Namensraum boost::gregorian
stehen außerdem zahlreiche freistehende Funktionen zur Verfügung, um ein Datum in einem String in ein Objekt vom Typ boost::gregorian::date
umzuwandeln. So wird im Beispiel 36.2 mit der Funktion boost::gregorian::date_from_iso_string()
ein Datum im ISO 8601-Format umgewandelt. Boost.DateTime bietet weitere freistehende Funktionen an wie boost::gregorian::from_simple_string()
und boost::gregorian::from_us_string()
.
Während Sie mit boost::gregorian::date
eine Klasse kennengelernt haben, die als Datum einen Zeitpunkt markiert, bietet Boost.DateTime mit boost::gregorian::date_duration
eine Klasse für einen Zeitraum an.
boost::gregorian::date_duration
erstellen#include <boost/date_time/gregorian/gregorian.hpp>
#include <iostream>
using namespace boost::gregorian;
int main()
{
date d1{2014, 1, 31};
date d2{2014, 2, 28};
date_duration dd = d2 - d1;
std::cout << dd.days() << '\n';
}
Da die Klasse boost::gregorian::date
den Operator operator-
überlädt, können wie im Beispiel 36.3 zwei Zeitpunkte mit dem Minuszeichen verknüpft werden. Der Rückgabewert hat den Typ boost::gregorian::date_duration
und entspricht dem Zeitraum zwischen den beiden Daten.
Die wichtigste Methode, die boost::gregorian::date_duration
anbietet, ist days()
. Sie gibt die Anzahl der Tage zurück, aus denen der Zeitraum besteht.
#include <boost/date_time/gregorian/gregorian.hpp>
#include <iostream>
using namespace boost::gregorian;
int main()
{
date_duration dd{4};
std::cout << dd.days() << '\n';
weeks ws{4};
std::cout << ws.days() << '\n';
months ms{4};
std::cout << ms.number_of_months() << '\n';
years ys{4};
std::cout << ys.number_of_years() << '\n';
}
Sie können ein Objekt vom Typ boost::gregorian::date_duration
auch selbst erstellen. Sie übergeben in diesem Fall dem Konstruktor als einzigen Parameter die Anzahl der Tage. Wenn Sie einen Zeitraum erstellen wollen, der Wochen, Monate oder Jahre umfasst, können Sie wie im Beispiel 36.4 auf die Klassen boost::gregorian::weeks
, boost::gregorian::months
und boost::gregorian::years
zugreifen.
Die Klassen boost::gregorian::months
und boost::gregorian::years
bieten keine Methoden an, um die Anzahl der Tage zu ermitteln – immerhin können Monate und Jahre unterschiedlich lang sein. Warum es dennoch sinnvoll sein kann, diese Klassen zu verwenden, sehen Sie am Beispiel 36.5.
#include <boost/date_time/gregorian/gregorian.hpp>
#include <iostream>
using namespace boost::gregorian;
int main()
{
date d{2014, 1, 31};
months ms{1};
date d2 = d + ms;
std::cout << d2 << '\n';
date d3 = d2 - ms;
std::cout << d3 << '\n';
}
Im Beispiel 36.5 wird zum 31. Januar 2014 ein Monat hinzuaddiert. Boost.DateTime errechnet für d2 daraufhin den 28. Februar 2014. Im nächsten Schritt wird von diesem Datum ein Monat abgezogen, woraufhin für d3 wieder der 31. Januar 2014 errechnet wird. Wie Sie sehen, lassen sich mit Zeitpunkten und -räumen Berechnungen anstellen. Sie müssen jedoch auf Besonderheiten achten wie die, dass Sie ausgehend vom letzten Tag eines Monats immer zum letzten Tag eines anderen Monats gelangen, wenn Sie mit boost::gregorian::months
vorwärts- oder zurückspringen. Das kann zu unerwarteten Ergebnissen führen.
#include <boost/date_time/gregorian/gregorian.hpp>
#include <iostream>
using namespace boost::gregorian;
int main()
{
date d{2014, 1, 30};
months ms{1};
date d2 = d + ms;
std::cout << d2 << '\n';
date d3 = d2 - ms;
std::cout << d3 << '\n';
}
Beispiel 36.6 ähnelt dem vorherigen. Der einzige Unterschied ist, dass die Variable d mit dem 30. Januar 2014 initialisiert wurde. Obwohl es sich hierbei nicht um den letzten Tag im Januar handelt, wird nach dem Sprung vorwärts für d2 wieder der 28. Februar 2014 errechnet – schließlich gibt es keinen 30. Februar. Wenn im letzten Schritt ein Monat zurückgesprungen wird, wird für d3 jedoch der 31. Januar 2014 errechnet. Schließlich ist der 28. Februar 2014 der letzte Tag in diesem Monat, so dass Sie bei einem Sprung zurück zum letzten Tag im Januar gelangen.
Wenn Sie das für verwirrend halten, können Sie die Definition des Makros BOOST_DATE_TIME_OPTIONAL_GREGORIAN_TYPES
aufheben. Sie können dann die Klassen boost::gregorian::weeks
, boost::gregorian::months
und boost::gregorian::years
nicht verwenden. Da dann lediglich die Klasse boost::gregorian::date_duration
zur Verfügung steht, mit der eine bestimmte Anzahl an Tagen vorwärts oder rückwärts gezählt wird, kann es nicht mehr zu möglicherweise unerwarteten Ergebnissen kommen.
boost::gregorian::date_period
erstellen#include <boost/date_time/gregorian/gregorian.hpp>
#include <iostream>
using namespace boost::gregorian;
int main()
{
date d1{2014, 1, 1};
date d2{2014, 2, 28};
date_period dp{d1, d2};
date_duration dd = dp.length();
std::cout << dd.days() << '\n';
}
Während Sie mit boost::gregorian::date_duration
eine Klasse kennengelernt haben, die einen beliebigen Zeitraum umfasst, können Sie mit boost::gregorian::date_period
ein Zeitintervall angeben, das an einem bestimmten Datum beginnt und an einem bestimmten Datum endet.
Dem Konstruktor von boost::gregorian::date_period
werden wie im Beispiel 36.7 zwei Parameter vom Typ boost::gregorian::date
übergeben, die den Anfangs- und Endzeitpunkt bestimmen. Alternativ kann auch ein Anfangszeitpunkt und ein Zeitraum vom Typ boost::gregorian::date_duration
angegeben werden. Beachten Sie, dass in beiden Fällen der Endzeitpunkt nicht zum Zeitintervall zählt, sondern der Tag vor dem Endzeitpunkt der letzte Tag im Zeitintervall ist. Das ist wichtig, um zu verstehen, welche Ergebnisse Beispiel 36.8 ausgibt.
#include <boost/date_time/gregorian/gregorian.hpp>
#include <iostream>
using namespace boost::gregorian;
int main()
{
date d1{2014, 1, 1};
date d2{2014, 2, 28};
date_period dp{d1, d2};
std::cout.setf(std::ios::boolalpha);
std::cout << dp.contains(d1) << '\n';
std::cout << dp.contains(d2) << '\n';
}
Im Beispiel 36.8 wird mit Hilfe der Methode contains()
getestet, ob ein bestimmes Datum innerhalb des Zeitintervalls liegt. Obwohl dem Konstruktor von boost::gregorian::date_period
die beiden Variablen d1 und d2 übergeben wurden, gibt contains()
lediglich beim ersten Aufruf true
zurück. Weil der Endzeitpunkt nicht zum Zeitintervall zählt, wird beim Aufruf von contains()
mit d2 als Parameter false
zurückgegeben.
Die Klasse boost::gregorian::date_period
bietet weitere Methoden an, um zum Beispiel ein Zeitintervall zu verschieben oder die Schnittmenge zweier sich überschneidender Zeitintervalle zu ermitteln.
Neben Klassen für Daten, Zeiträume und Zeitintervalle bietet Boost.DateTime Iteratoren und verschiedene nützliche freistehende Funktionen an. Sehen Sie sich dazu Beispiel 36.9 an.
#include <boost/date_time/gregorian/gregorian.hpp>
#include <iostream>
using namespace boost;
int main()
{
gregorian::date d{2014, 5, 12};
gregorian::day_iterator it{d};
std::cout << *++it << '\n';
std::cout << date_time::next_weekday(*it,
gregorian::greg_weekday(date_time::Friday)) << '\n';
}
Der Iterator boost::gregorian::day_iterator
ermöglicht es, von einem bestimmten Datum ausgehend schrittweise einen Tag vorwärts oder rückwärts zu zählen. Neben diesem Iterator werden auch boost::gregorian::week_iterator
, boost::gregorian::month_iterator
und boost::gregorian::year_iterator
angeboten, die jeweils Wochen, Monate oder Jahre vor- oder zurückspringen.
Im Beispiel 36.9 wird außerdem die Funktion boost::date_time::next_weekday()
verwendet, die das Datum des nächsten Wochentags ausgehend von einem Zeitpunkt zurückgibt. So gibt Beispiel 36.9 2014-May-16
aus, weil dies das Datum für den ersten Freitag nach dem 13. Mai 2014 ist.
Entwickeln Sie ein Programm, das die Wochentage für den nächsten 24. Dezember und die beiden darauf folgenden Feiertage ausgibt.
Berechnen Sie Ihr Alter in Tagen. Ihr Programm soll dabei automatisch das aktuelle Datum ermitteln und zur Berechnung verwenden.