C++20: Abfrage von Kalenderdaten und Ordinaldaten

Die erweiterte Chrono-Bibliothek in C++20 macht es relativ einfach, die Zeitdauer zwischen Kalenderdaten abzufragen.​

In Pocket speichern vorlesen Druckansicht

(Bild: Kwangmoozaa/Shutterstock.com)

Lesezeit: 4 Min.
Von
  • Rainer Grimm
Inhaltsverzeichnis

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

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++.

Das folgende Programm queryCalendarDates.cpp fragt kurzerhand ein paar Kalenderdaten ab.

// queryCalendarDates.cpp

#include <chrono>
#include <iostream>

int main() {

    std::cout << '\n';

    using std::chrono::floor;

    using std::chrono::January;

    using std::chrono::years;
    using std::chrono::days;
    using std::chrono::hours;

    using std::chrono::year_month_day;
    using std::chrono::year_month_weekday;

    using std::chrono::sys_days;

    auto now = std::chrono::system_clock::now(); 
    std::cout << "The current time is: " << now << " UTC\n"; // (1)
    std::cout << "The current date is: " 
              << floor<days>(now) << '\n';
    std::cout << "The current date is: " 
              << year_month_day{floor<days>(now)} 
              << '\n';
    std::cout << "The current date is: "
              << year_month_weekday{floor<days>(now)} 
              << '\n';

    std::cout << '\n';

    
    auto currentDate = year_month_day(floor<days>(now));    // (2)
    auto currentYear = currentDate.year();
    std::cout << "The current year is " << currentYear << '\n';
    auto currentMonth = currentDate.month();
    std::cout << "The current month is " << currentMonth << '\n';
    auto currentDay = currentDate.day();
    std::cout << "The current day is " << currentDay << '\n'; 

    std::cout << '\n';
        
    auto hAfter = floor<hours>(now) -
                  sys_days(January/1/currentYear);          // (3)
    std::cout << "It has been " << hAfter 
              << " since New Year!\n";  
    auto nextYear = currentDate.year() + years(1); 
    auto nextNewYear = sys_days(January/1/nextYear);
    auto hBefore =  sys_days(January/1/nextYear) - 
                    floor<hours>(now);                      // (4)
    std::cout << "It is " << hBefore << " before New Year!\n";

    std::cout << '\n';
        
    std::cout << "It has been " << floor<days>(hAfter) 
                                << " since New Year!\n";    // (5)
    std::cout << "It is " << floor<days>(hBefore) 
                          << " before New Year!\n";         // (6)
    
    std::cout << '\n';
    
}

Mit der C++20-Erweiterung lässt sich ein Zeitpunkt direkt anzeigen, beispielsweise jetzt (1). std::chrono::floor rundet den Zeitpunkt auf einen Tag std::chrono::sys_days ab. Dieser Wert kann zur Initialisierung des Datentyps std::chrono::year_month_day verwendet werden. Wenn ich den Wert schließlich in einen std::chrono::year_month_weekday-Kalendertyp eintrage, erhalte ich die Antwort, dass dieser bestimmte Tag der dritte Dienstag im Oktober ist.

Natürlich kann ich ein Kalenderdatum auch nach seinen Bestandteilen wie dem aktuellen Jahr, Monat oder Tag (2) fragen.

(3) ist die interessanteste Zeile: Wenn ich vom aktuellen Datum mit Stundenauflösung den ersten Januar des laufenden Jahres abziehe, erhalte ich die Anzahl der Stunden seit dem Jahreswechsel. Wenn ich umgekehrt vom ersten Januar des nächsten Jahres (4) das aktuelle Datum abziehe, erhalte ich die Stunden bis zum neuen Jahr mit der Stundenauflösung. Wer die Stundenauflösung nicht mag: (5) und (6) zeigen die Werte in Tagesauflösung an.

Jetzt möchte ich die Wochentage meiner Geburtstage wissen.

Dank der erweiterten Chrono-Bibliothek ist es ziemlich einfach, den Wochentag eines bestimmten Kalenderdatums abzufragen.

// weekdaysOfBirthdays.cpp

#include <chrono>
#include <cstdlib>
#include <iostream>

int main() {

    std::cout << '\n';

    int y;
    int m;
    int d;

    std::cout << "Year: ";                             // (1)
    std::cin >> y;
    std::cout << "Month: ";
    std::cin >> m;
    std::cout << "Day: ";
    std::cin >> d;

    std::cout << '\n';

    auto birthday = std::chrono::year(y)/
                    std::chrono::month(m)/
                    std::chrono::day(d);               // (2)

    if (not birthday.ok()) {                           // (3)
        std::cout << birthday << '\n';
        std::exit(EXIT_FAILURE);
    }

    std::cout << "Birthday: " << birthday << '\n';
    auto birthdayWeekday = 
      std::chrono::year_month_weekday(birthday);       // (4)
    std::cout << "Weekday of birthday: " 
              << birthdayWeekday.weekday() << '\n';

    auto currentDate = std::chrono::year_month_day(
        std::chrono::floor<std::chrono::days>
          (std::chrono::system_clock::now()));  
    auto currentYear = currentDate.year();
    
    auto age = static_cast<int>(currentDate.year()) - 
               static_cast<int>(birthday.year());      // (5)
    std::cout << "Your age: " << age << '\n';

    std::cout << '\n';

    std::cout << "Weekdays for your next 10 birthdays" 
              << '\n';  

    for (int i = 1, newYear = (int)currentYear; 
         i <= 10;  ++i ) {                             // (6)
        std::cout << "  Age " <<  ++age << '\n';
        auto newBirthday = std::chrono::year(++newYear)/
                           std::chrono::month(m)/
                           std::chrono::day(d);
        std::cout << "    Birthday: " << newBirthday 
                  << '\n';
        std::cout << "    Weekday of birthday: " 
          << std::chrono::year_month_weekday(newBirthday).weekday()
          << '\n';
    }

    std::cout << '\n';

}

Zuerst fragt das Programm nach dem Jahr, dem Monat und dem Tag des Geburtstags (1). Anhand der Eingaben wird ein Kalenderdatum erstellt (2) und auf seine Gültigkeit überprüft (3). Jetzt zeige ich den Wochentag des Geburtstags an. Ich benutze das Kalenderdatum, um den Kalendertyp std::chrono::year_month_weekday auszufüllen (4). Um die int-Darstellung des Datentyps Jahr zu erhalten, muss ich ihn in int konvertieren (5). Jetzt kann ich das Alter anzeigen. Schließlich zeigt die for-Schleife für jeden der nächsten zehn Geburtstage (6) die folgenden Informationen an: das Alter, das Kalenderdatum und den Wochentag. Ich muss die Variablen age und newYear lediglich inkrementieren.

Hier ist eine Ausführung des Programms mit meinem Geburtstag.

Als letztes Beispiel für die neue Kalenderfunktion möchte ich die Online-Ressource Examples and Recipes von Howard Hinnant vorstellen, die etwa 40 Beispiele für die neue Chrono-Funktionalität enthält. Vermutlich ist die Chrono-Erweiterung in C++20 nicht einfach zu verstehen; deshalb ist es wichtig, so viele Beispiele zu haben. Es empfiehlt sich, diese Beispiele als Ausgangspunkt für weitere Experimente zu nutzen und so das Verständnis dafür zu schärfen. Man kann auch seine eigenen Rezepte hinzufügen.

Um einen Eindruck von den Beispielen und Rezepten zu bekommen, möchte ich ein leicht abgewandeltes Programm von Roland Bock vorstellen, das Ordinaldaten berechnet.

“An ordinal date consists of a year and a day of year (1st of January being day 1, 31st of December being day 365 or day 366). The year can be obtained directly from year_month_day. And calculating the day is wonderfully easy. In the code below, we make us of the fact that year_month_day can deal with invalid dates like the 0th of January:” (Roland Bock)

Ich habe die notwendigen Header in Rolands Programm eingefügt.

// ordinalDate.cpp

#include "date.h"
#include <iomanip>
#include <iostream>

int main()
{
   using namespace date;
   
   const auto time = std::chrono::system_clock::now();
   const auto daypoint = floor<days>(time);                 // (1)
   const auto ymd = year_month_day{daypoint};  
   
   // calculating the year and the day of the year
   const auto year = ymd.year();
   const auto year_day = daypoint - 
     sys_days{year/January/0};                              // (2)
                                                            // (3)
   std::cout << year << '-' << std::setfill('0') 
             << std::setw(3) << year_day.count() 
             << std::endl;
   
   // inverse calculation and check
   assert(ymd == year_month_day{sys_days{year/January/0} + 
                                year_day});
}

Ich möchte dem Programm noch ein paar Anmerkungen hinzufügen. (1) schneidet den aktuellen Zeitpunkt ab. Der Wert wird in der folgenden Zeile verwendet, um ein Kalenderdatum zu initialisieren. (2) berechnet die Zeitdauer zwischen den beiden Zeitpunkten. Beide Zeitpunkte nutzen den Tag als Auflösungs. Schließlich gibt year_day.count() (3) die Zeitdauer in Tagen zurück.

Eine wichtige Komponente in meinen Artikeln zur erweiterten Chrono-Bibliothek in C++20 fehlt noch: Zeitzonen. (rme)