winget-cli

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

DateTime.cpp (8970B)


      1 // Copyright (c) Microsoft Corporation.
      2 // Licensed under the MIT License.
      3 #include "pch.h"
      4 #include "Public/AppInstallerDateTime.h"
      5 
      6 using namespace std::chrono;
      7 
      8 namespace AppInstaller::Utility
      9 {
     10     namespace
     11     {
     12         struct OutputTimePointContext
     13         {
     14             OutputTimePointContext(std::ostream& stream, const std::chrono::system_clock::time_point& time, TimeFacet facet) :
     15                 Stream(stream), Time(time), Facet(facet)
     16             {
     17                 auto tt = system_clock::to_time_t(time);
     18                 _localtime64_s(&LocalTime, &tt);
     19             }
     20 
     21             std::ostream& Stream;
     22             const std::chrono::system_clock::time_point& Time;
     23             tm LocalTime{};
     24             TimeFacet Facet;
     25         };
     26 
     27         struct OutputTimePointFacetInfo
     28         {
     29             TimeFacet Facet;
     30             char FollowingSeparator;
     31             void (*Action)(const OutputTimePointContext&);
     32         };
     33     }
     34 
     35     void OutputTimePoint(std::ostream& stream, const std::chrono::system_clock::time_point& time, bool useRFC3339)
     36     {
     37         OutputTimePoint(stream, time, TimeFacet::Default | (useRFC3339 ? TimeFacet::RFC3339 : TimeFacet::None));
     38     }
     39 
     40     // If moved to C++20, this can be replaced with standard library implementations.
     41     void OutputTimePoint(std::ostream& stream, const std::chrono::system_clock::time_point& time, TimeFacet facet)
     42     {
     43         OutputTimePointContext context{ stream, time, facet };
     44         using Ctx = const OutputTimePointContext&;
     45 
     46         bool useRFC3339 = WI_IsFlagSet(facet, TimeFacet::RFC3339);
     47         bool filename = WI_IsFlagSet(facet, TimeFacet::Filename);
     48         char day_time_separator = useRFC3339 ? 'T' : (filename ? '-' : ' ');
     49         char time_field_separator = filename ? '-' : ':';
     50 
     51         bool needsSeparator = false;
     52         char currentSeparator = '-';
     53 
     54         for (const auto& info : {
     55             OutputTimePointFacetInfo{ TimeFacet::ShortYear, '-', [](Ctx ctx) { ctx.Stream << (ctx.LocalTime.tm_year - 100); }},
     56             OutputTimePointFacetInfo{ TimeFacet::Year, '-', [](Ctx ctx) { ctx.Stream << (1900 + ctx.LocalTime.tm_year); }},
     57             OutputTimePointFacetInfo{ TimeFacet::Month, '-', [](Ctx ctx) { ctx.Stream << std::setw(2) << std::setfill('0') << (1 + ctx.LocalTime.tm_mon); }},
     58             OutputTimePointFacetInfo{ TimeFacet::Day, day_time_separator, [](Ctx ctx) { ctx.Stream << std::setw(2) << std::setfill('0') << ctx.LocalTime.tm_mday; }},
     59             OutputTimePointFacetInfo{ TimeFacet::Hour, time_field_separator, [](Ctx ctx) { ctx.Stream << std::setw(2) << std::setfill('0') << ctx.LocalTime.tm_hour; }},
     60             OutputTimePointFacetInfo{ TimeFacet::Minute, time_field_separator, [](Ctx ctx) { ctx.Stream << std::setw(2) << std::setfill('0') << ctx.LocalTime.tm_min; }},
     61             OutputTimePointFacetInfo{ TimeFacet::Second, '.', [](Ctx ctx) { ctx.Stream << std::setw(2) << std::setfill('0') << ctx.LocalTime.tm_sec; }},
     62             OutputTimePointFacetInfo{ TimeFacet::Millisecond, '-', [](Ctx ctx)
     63             {
     64                 // Get partial seconds
     65                 auto sinceEpoch = ctx.Time.time_since_epoch();
     66                 auto leftoverMillis = duration_cast<milliseconds>(sinceEpoch) - duration_cast<seconds>(sinceEpoch);
     67 
     68                 ctx.Stream << std::setw(3) << std::setfill('0') << leftoverMillis.count();
     69             }},
     70             OutputTimePointFacetInfo{ TimeFacet::RFC3339, '\0', [](Ctx ctx)
     71             {
     72                 // RFC 3339 requires adding time zone info.
     73                 // No need to bother getting the actual time zone as we don't need it.
     74                 // -00:00 represents an unspecified time zone, not UTC.
     75                 ctx.Stream << "00:00";
     76             }},
     77             })
     78         {
     79             if (WI_AreAllFlagsSet(facet, info.Facet))
     80             {
     81                 if (needsSeparator)
     82                 {
     83                     stream << currentSeparator;
     84                 }
     85 
     86                 info.Action(context);
     87                 needsSeparator = true;
     88             }
     89 
     90             // Getting this right for every mix of facets is probably not possible.
     91             // Future needs can dictate changes here.
     92             currentSeparator = info.FollowingSeparator;
     93         }
     94     }
     95 
     96     std::string TimePointToString(const std::chrono::system_clock::time_point& time, bool useRFC3339)
     97     {
     98         std::ostringstream stream;
     99         OutputTimePoint(stream, time, useRFC3339);
    100         return std::move(stream).str();
    101     }
    102 
    103     std::string TimePointToString(const std::chrono::system_clock::time_point& time, TimeFacet facet)
    104     {
    105         std::ostringstream stream;
    106         OutputTimePoint(stream, time, facet);
    107         return std::move(stream).str();
    108     }
    109 
    110     std::string GetCurrentTimeForFilename(bool shortTime)
    111     {
    112         return TimePointToString(std::chrono::system_clock::now(), (shortTime ? TimeFacet::ShortYearSecondPrecision : TimeFacet::Default) | TimeFacet::Filename);
    113     }
    114 
    115     std::string GetCurrentDateForARP()
    116     {
    117         auto now = std::chrono::system_clock::now();
    118         std::time_t tt = std::chrono::system_clock::to_time_t(now);
    119 
    120         struct tm newTime;
    121         localtime_s(&newTime, &tt);
    122 
    123         std::stringstream ss;
    124         ss << std::put_time(&newTime, "%Y%m%d");
    125         return ss.str();
    126     }
    127 
    128     int64_t GetCurrentUnixEpoch()
    129     {
    130         static_assert(std::is_same_v<int64_t, decltype(time(nullptr))>, "time returns a 64-bit integer");
    131         time_t now = time(nullptr);
    132         return static_cast<int64_t>(now);
    133     }
    134 
    135     int64_t ConvertSystemClockToUnixEpoch(const std::chrono::system_clock::time_point& time)
    136     {
    137         static_assert(std::is_same_v<int64_t, decltype(std::chrono::system_clock::to_time_t(time))>, "to_time_t returns a 64-bit integer");
    138         time_t timeAsTimeT = std::chrono::system_clock::to_time_t(time);
    139         return static_cast<int64_t>(timeAsTimeT);
    140     }
    141 
    142     std::chrono::system_clock::time_point ConvertUnixEpochToSystemClock(int64_t epoch)
    143     {
    144         return std::chrono::system_clock::from_time_t(static_cast<time_t>(epoch));
    145     }
    146 
    147     std::chrono::system_clock::time_point ConvertFiletimeToSystemClock(const FILETIME& fileTime)
    148     {
    149         // Windows epoch (1601) to Unix epoch (1970) offset in 100-nanosecond intervals
    150         constexpr int64_t EPOCH_DIFFERENCE = 116444736000000000LL;
    151 
    152         // Combine FILETIME into a 64-bit value
    153         uint64_t fileTimeValue = (static_cast<uint64_t>(fileTime.dwHighDateTime) << 32) | fileTime.dwLowDateTime;
    154 
    155         // Convert to 100-nanosecond intervals since Unix epoch
    156         int64_t unixTime100ns = static_cast<int64_t>(fileTimeValue) - EPOCH_DIFFERENCE;
    157 
    158         // Convert to chrono duration (system_clock::duration is usually nanoseconds or microseconds)
    159         return std::chrono::system_clock::time_point(
    160             std::chrono::duration_cast<std::chrono::system_clock::duration>(
    161                 std::chrono::nanoseconds(unixTime100ns * 100)));
    162     }
    163 
    164     std::chrono::system_clock::time_point GetTimePointFromVersion(const UInt64Version& version)
    165     {
    166         // Our custom format for converting UTC into a version is:
    167         //  Major :: `Year` [1, 9999]
    168         //  Minor :: `Month * 100 + Day` where Month [1, 12] and Day [1, 31]
    169         //  Build :: `Hour * 100 + Minute` where Hour [1, 24] and Minute [0, 59]
    170         //  Revision :: Milliseconds, but since no seconds are available we will disregard this
    171 
    172         tm versionTime{};
    173 
    174         // Limit to the range supported by _mkgmtime64, which is 1970 to 3000 (hello to Y3K maintainers from 2023!)
    175         UINT64 majorVersion = version.Major();
    176         if (majorVersion < 1970 || majorVersion > 3000)
    177         {
    178             return std::chrono::system_clock::time_point::min();
    179         }
    180         versionTime.tm_year = static_cast<int>(majorVersion) - 1900;
    181 
    182         UINT64 minorVersion = version.Minor();
    183         UINT64 monthValue = minorVersion / 100;
    184         UINT64 dayValue = minorVersion % 100;
    185         if (monthValue < 1 || monthValue > 12 || dayValue < 1 || dayValue > 31)
    186         {
    187             return std::chrono::system_clock::time_point::min();
    188         }
    189         versionTime.tm_mon = static_cast<int>(monthValue) - 1;
    190         versionTime.tm_mday = static_cast<int>(dayValue);
    191 
    192         UINT64 buildVersion = version.Build();
    193         UINT64 hourValue = buildVersion / 100;
    194         UINT64 minuteValue = buildVersion % 100;
    195         if (hourValue < 1 || hourValue > 24 || minuteValue > 59)
    196         {
    197             return std::chrono::system_clock::time_point::min();
    198         }
    199         versionTime.tm_hour = static_cast<int>(hourValue) - 1;
    200         versionTime.tm_min = static_cast<int>(minuteValue);
    201 
    202         return std::chrono::system_clock::from_time_t(_mkgmtime64(&versionTime));
    203     }
    204 }