Kalender und Zeitzonen in C++20: Kalendertag
Ein neuer Datentyp der Kalender- und Zeitzonenerweiterung in C++20 ist der Kalendertag. C++20 bietet viele Möglichkeiten an, Kalendertage zu erzeugen und mit ihnen zu interagieren.
- Rainer Grimm
Ein neuer Datentyp der Kalender- und Zeitzonenerweiterung in C++20 ist der Kalendertag. C++20 bietet viele Möglichkeiten an, Kalendertage zu erzeugen und mit ihnen zu interagieren.
Ich habe einige Zeit benötigt, die nahezu zwanzig neuen Datentypen rund um die Kalenderdaten zu verstehen. Genau aus dem Grund beginne ich den Artikel mit dem Kalendertag. Entsprechend meinem letzten Artikel "Kalender und Zeitzonen in C++20: Tageszeit" verwende ich die date-Bibliothek von Howard Hinnant als Prototyp der neuen chrono-Erweiterung.
Kalendertag
Ein Kalendertag ist ein Tag, der aus einem Jahr, einem Monat und einem Tag besteht. Dementsprechend besitzt C++20 den Datentyp std::chrono_year_month_day
. C++20 bietet aber deutlich mehr an. Die beiden Tabelle geben den ersten Überblick:
Ich möchte mit einem einfachen Programm beginnen. createCalender.cpp
stellt verschiedene Möglichkeiten vor, Kalenderdaten zu erzeugen:
// createCalendar.cpp
#include <iostream>
#include "date.h"
int main() {
std::cout << std::endl;
using namespace date;
constexpr auto yearMonthDay{year(1940)/month(6)/day(26)}; // (1)
std::cout << yearMonthDay << " ";
std::cout << date::year_month_day(1940_y, June, 26_d) << std::endl; // (2)
std::cout << std::endl;
constexpr auto yearMonthDayLast{year(2010)/March/last}; // (3)
std::cout << yearMonthDayLast << " ";
std::cout << date::year_month_day_last(2010_y, month_day_last(month(3))) << std::endl;
constexpr auto yearMonthWeekday{year(2020)/March/Thursday[2]}; // (4)
std::cout << yearMonthWeekday << " ";
std::cout << date::year_month_weekday(2020_y, month(March), Thursday[2]) << std::endl;
constexpr auto yearMonthWeekdayLast{year(2010)/March/Monday[last]}; // (5)
std::cout << yearMonthWeekdayLast << " ";
std::cout << date::year_month_weekday_last(2010_y, month(March), weekday_last(Monday)) << std::endl;
std::cout << std::endl;
constexpr auto day_{day(19)}; // (5)
std::cout << day_ << " ";
std::cout << date::day(19) << std::endl;
constexpr auto month_{month(1)}; // (6)
std::cout << month_ << " ";
std::cout << date::month(1) << std::endl;
constexpr auto year_{year(1988)}; // (7)
std::cout << year_ << " ";
std::cout << date::year(1988) << std::endl;
constexpr auto weekday_{weekday(5)};
std::cout << weekday_ << " ";
std::cout << date::weekday(5) << std::endl;
constexpr auto yearMonth{year(1988)/1};
std::cout << yearMonth << " ";
std::cout << date::year_month(year(1988), January) << std::endl;
constexpr auto monthDay{10/day(22)};
std::cout << monthDay << " ";
std::cout << date::month_day(October, day(22)) << std::endl;
constexpr auto monthDayLast{June/last};
std::cout << monthDayLast << " ";
std::cout << date::month_day_last(month(6)) << std::endl;
constexpr auto monthWeekday{2/Monday[3]};
std::cout << monthWeekday << " ";
std::cout << date::month_weekday(February, Monday[3]) << std::endl;
constexpr auto monthWeekDayLast{June/Sunday[last]};
std::cout << monthWeekDayLast << " ";
std::cout << date::month_weekday_last(June, weekday_last(Sunday)) << std::endl;
std::cout << std::endl;
}
C++20 bietet im Wesentlichen zwei Wege an, ein Kalenderdatum zu erzeugen. Einerseits gibt es die sogenannte Cute-Syntax yearMonthDay{year(1940)/month(6)/day(26)}
(Zeile 1), andererseits lässt sich ein expliziter Datentyp verwenden: date::year_month_day(1940_y, June, 26_d)
(Zeile 2). Ich verschiebe die Erklärung der Cute-Syntax auf den nächsten Abschnitt dieses Artikels und konzentriere mich zuerst auf die expliziten Datentypen. Sie sind sehr praktisch, denn sie können Zeitliterale (1940_y
, 26_d
) und vordefinierte Konstante (June
) verwenden. Mit C++20 werden die Zeitliterale wie 1940_y
und 26_d
ohne Unterstrich zur Verfügung stehen: 1940y
und 26d
. Dies war wohl der eingängige Teil meiner Erklärung.
Die Zeilen (3), (4) und (5) bieten weitere angenehme Möglichkeiten an, Kalenderdaten zu definieren.
- Line (3): der letzte Tag des März 2010: 2010:
{year(2010)/March/last}
oderyear_month_day_last(2010_y, month_day_last(month(3))
- Line (4): der zweite Donnerstag des März 2020:
{year(2020)/March/Thursday[2]}
oderyear_month_weekday(2020_y, month(March), Thursday[2])
- Line (5): der letzte Montag des März 2010:
{year(2010)/March/Monday[last]}
oderyear_month_weekday_last(2010_y, month(March), weekday_last(Monday))
Die verbleibenden Kalenderdaten stehen für einen Tag (Zeile 6), einen Monat (Zeile 7) oder ein Jahr (Zeile 8). Du kannst diese Kalenderdaten als elementare Bausteine verwenden, um Kalendertage wie in den Zeilen (3) oder (4) zu erzeugen.
Bevor ich noch weiter in den Kalenderdaten abtauche, ist hier die Ausgabe des Programms:
Wie angekündigt, schaue ich mir jetzt die Cute-Syntax genauer an.
Cute-Syntax
Die Cute-Syntax besteht aus einem überladenen Teile-Operator, mit dem sich ein Kalendertag spezifizieren lässt. Der überladene Operator unterstützt die Zeitliterale (zum Beispiel 2020_y
und 31_d
) und die folgenden Konstanten: January, February, March, April, May, June, July, August, September, October, November, December.
Die folgenden drei Kombinationen von Jahr, Monat und Tag sind beim Einsatz der Cute-Syntax zulässig.
- Jahr/Monat/Tag
- Tag/Monat/Jahr
- Monat/Tag/Jahr
Diese drei Kombinationen sind aus dem naheliegenden Grund die einzigen, denn sie werden weltweit verwendet. Jede andere Kombination ist nicht zulässig.
Durch den ersten Datentyp der Cute-Syntax werden die zwei verbleibenden Datentypen vorgegeben. Daher ist es für die zwei verbleibenden Datentypen ausreichend, ein Integral zu verwenden:
// cuteSyntax.cpp
#include <iostream>
#include "date.h"
int main() {
std::cout << std::endl;
using namespace date;
constexpr auto yearMonthDay{year(1966)/6/26};
std::cout << yearMonthDay << std::endl;
constexpr auto dayMonthYear{day(26)/6/1966};
std::cout << dayMonthYear << std::endl;
constexpr auto monthDayYear{month(6)/26/1966};
std::cout << monthDayYear << std::endl;
constexpr auto yearDayMonth{year(1966)/month(26)/6}; //(1)
std::cout << yearDayMonth << std::endl;
std::cout << std::endl;
}
Die Kombination year/day/month in Zeile (1) ist nicht zulässig und führt zu einer Laufzeitnachricht.
Ich denke, du willst ein Kalenderdatum wie {year(2010)/March/last}
in einem lesbaren Format wie 2020-03-31 darstellen? Dies ist der Job des local_days
- oder sys_days
-Operators.
Kalenderdatum darstellen
Dank std::chrono::local_days
oder std::chrono::sys_days
lassen sich Kalenderdaten zu einem std::chrono::time_point
konvertieren. Dieser repräsentiert dann dasselbe Datum wie year_month_day
. Ich verwende in meinem Beispiel std::chrono::sys_days. std::chrono::sys_days
basiert auf der std::chrono::system_clock
. Im folgenden Programm sysDays.cpp
konvertiere ich die Kalenderdaten (Zeile (3) bis Zeile (5)) auf dem vorherigen Programm createCalendar.cpp
:
// sysDays.cpp
#include <iostream>
#include "date.h"
int main() {
std::cout << std::endl;
using namespace date;
constexpr auto yearMonthDayLast{year(2010)/March/last};
std::cout << "sys_days(yearMonthDayLast): " << sys_days(yearMonthDayLast) << std::endl;
constexpr auto yearMonthWeekday{year(2020)/March/Thursday[2]};
std::cout << "sys_days(yearMonthWeekday): " << sys_days(yearMonthWeekday) << std::endl;
constexpr auto yearMonthWeekdayLast{year(2010)/March/Monday[last]};
std::cout << "sys_days(yearMonthWeekdayLast): " << sys_days(yearMonthWeekdayLast) << std::endl;
std::cout << std::endl;
constexpr auto leapDate{year(2012)/February/last}; // (1)
std::cout << "sys_days(leapDate): " << sys_days(leapDate) << std::endl;
constexpr auto noLeapDate{year(2013)/February/last}; // (2)
std::cout << "sys_day(noLeapDate): " << sys_days(noLeapDate) << std::endl;
std::cout << std::endl;
}
Dank std::chrono::last
kann ich sehr einfach herausfinden, wie viele Tage ein Monat besitzt. Die Ausgabe zeigt, dass das Jahr 2012 im Gegensatz zum Jahr 2013 ein Schaltjahr war:
Wie geht's weiter?
Das Arbeiten mit Kalenderdaten wird sehr mächtig, wenn sich diese auf ihre Gültigkeit prüfen lassen oder mit Zeitdauern modifiziert werden können.
Neue Online-Seminare
- Embedded-Programmierung mit modernem C++ (Deutsch): 26. bis 28. Januar 2021 (9 bis 17 Uhr CEST)
- C++20 – A Deep Insight (English): 1. bis 3. Februar 2021 (16 bis 20 Uhr UTC)