﻿/* Zadanie dodatkowe ZPR - 2023L
   Autor: Krystian Grela

    Czas w klasycznym tego słowa znaczeniu jest czymś intuicyjnym. Praktycznie każdy potrafi posługiwać się zegarkiem lub kalendarzem.
    Czas w programowaniu jest równie istotny jak życiu codziennym. Praktycznie każdy większy projekt korzysta z obsługi czasu w jednej z kilku form.
    Najpopularniejszymi typami do prezentacji czasu w programowaniu jest Unix Timestamp, czyli ilość sekund, które minęły od 1 stycznie 1970, różne Epoch time,
    które zawierają inne punkty odniesienia oraz na przykład obiekty do obsługi dat np. DateTime.

    Użycie i obsługa czasu i dat w programowaniu potrafi być problematyczne. Elementy czasu, które wydają nam się logiczne jak strefy czasowe,
    w programowaniu powodują rozległe drzewo warunków i sytuacji brzegowych, które należy obsłużyć. Poza tym kalendarz zawiera lata przestępne,
    co więcej, miesiące nie mają równych długości co przy uwzględnianiu tego w kodzie potrafi być kłopotliwe.
    Istnieją różne kalendarze, różne metody podziału czasu, oraz różne formaty dat.
    Bez odpowiedniej biblioteki do obsługi czasu implementacja najmniejszej funkcjonalności wykorzystującej czas może skutkować utworzeniem
    podprojektu, który zajmie wiele godzin pracy.

    W języku C++ z pomocą przychodzi nam biblioteka chrono, wprowadzona w standardzie C++11. Zapewnia ona elementy do pracy z czasem, okresami czasu,
    który jest typowo bezpieczny i ustandaryzowany. Bibliteka, była stopniowo rozbudowywana np. w standardzie C++17 dodano std::chrono::floor,
    które dokonuje konwersji daty z zaokrągleniem. Spore usprawnienia pojawiły się w standardzie C++20,
    kiedy biblioteka została znacznie rozbudowana o kolejne funkcje, które zostały
    zaprezentowane w funkcji main poniżej.

    Nowością w bibliotece Chrono jest usprawniona obsługa kalendarza, i możliwość działania bezpośrednio na nim, co umożliwia nam bardzo łatwą implementację kalendarza do
    naszego projektu. Inną funkcją dodaną w nowym standarcie C++ jest obsługa czasu powiązana w strefach czasowych. Możemy nawet podawać nazwę miejsca
    zdefiniowanego dla danej strefy czasowej. Ułatwienie przychodzi też do obliczeń czasu. Możemy łatwo dostać czas, który upłynął pomiędzy dwoma datami
    sformatowany automatycznie do dni. Poza usprawnieniami w modyfikacji dat ważne jest żeby takie obliczenia były intuicyjne w odczycie dla programisty, co pozwala
    na łatwiejszą analizę kodu. Przykładem jest intuicyjny zapis wartości zmiennej odpowiedzialnej za datę: February / 27 / 2019.

    Reasumując rozwój biblioteki Chrono można dojść do wniosku, że biblioteki do zarządzania czasem znacznie ułatwiają implementację w teorii skomplikowanych
    programistycznie elementów. Każde usprawnienie takiej biblioteki jest pożądane w dowolnym języku programowania. Zmiany te jednak muszą być dobrze zabezpieczone
    i pewne, ponieważ na obsłudze czasu zwykle opiera się najistotniejsza infrastruktura projektu programistycznego.

    Wykorzystane materiały:
    Dokumentacja C++ (Chrono) https://en.cppreference.com/w/cpp/chrono
    Dokumentacja C++ (Pomocnicza biblioteka IoManip) https://en.cppreference.com/w/cpp/io/manip
    Elementy dodane w C++ 20 - https://www.geeksforgeeks.org/features-of-c-20/
    Przykłady z użyciem kalendarza: https://devblogs.microsoft.com/cppblog/cpp20s-extensions-to-chrono-available-in-visual-studio-2019-version-16-10/
    Problem dat w programowaniu: https://levelup.gitconnected.com/why-is-programming-with-dates-so-hard-7477b4aeff4c
 */


#define _CRT_SECURE_NO_WARNINGS

#include <chrono>
#include <iostream>
#include <iomanip>
using namespace std::chrono;
// using namespace użyte, żeby zaprezentować ułatwiony zapis jednostek czasowych. W kodzie std::chrono występuje z racji prezentacji,
// że z tej właśnie biblioteki pochodzi dana metoda lub typ.

int main() {
    // Funkcjonalność chrono występująca w C++ 11.
    std::string timeFormat = "%H:%M:%S %d-%m-%Y";

    std::chrono::system_clock::time_point now = std::chrono::system_clock::now();

    std::time_t UTCTime = std::chrono::system_clock::to_time_t(now);

    std::cout << "Current UTC time: " << std::put_time(std::gmtime(&UTCTime), timeFormat.c_str()) << std::endl;

    std::cout << "Local time: " << std::put_time(std::localtime(&UTCTime), timeFormat.c_str()) << std::endl;

    // Funkcjonalności nowe, dodane w C++ 20.
    const auto& tzdb = std::chrono::get_tzdb();
    const std::chrono::time_zone* englandTimezone = tzdb.locate_zone("Europe/London");
    auto calendarDay = std::chrono::local_days{ std::chrono::year{ 2023 } / std::chrono::month{ 5 } / std::chrono::day{ 24 } };
    std::chrono::zoned_time exampleUpdatedDate{ englandTimezone, calendarDay + std::chrono::days{ 24 } + std::chrono::minutes{19} + 12s };
    // Co ciekawe użycie using namespace std::chrono pozwala nam na zapis dodawania dni do daty w bardzo prosty sposób np: 3h + 44min + 12s
    std::chrono::zoned_time utcExampleUpdatedDate{ "Etc/UTC", exampleUpdatedDate };

    std::cout << "Example Updated Date (England Timezone): " << exampleUpdatedDate << std::endl;

    std::cout << "UTC Example Updated Date: " << utcExampleUpdatedDate << std::endl;

    std::chrono::gps_clock::time_point gpsTime = std::chrono::clock_cast<std::chrono::gps_clock>(now);

    std::chrono::seconds gpsEpochSeconds = std::chrono::time_point_cast<std::chrono::seconds>(gpsTime).time_since_epoch();
    std::cout << "GPS epoch seconds: " << gpsEpochSeconds.count() << std::endl;

    auto currentZone = std::chrono::current_zone();
    std::cout << "Current time zone: " << currentZone->name() << std::endl;

    auto currentTime = std::chrono::system_clock::now();
    std::time_t currentTime_t = std::chrono::system_clock::to_time_t(currentTime);
    std::tm* currentTime_tm = std::gmtime(&currentTime_t);
    int dayOfWeek = currentTime_tm->tm_wday;
    const char* daysOfWeek[] = {
    "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"
    };
    std::cout << "Today is " << daysOfWeek[dayOfWeek] << std::endl;

    std::chrono::year_month_day startDate{ std::chrono::year{2023}, std::chrono::month{5}, std::chrono::day{1} };
    std::chrono::year_month_day endDate{ std::chrono::year{2023}, std::chrono::month{5}, std::chrono::day{25} };

    auto startLocalTime = std::chrono::floor<std::chrono::days>(std::chrono::sys_days{ startDate });
    auto endLocalTime = std::chrono::floor<std::chrono::days>(std::chrono::sys_days{ endDate });


    auto duration = endLocalTime - startLocalTime;

    int numberOfDays = duration.count();

    std::cout << "Number of days between the two dates: " << numberOfDays << std::endl;

    // przykład łatwego zapisu dat kalendarzowych przy użyciu using namespace std::chrono;
    auto date1 = 2020y / September / 8;
    auto date2 = 21d / October / 2018;
    auto date3 = February / 27 / 2019;

    return 0;
}
