xref: /openbmc/bmcweb/features/redfish/include/utils/time_utils.hpp (revision 89492a157c9cf972b342421e24d41fd382510251)
1081ebf06SWludzik, Jozef #pragma once
2081ebf06SWludzik, Jozef 
34dbb8aeaSWludzik, Jozef #include "logging.hpp"
44dbb8aeaSWludzik, Jozef 
5c2e32007SEd Tanous #include <boost/date_time.hpp>
6c2e32007SEd Tanous 
79ea15c35SEd Tanous #include <algorithm>
84dbb8aeaSWludzik, Jozef #include <charconv>
9081ebf06SWludzik, Jozef #include <chrono>
104dbb8aeaSWludzik, Jozef #include <cmath>
119ea15c35SEd Tanous #include <compare>
129ea15c35SEd Tanous #include <cstddef>
13d5c80ad9SNan Zhou #include <cstdint>
144dbb8aeaSWludzik, Jozef #include <optional>
159ea15c35SEd Tanous #include <ratio>
16081ebf06SWludzik, Jozef #include <string>
179ea15c35SEd Tanous #include <string_view>
184dbb8aeaSWludzik, Jozef #include <system_error>
19081ebf06SWludzik, Jozef 
20d5c80ad9SNan Zhou // IWYU pragma: no_include <stddef.h>
21d5c80ad9SNan Zhou // IWYU pragma: no_include <stdint.h>
22d5c80ad9SNan Zhou 
23081ebf06SWludzik, Jozef namespace redfish
24081ebf06SWludzik, Jozef {
25081ebf06SWludzik, Jozef 
26081ebf06SWludzik, Jozef namespace time_utils
27081ebf06SWludzik, Jozef {
28081ebf06SWludzik, Jozef 
29081ebf06SWludzik, Jozef namespace details
30081ebf06SWludzik, Jozef {
31081ebf06SWludzik, Jozef 
326de264ccSEd Tanous constexpr intmax_t dayDuration = static_cast<intmax_t>(24 * 60 * 60);
336de264ccSEd Tanous using Days = std::chrono::duration<long long, std::ratio<dayDuration>>;
344dbb8aeaSWludzik, Jozef 
357be4c8adSEd Tanous // Creates a string from an integer in the most efficient way possible without
367be4c8adSEd Tanous // using std::locale.  Adds an exact zero pad based on the pad input parameter.
377be4c8adSEd Tanous // Does not handle negative numbers.
387be4c8adSEd Tanous inline std::string padZeros(int64_t value, size_t pad)
39081ebf06SWludzik, Jozef {
407be4c8adSEd Tanous     std::string result(pad, '0');
417be4c8adSEd Tanous     for (int64_t val = value; pad > 0; pad--)
42081ebf06SWludzik, Jozef     {
437be4c8adSEd Tanous         result[pad - 1] = static_cast<char>('0' + val % 10);
447be4c8adSEd Tanous         val /= 10;
45081ebf06SWludzik, Jozef     }
467be4c8adSEd Tanous     return result;
47081ebf06SWludzik, Jozef }
484dbb8aeaSWludzik, Jozef 
494dbb8aeaSWludzik, Jozef template <typename FromTime>
504dbb8aeaSWludzik, Jozef bool fromDurationItem(std::string_view& fmt, const char postfix,
514dbb8aeaSWludzik, Jozef                       std::chrono::milliseconds& out)
524dbb8aeaSWludzik, Jozef {
534dbb8aeaSWludzik, Jozef     const size_t pos = fmt.find(postfix);
544dbb8aeaSWludzik, Jozef     if (pos == std::string::npos)
554dbb8aeaSWludzik, Jozef     {
564dbb8aeaSWludzik, Jozef         return true;
574dbb8aeaSWludzik, Jozef     }
584dbb8aeaSWludzik, Jozef     if ((pos + 1U) > fmt.size())
594dbb8aeaSWludzik, Jozef     {
604dbb8aeaSWludzik, Jozef         return false;
614dbb8aeaSWludzik, Jozef     }
624dbb8aeaSWludzik, Jozef 
63543f4400SEd Tanous     const char* end = nullptr;
644dbb8aeaSWludzik, Jozef     std::chrono::milliseconds::rep ticks = 0;
654dbb8aeaSWludzik, Jozef     if constexpr (std::is_same_v<FromTime, std::chrono::milliseconds>)
664dbb8aeaSWludzik, Jozef     {
674dbb8aeaSWludzik, Jozef         end = fmt.data() + std::min<size_t>(pos, 3U);
684dbb8aeaSWludzik, Jozef     }
694dbb8aeaSWludzik, Jozef     else
704dbb8aeaSWludzik, Jozef     {
714dbb8aeaSWludzik, Jozef         end = fmt.data() + pos;
724dbb8aeaSWludzik, Jozef     }
734dbb8aeaSWludzik, Jozef 
744dbb8aeaSWludzik, Jozef     auto [ptr, ec] = std::from_chars(fmt.data(), end, ticks);
754dbb8aeaSWludzik, Jozef     if (ptr != end || ec != std::errc())
764dbb8aeaSWludzik, Jozef     {
774dbb8aeaSWludzik, Jozef         BMCWEB_LOG_ERROR << "Failed to convert string to decimal with err: "
784dbb8aeaSWludzik, Jozef                          << static_cast<int>(ec) << "("
794dbb8aeaSWludzik, Jozef                          << std::make_error_code(ec).message() << "), ptr{"
804dbb8aeaSWludzik, Jozef                          << static_cast<const void*>(ptr) << "} != end{"
814dbb8aeaSWludzik, Jozef                          << static_cast<const void*>(end) << "})";
824dbb8aeaSWludzik, Jozef         return false;
834dbb8aeaSWludzik, Jozef     }
844dbb8aeaSWludzik, Jozef 
854dbb8aeaSWludzik, Jozef     if constexpr (std::is_same_v<FromTime, std::chrono::milliseconds>)
864dbb8aeaSWludzik, Jozef     {
874dbb8aeaSWludzik, Jozef         ticks *= static_cast<std::chrono::milliseconds::rep>(
884dbb8aeaSWludzik, Jozef             std::pow(10, 3 - std::min<size_t>(pos, 3U)));
894dbb8aeaSWludzik, Jozef     }
904dbb8aeaSWludzik, Jozef     if (ticks < 0)
914dbb8aeaSWludzik, Jozef     {
924dbb8aeaSWludzik, Jozef         return false;
934dbb8aeaSWludzik, Jozef     }
944dbb8aeaSWludzik, Jozef 
954dbb8aeaSWludzik, Jozef     out += FromTime(ticks);
964dbb8aeaSWludzik, Jozef     const auto maxConversionRange =
974dbb8aeaSWludzik, Jozef         std::chrono::duration_cast<FromTime>(std::chrono::milliseconds::max())
984dbb8aeaSWludzik, Jozef             .count();
994dbb8aeaSWludzik, Jozef     if (out < FromTime(ticks) || maxConversionRange < ticks)
1004dbb8aeaSWludzik, Jozef     {
1014dbb8aeaSWludzik, Jozef         return false;
1024dbb8aeaSWludzik, Jozef     }
1034dbb8aeaSWludzik, Jozef 
1044dbb8aeaSWludzik, Jozef     fmt.remove_prefix(pos + 1U);
1054dbb8aeaSWludzik, Jozef     return true;
1064dbb8aeaSWludzik, Jozef }
107081ebf06SWludzik, Jozef } // namespace details
108081ebf06SWludzik, Jozef 
109081ebf06SWludzik, Jozef /**
1104dbb8aeaSWludzik, Jozef  * @brief Convert string that represents value in Duration Format to its numeric
1114dbb8aeaSWludzik, Jozef  *        equivalent.
1124dbb8aeaSWludzik, Jozef  */
1134f48d5f6SEd Tanous inline std::optional<std::chrono::milliseconds>
1144dbb8aeaSWludzik, Jozef     fromDurationString(const std::string& str)
1154dbb8aeaSWludzik, Jozef {
1164dbb8aeaSWludzik, Jozef     std::chrono::milliseconds out = std::chrono::milliseconds::zero();
1174dbb8aeaSWludzik, Jozef     std::string_view v = str;
1184dbb8aeaSWludzik, Jozef 
1194dbb8aeaSWludzik, Jozef     if (v.empty())
1204dbb8aeaSWludzik, Jozef     {
1214dbb8aeaSWludzik, Jozef         return out;
1224dbb8aeaSWludzik, Jozef     }
1234dbb8aeaSWludzik, Jozef     if (v.front() != 'P')
1244dbb8aeaSWludzik, Jozef     {
1254dbb8aeaSWludzik, Jozef         BMCWEB_LOG_ERROR << "Invalid duration format: " << str;
1264dbb8aeaSWludzik, Jozef         return std::nullopt;
1274dbb8aeaSWludzik, Jozef     }
1284dbb8aeaSWludzik, Jozef 
1294dbb8aeaSWludzik, Jozef     v.remove_prefix(1);
1304dbb8aeaSWludzik, Jozef     if (!details::fromDurationItem<details::Days>(v, 'D', out))
1314dbb8aeaSWludzik, Jozef     {
1324dbb8aeaSWludzik, Jozef         BMCWEB_LOG_ERROR << "Invalid duration format: " << str;
1334dbb8aeaSWludzik, Jozef         return std::nullopt;
1344dbb8aeaSWludzik, Jozef     }
1354dbb8aeaSWludzik, Jozef 
1364dbb8aeaSWludzik, Jozef     if (v.empty())
1374dbb8aeaSWludzik, Jozef     {
1384dbb8aeaSWludzik, Jozef         return out;
1394dbb8aeaSWludzik, Jozef     }
1404dbb8aeaSWludzik, Jozef     if (v.front() != 'T')
1414dbb8aeaSWludzik, Jozef     {
1424dbb8aeaSWludzik, Jozef         BMCWEB_LOG_ERROR << "Invalid duration format: " << str;
1434dbb8aeaSWludzik, Jozef         return std::nullopt;
1444dbb8aeaSWludzik, Jozef     }
1454dbb8aeaSWludzik, Jozef 
1464dbb8aeaSWludzik, Jozef     v.remove_prefix(1);
1474dbb8aeaSWludzik, Jozef     if (!details::fromDurationItem<std::chrono::hours>(v, 'H', out) ||
1484dbb8aeaSWludzik, Jozef         !details::fromDurationItem<std::chrono::minutes>(v, 'M', out))
1494dbb8aeaSWludzik, Jozef     {
1504dbb8aeaSWludzik, Jozef         BMCWEB_LOG_ERROR << "Invalid duration format: " << str;
1514dbb8aeaSWludzik, Jozef         return std::nullopt;
1524dbb8aeaSWludzik, Jozef     }
1534dbb8aeaSWludzik, Jozef 
1544dbb8aeaSWludzik, Jozef     if (v.find('.') != std::string::npos && v.find('S') != std::string::npos)
1554dbb8aeaSWludzik, Jozef     {
1564dbb8aeaSWludzik, Jozef         if (!details::fromDurationItem<std::chrono::seconds>(v, '.', out) ||
1574dbb8aeaSWludzik, Jozef             !details::fromDurationItem<std::chrono::milliseconds>(v, 'S', out))
1584dbb8aeaSWludzik, Jozef         {
1594dbb8aeaSWludzik, Jozef             BMCWEB_LOG_ERROR << "Invalid duration format: " << str;
1604dbb8aeaSWludzik, Jozef             return std::nullopt;
1614dbb8aeaSWludzik, Jozef         }
1624dbb8aeaSWludzik, Jozef     }
1634dbb8aeaSWludzik, Jozef     else if (!details::fromDurationItem<std::chrono::seconds>(v, 'S', out))
1644dbb8aeaSWludzik, Jozef     {
1654dbb8aeaSWludzik, Jozef         BMCWEB_LOG_ERROR << "Invalid duration format: " << str;
1664dbb8aeaSWludzik, Jozef         return std::nullopt;
1674dbb8aeaSWludzik, Jozef     }
1684dbb8aeaSWludzik, Jozef 
1694dbb8aeaSWludzik, Jozef     if (!v.empty())
1704dbb8aeaSWludzik, Jozef     {
1714dbb8aeaSWludzik, Jozef         BMCWEB_LOG_ERROR << "Invalid duration format: " << str;
1724dbb8aeaSWludzik, Jozef         return std::nullopt;
1734dbb8aeaSWludzik, Jozef     }
1744dbb8aeaSWludzik, Jozef     return out;
1754dbb8aeaSWludzik, Jozef }
1764dbb8aeaSWludzik, Jozef 
1774dbb8aeaSWludzik, Jozef /**
178081ebf06SWludzik, Jozef  * @brief Convert time value into duration format that is based on ISO 8601.
179081ebf06SWludzik, Jozef  *        Example output: "P12DT1M5.5S"
180081ebf06SWludzik, Jozef  *        Ref: Redfish Specification, Section 9.4.4. Duration values
181081ebf06SWludzik, Jozef  */
182b00dcc27SEd Tanous inline std::string toDurationString(std::chrono::milliseconds ms)
183081ebf06SWludzik, Jozef {
184081ebf06SWludzik, Jozef     if (ms < std::chrono::milliseconds::zero())
185081ebf06SWludzik, Jozef     {
186081ebf06SWludzik, Jozef         return "";
187081ebf06SWludzik, Jozef     }
188081ebf06SWludzik, Jozef 
189081ebf06SWludzik, Jozef     std::string fmt;
190081ebf06SWludzik, Jozef     fmt.reserve(sizeof("PxxxxxxxxxxxxDTxxHxxMxx.xxxxxxS"));
191081ebf06SWludzik, Jozef 
1924dbb8aeaSWludzik, Jozef     details::Days days = std::chrono::floor<details::Days>(ms);
193081ebf06SWludzik, Jozef     ms -= days;
194081ebf06SWludzik, Jozef 
195081ebf06SWludzik, Jozef     std::chrono::hours hours = std::chrono::floor<std::chrono::hours>(ms);
196081ebf06SWludzik, Jozef     ms -= hours;
197081ebf06SWludzik, Jozef 
198081ebf06SWludzik, Jozef     std::chrono::minutes minutes = std::chrono::floor<std::chrono::minutes>(ms);
199081ebf06SWludzik, Jozef     ms -= minutes;
200081ebf06SWludzik, Jozef 
201081ebf06SWludzik, Jozef     std::chrono::seconds seconds = std::chrono::floor<std::chrono::seconds>(ms);
202081ebf06SWludzik, Jozef     ms -= seconds;
203081ebf06SWludzik, Jozef 
204081ebf06SWludzik, Jozef     fmt = "P";
205081ebf06SWludzik, Jozef     if (days.count() > 0)
206081ebf06SWludzik, Jozef     {
207081ebf06SWludzik, Jozef         fmt += std::to_string(days.count()) + "D";
208081ebf06SWludzik, Jozef     }
209081ebf06SWludzik, Jozef     fmt += "T";
210081ebf06SWludzik, Jozef     if (hours.count() > 0)
211081ebf06SWludzik, Jozef     {
212081ebf06SWludzik, Jozef         fmt += std::to_string(hours.count()) + "H";
213081ebf06SWludzik, Jozef     }
214081ebf06SWludzik, Jozef     if (minutes.count() > 0)
215081ebf06SWludzik, Jozef     {
216081ebf06SWludzik, Jozef         fmt += std::to_string(minutes.count()) + "M";
217081ebf06SWludzik, Jozef     }
218081ebf06SWludzik, Jozef     if (seconds.count() != 0 || ms.count() != 0)
219081ebf06SWludzik, Jozef     {
220081ebf06SWludzik, Jozef         fmt += std::to_string(seconds.count()) + ".";
2217be4c8adSEd Tanous         fmt += details::padZeros(ms.count(), 3);
2227be4c8adSEd Tanous         fmt += "S";
223081ebf06SWludzik, Jozef     }
224081ebf06SWludzik, Jozef 
225081ebf06SWludzik, Jozef     return fmt;
226081ebf06SWludzik, Jozef }
227081ebf06SWludzik, Jozef 
2281b7e696bSLukasz Kazmierczak inline std::optional<std::string>
2291b7e696bSLukasz Kazmierczak     toDurationStringFromUint(const uint64_t timeMs)
2301b7e696bSLukasz Kazmierczak {
2311b7e696bSLukasz Kazmierczak     static const uint64_t maxTimeMs =
2321b7e696bSLukasz Kazmierczak         static_cast<uint64_t>(std::chrono::milliseconds::max().count());
2331b7e696bSLukasz Kazmierczak 
2341b7e696bSLukasz Kazmierczak     if (maxTimeMs < timeMs)
2351b7e696bSLukasz Kazmierczak     {
2361b7e696bSLukasz Kazmierczak         return std::nullopt;
2371b7e696bSLukasz Kazmierczak     }
2381b7e696bSLukasz Kazmierczak 
2391b7e696bSLukasz Kazmierczak     std::string duration = toDurationString(std::chrono::milliseconds(timeMs));
2401b7e696bSLukasz Kazmierczak     if (duration.empty())
2411b7e696bSLukasz Kazmierczak     {
2421b7e696bSLukasz Kazmierczak         return std::nullopt;
2431b7e696bSLukasz Kazmierczak     }
2441b7e696bSLukasz Kazmierczak 
2451b7e696bSLukasz Kazmierczak     return std::make_optional(duration);
2461b7e696bSLukasz Kazmierczak }
2471b7e696bSLukasz Kazmierczak 
2482b82937eSEd Tanous namespace details
2492b82937eSEd Tanous {
2502b82937eSEd Tanous // Returns year/month/day triple in civil calendar
2512b82937eSEd Tanous // Preconditions:  z is number of days since 1970-01-01 and is in the range:
2522b82937eSEd Tanous //                   [numeric_limits<Int>::min(),
2532b82937eSEd Tanous //                   numeric_limits<Int>::max()-719468].
2542b82937eSEd Tanous // Algorithm sourced from
2552b82937eSEd Tanous // https://howardhinnant.github.io/date_algorithms.html#civil_from_days
2562b82937eSEd Tanous // All constants are explained in the above
2572b82937eSEd Tanous template <class IntType>
2582b82937eSEd Tanous constexpr std::tuple<IntType, unsigned, unsigned>
2592b82937eSEd Tanous     civilFromDays(IntType z) noexcept
2602b82937eSEd Tanous {
2612b82937eSEd Tanous     z += 719468;
2622b82937eSEd Tanous     IntType era = (z >= 0 ? z : z - 146096) / 146097;
2632b82937eSEd Tanous     unsigned doe = static_cast<unsigned>(z - era * 146097); // [0, 146096]
264*89492a15SPatrick Williams     unsigned yoe = (doe - doe / 1460 + doe / 36524 - doe / 146096) /
265*89492a15SPatrick Williams                    365;                                     // [0, 399]
2662b82937eSEd Tanous     IntType y = static_cast<IntType>(yoe) + era * 400;
2672b82937eSEd Tanous     unsigned doy = doe - (365 * yoe + yoe / 4 - yoe / 100); // [0, 365]
2682b82937eSEd Tanous     unsigned mp = (5 * doy + 2) / 153;                      // [0, 11]
2692b82937eSEd Tanous     unsigned d = doy - (153 * mp + 2) / 5 + 1;              // [1, 31]
2702b82937eSEd Tanous     unsigned m = mp < 10 ? mp + 3 : mp - 9;                 // [1, 12]
2712b82937eSEd Tanous 
2722b82937eSEd Tanous     return std::tuple<IntType, unsigned, unsigned>(y + (m <= 2), m, d);
2732b82937eSEd Tanous }
2742b82937eSEd Tanous 
2752b82937eSEd Tanous template <typename IntType, typename Period>
2762b82937eSEd Tanous std::string toISO8061ExtendedStr(std::chrono::duration<IntType, Period> t)
2772b82937eSEd Tanous {
2782b82937eSEd Tanous     using seconds = std::chrono::duration<int>;
2792b82937eSEd Tanous     using minutes = std::chrono::duration<int, std::ratio<60>>;
2802b82937eSEd Tanous     using hours = std::chrono::duration<int, std::ratio<3600>>;
2812b82937eSEd Tanous     using days = std::chrono::duration<
2822b82937eSEd Tanous         IntType, std::ratio_multiply<hours::period, std::ratio<24>>>;
2832b82937eSEd Tanous 
2842b82937eSEd Tanous     // d is days since 1970-01-01
2852b82937eSEd Tanous     days d = std::chrono::duration_cast<days>(t);
2862b82937eSEd Tanous 
2872b82937eSEd Tanous     // t is now time duration since midnight of day d
2882b82937eSEd Tanous     t -= d;
2892b82937eSEd Tanous 
2902b82937eSEd Tanous     // break d down into year/month/day
2912b82937eSEd Tanous     int year = 0;
2922b82937eSEd Tanous     int month = 0;
2932b82937eSEd Tanous     int day = 0;
2942b82937eSEd Tanous     std::tie(year, month, day) = details::civilFromDays(d.count());
2952b82937eSEd Tanous     // Check against limits.  Can't go above year 9999, and can't go below epoch
2962b82937eSEd Tanous     // (1970)
2972b82937eSEd Tanous     if (year >= 10000)
2982b82937eSEd Tanous     {
2992b82937eSEd Tanous         year = 9999;
3002b82937eSEd Tanous         month = 12;
3012b82937eSEd Tanous         day = 31;
3022b82937eSEd Tanous         t = days(1) - std::chrono::duration<IntType, Period>(1);
3032b82937eSEd Tanous     }
3042b82937eSEd Tanous     else if (year < 1970)
3052b82937eSEd Tanous     {
3062b82937eSEd Tanous         year = 1970;
3072b82937eSEd Tanous         month = 1;
3082b82937eSEd Tanous         day = 1;
3092b82937eSEd Tanous         t = std::chrono::duration<IntType, Period>::zero();
3102b82937eSEd Tanous     }
3112b82937eSEd Tanous 
3122b82937eSEd Tanous     std::string out;
3132b82937eSEd Tanous     out += details::padZeros(year, 4);
3142b82937eSEd Tanous     out += '-';
3152b82937eSEd Tanous     out += details::padZeros(month, 2);
3162b82937eSEd Tanous     out += '-';
3172b82937eSEd Tanous     out += details::padZeros(day, 2);
3182b82937eSEd Tanous     out += 'T';
3192b82937eSEd Tanous     hours hr = duration_cast<hours>(t);
3202b82937eSEd Tanous     out += details::padZeros(hr.count(), 2);
3212b82937eSEd Tanous     t -= hr;
3222b82937eSEd Tanous     out += ':';
3232b82937eSEd Tanous 
3242b82937eSEd Tanous     minutes mt = duration_cast<minutes>(t);
3252b82937eSEd Tanous     out += details::padZeros(mt.count(), 2);
3262b82937eSEd Tanous     t -= mt;
3272b82937eSEd Tanous     out += ':';
3282b82937eSEd Tanous 
3292b82937eSEd Tanous     seconds se = duration_cast<seconds>(t);
3302b82937eSEd Tanous     out += details::padZeros(se.count(), 2);
3312b82937eSEd Tanous     t -= se;
3322b82937eSEd Tanous 
3332b82937eSEd Tanous     if constexpr (std::is_same_v<typename decltype(t)::period, std::milli>)
3342b82937eSEd Tanous     {
3352b82937eSEd Tanous         out += '.';
3362b82937eSEd Tanous         using MilliDuration = std::chrono::duration<int, std::milli>;
3372b82937eSEd Tanous         MilliDuration subsec = duration_cast<MilliDuration>(t);
3382b82937eSEd Tanous         out += details::padZeros(subsec.count(), 3);
3392b82937eSEd Tanous     }
3402b82937eSEd Tanous     else if constexpr (std::is_same_v<typename decltype(t)::period, std::micro>)
3412b82937eSEd Tanous     {
3422b82937eSEd Tanous         out += '.';
3432b82937eSEd Tanous 
3442b82937eSEd Tanous         using MicroDuration = std::chrono::duration<int, std::micro>;
3452b82937eSEd Tanous         MicroDuration subsec = duration_cast<MicroDuration>(t);
3462b82937eSEd Tanous         out += details::padZeros(subsec.count(), 6);
3472b82937eSEd Tanous     }
3482b82937eSEd Tanous 
3492b82937eSEd Tanous     out += "+00:00";
3502b82937eSEd Tanous     return out;
3512b82937eSEd Tanous }
3522b82937eSEd Tanous } // namespace details
3532b82937eSEd Tanous 
3542b82937eSEd Tanous // Returns the formatted date time string.
3552b82937eSEd Tanous // Note that the maximum supported date is 9999-12-31T23:59:59+00:00, if
3562b82937eSEd Tanous // the given |secondsSinceEpoch| is too large, we return the maximum supported
3572b82937eSEd Tanous // date.
3582b82937eSEd Tanous inline std::string getDateTimeUint(uint64_t secondsSinceEpoch)
3592b82937eSEd Tanous {
3602b82937eSEd Tanous     using DurationType = std::chrono::duration<uint64_t>;
3612b82937eSEd Tanous     DurationType sinceEpoch(secondsSinceEpoch);
3622b82937eSEd Tanous     return details::toISO8061ExtendedStr(sinceEpoch);
3632b82937eSEd Tanous }
3642b82937eSEd Tanous 
3652b82937eSEd Tanous // Returns the formatted date time string with millisecond precision
3662b82937eSEd Tanous // Note that the maximum supported date is 9999-12-31T23:59:59+00:00, if
3672b82937eSEd Tanous // the given |secondsSinceEpoch| is too large, we return the maximum supported
3682b82937eSEd Tanous // date.
3692b82937eSEd Tanous inline std::string getDateTimeUintMs(uint64_t milliSecondsSinceEpoch)
3702b82937eSEd Tanous {
3712b82937eSEd Tanous     using DurationType = std::chrono::duration<uint64_t, std::milli>;
3722b82937eSEd Tanous     DurationType sinceEpoch(milliSecondsSinceEpoch);
3732b82937eSEd Tanous     return details::toISO8061ExtendedStr(sinceEpoch);
3742b82937eSEd Tanous }
3752b82937eSEd Tanous 
3762b82937eSEd Tanous // Returns the formatted date time string with microsecond precision
3772b82937eSEd Tanous inline std::string getDateTimeUintUs(uint64_t microSecondsSinceEpoch)
3782b82937eSEd Tanous {
3792b82937eSEd Tanous     using DurationType = std::chrono::duration<uint64_t, std::micro>;
3802b82937eSEd Tanous     DurationType sinceEpoch(microSecondsSinceEpoch);
3812b82937eSEd Tanous     return details::toISO8061ExtendedStr(sinceEpoch);
3822b82937eSEd Tanous }
3832b82937eSEd Tanous 
3842b82937eSEd Tanous inline std::string getDateTimeStdtime(std::time_t secondsSinceEpoch)
3852b82937eSEd Tanous {
3862b82937eSEd Tanous     using DurationType = std::chrono::duration<std::time_t>;
3872b82937eSEd Tanous     DurationType sinceEpoch(secondsSinceEpoch);
3882b82937eSEd Tanous     return details::toISO8061ExtendedStr(sinceEpoch);
3892b82937eSEd Tanous }
3902b82937eSEd Tanous 
3912b82937eSEd Tanous /**
3922b82937eSEd Tanous  * Returns the current Date, Time & the local Time Offset
3932b82937eSEd Tanous  * infromation in a pair
3942b82937eSEd Tanous  *
3952b82937eSEd Tanous  * @param[in] None
3962b82937eSEd Tanous  *
3972b82937eSEd Tanous  * @return std::pair<std::string, std::string>, which consist
3982b82937eSEd Tanous  * of current DateTime & the TimeOffset strings respectively.
3992b82937eSEd Tanous  */
4002b82937eSEd Tanous inline std::pair<std::string, std::string> getDateTimeOffsetNow()
4012b82937eSEd Tanous {
4022b82937eSEd Tanous     std::time_t time = std::time(nullptr);
4032b82937eSEd Tanous     std::string dateTime = getDateTimeStdtime(time);
4042b82937eSEd Tanous 
4052b82937eSEd Tanous     /* extract the local Time Offset value from the
4062b82937eSEd Tanous      * recevied dateTime string.
4072b82937eSEd Tanous      */
4082b82937eSEd Tanous     std::string timeOffset("Z00:00");
4092b82937eSEd Tanous     std::size_t lastPos = dateTime.size();
4102b82937eSEd Tanous     std::size_t len = timeOffset.size();
4112b82937eSEd Tanous     if (lastPos > len)
4122b82937eSEd Tanous     {
4132b82937eSEd Tanous         timeOffset = dateTime.substr(lastPos - len);
4142b82937eSEd Tanous     }
4152b82937eSEd Tanous 
4162b82937eSEd Tanous     return std::make_pair(dateTime, timeOffset);
4172b82937eSEd Tanous }
4182b82937eSEd Tanous 
419c2e32007SEd Tanous using usSinceEpoch = std::chrono::duration<uint64_t, std::micro>;
420c2e32007SEd Tanous inline std::optional<usSinceEpoch> dateStringToEpoch(std::string_view datetime)
421c2e32007SEd Tanous {
422c2e32007SEd Tanous     std::string date(datetime);
423c2e32007SEd Tanous     std::stringstream stream(date);
424c2e32007SEd Tanous     // Convert from ISO 8601 to boost local_time
425c2e32007SEd Tanous     // (BMC only has time in UTC)
426c2e32007SEd Tanous     boost::posix_time::ptime posixTime;
427c2e32007SEd Tanous     boost::posix_time::ptime epoch(boost::gregorian::date(1970, 1, 1));
428c2e32007SEd Tanous     // Facet gets deleted with the stringsteam
429c2e32007SEd Tanous     auto ifc = std::make_unique<boost::local_time::local_time_input_facet>(
430c2e32007SEd Tanous         "%Y-%m-%d %H:%M:%S%F %ZP");
431c2e32007SEd Tanous     stream.imbue(std::locale(stream.getloc(), ifc.release()));
432c2e32007SEd Tanous 
433c2e32007SEd Tanous     boost::local_time::local_date_time ldt(boost::local_time::not_a_date_time);
434c2e32007SEd Tanous 
435c2e32007SEd Tanous     if (!(stream >> ldt))
436c2e32007SEd Tanous     {
437c2e32007SEd Tanous         return std::nullopt;
438c2e32007SEd Tanous     }
439c2e32007SEd Tanous     posixTime = ldt.utc_time();
440c2e32007SEd Tanous     boost::posix_time::time_duration dur = posixTime - epoch;
441c2e32007SEd Tanous     uint64_t durMicroSecs = static_cast<uint64_t>(dur.total_microseconds());
442c2e32007SEd Tanous     return std::chrono::duration<uint64_t, std::micro>{durMicroSecs};
443c2e32007SEd Tanous }
444c2e32007SEd Tanous 
445081ebf06SWludzik, Jozef } // namespace time_utils
446081ebf06SWludzik, Jozef } // namespace redfish
447