Zeit in C++20: Kalendertermine darstellen und prüfen

C++20 bietet neben den Funktionen zum Erstellen und Berechnen von Kalenderdaten auch Methoden, um die Daten anzuzeigen und zu überprüfen.

In Pocket speichern vorlesen Druckansicht 1 Kommentar lesen

(Bild: Kwangmoozaa/Shutterstock.com)

Lesezeit: 3 Min.
Von
  • Rainer Grimm
Inhaltsverzeichnis

In meinem letzten Artikel, "Zeit in C++20: Kalendertermine erstellen", habe ich Kalenderdaten erstellt, die ich heute anzeigen und überprüfen werde.

Modernes C++ – Rainer Grimm

Rainer Grimm ist seit vielen Jahren als Softwarearchitekt, Team- und Schulungsleiter tätig. Er schreibt gerne Artikel zu den Programmiersprachen C++, Python und Haskell, spricht aber auch gerne und häufig auf Fachkonferenzen. Auf seinem Blog Modernes C++ beschäftigt er sich intensiv mit seiner Leidenschaft C++.

Dieser Artikel ist der fünfte Beitrag meiner ausführlichen Reise durch die Chrono-Erweiterung in C++20:

Dank std::chrono::local_days oder std::chrono::sys_days kann man Kalenderdaten in einen lokalen oder einen System-std::chrono::time_point umwandeln. Ich verwende std::chrono::sys_days in meinem Beispiel. std::chrono::sys_days basiert auf std::chrono::system_clock.

// sysDays.cpp

#include <chrono>
#include <iostream>
 
int main() {

    std::cout << '\n';

    using std::chrono::last;

    using std::chrono::year;
    using std::chrono::sys_days;

    using std::chrono::March;
    using std::chrono::February;

    using std::chrono::Monday;
    using std::chrono::Thursday;

    constexpr auto yearMonthDayLast{year(2010)/March/last};  // (1)
    std::cout << "sys_days(yearMonthDayLast): " 
              << sys_days(yearMonthDayLast)  <<  '\n';

    constexpr auto yearMonthWeekday{year(2020)/March/Thursday[2]};
    std::cout << "sys_days(yearMonthWeekday): " 
              <<  sys_days(yearMonthWeekday) << '\n';

    constexpr  
      auto yearMonthWeekdayLast{year(2010)/March/Monday[last]};
    std::cout << "sys_days(yearMonthWeekdayLast): " 
              << sys_days(yearMonthWeekdayLast) << '\n';

    std::cout << '\n';

    constexpr auto leapDate{year(2012)/February/last};               
    std::cout << "sys_days(leapDate): " 
              << sys_days(leapDate) << '\n';                 // (2)

    constexpr auto noLeapDate{year(2013)/February/last};             
    std::cout << "sys_day(noLeapDate): " 
              << sys_days(noLeapDate) << '\n';               // (3)

    std::cout << '\n';

}  

Mit der Konstante std::chrono::last (1) kann ich schnell feststellen, wie viele Tage ein Monat hat. Die Ausgabe zeigt, dass 2012 ein Schaltjahr ist (2), aber 2013 keins.

Für ein Kalenderdatum wie year(2100)/2/29 könnte die erste Frage lauten: Ist dieses Datum gültig?

Die verschiedenen Kalendertypen in C++20 haben die Funktion ok. Diese Funktion gibt true zurück, wenn das Datum gültig ist.

//  leapYear.cpp

#include <chrono>
#include <iostream>
 
int main() {

    std::cout << std::boolalpha << '\n';

    std::cout << "Valid days" << '\n';                      // (1)
    std::chrono::day day31(31);
    std::chrono::day day32 = day31 + std::chrono::days(1);
    std::cout << "  day31: " << day31 << "; ";
    std::cout << "day31.ok(): "  << day31.ok() << '\n';
    std::cout << "  day32: " << day32 << "; ";
    std::cout << "day32.ok(): "  << day32.ok() << '\n';
   

    std::cout << '\n';

    std::cout << "Valid months" << '\n';                   // (2)
    std::chrono::month month1(1);
    std::chrono::month month0(0);
    std::cout << "  month1: " << month1 << "; ";
    std::cout << "month1.ok(): "  << month1.ok() << '\n';
    std::cout << "  month0: " << month0 << "; ";
    std::cout << "month0.ok(): "  << month0.ok() << '\n';

    std::cout << '\n';

    std::cout << "Valid years" << '\n';                    // (3)
    std::chrono::year year2020(2020);
    std::chrono::year year32768(-32768);
    std::cout << "  year2020: " << year2020 << "; ";
    std::cout << "year2020.ok(): "  << year2020.ok() << '\n';
    std::cout << "  year32768: " << year32768 << "; ";
    std::cout << "year32768.ok(): "  << year32768.ok() << '\n';

    std::cout << '\n';

    std::cout << "Leap Years"  << '\n';       

    constexpr auto leapYear2016{std::chrono::year(2016)/2/29};
    constexpr auto leapYear2020{std::chrono::year(2020)/2/29};
    constexpr auto leapYear2024{std::chrono::year(2024)/2/29};

    std::cout << "  leapYear2016.ok(): " 
              << leapYear2016.ok() << '\n';
    std::cout << "  leapYear2020.ok(): " 
              << leapYear2020.ok() << '\n';
    std::cout << "  leapYear2024.ok(): " 
              << leapYear2024.ok() << '\n';

    std::cout << '\n';

     std::cout << "No Leap Years"  << '\n';   

    constexpr auto leapYear2100{std::chrono::year(2100)/2/29};
    constexpr auto leapYear2200{std::chrono::year(2200)/2/29};
    constexpr auto leapYear2300{std::chrono::year(2300)/2/29};

    std::cout << "  leapYear2100.ok(): " 
              << leapYear2100.ok() << '\n';
    std::cout << "  leapYear2200.ok(): " 
              << leapYear2200.ok() << '\n';
    std::cout << "  leapYear2300.ok(): "
              << leapYear2300.ok() << '\n';

    std::cout << '\n';

    std::cout << "Leap Years"  << '\n';      

    constexpr auto leapYear2000{std::chrono::year(2000)/2/29};
    constexpr auto leapYear2400{std::chrono::year(2400)/2/29};
    constexpr auto leapYear2800{std::chrono::year(2800)/2/29};

    std::cout << "  leapYear2000.ok(): " 
              << leapYear2000.ok() << '\n';
    std::cout << "  leapYear2400.ok(): " 
              << leapYear2400.ok() << '\n';
    std::cout << "  leapYear2800.ok(): " 
              << leapYear2800.ok() << '\n';

    std::cout << '\n';

}

Ich prüfe in dem Programm, ob ein bestimmter Tag (1), ein bestimmter Monat (2) oder ein bestimmtes Jahr (3) gültig ist. Der Bereich für einen Tag ist [1, 31], für einen Monat [1, 12] und für ein Jahr [ -32767, 32767]. Folglich geben die ok()-Aufrufe für die entsprechenden Werte false zurück. Zwei Fakten sind interessant, wenn ich verschiedene Werte anzeige. Erstens: Wenn der Wert ungültig ist, zeigt die Ausgabe an: “is not a valid day”, “is not a valid month”, “is not a valid year”. Zweitens werden die Werte als String angezeigt.

Der ok-Aufruf lässt sich auf ein Kalenderdatum anwenden. Jetzt ist es ziemlich einfach zu prüfen, ob ein bestimmtes Kalenderdatum ein Schalttag und das entsprechende Jahr ein Schaltjahr ist. Im weltweit verwendeten Gregorianischen Kalender gelten die folgenden Regeln:

Jedes Jahr, das genau durch 4 teilbar ist, ist ein Schaltjahr.

  • Mit Ausnahme der Jahre, die genau durch 100 teilbar sind. Sie sind keine Schaltjahre.
  • Für die Ausnahme gilt wiederum die Ausnahme, dass die Jahre, die genau durch 400 teilbar sind, doch Schaltjahre sind.

Zu kompliziert? Das Programm leapYears.cpp veranschaulicht diese Regel.

Die erweiterte Chrono-Bibliothek macht es relativ einfach, die Zeitdauer zwischen Kalenderdaten abzufragen. (rme)