xref: /openbmc/bmcweb/features/redfish/include/utils/time_utils.hpp (revision b5190062437414f32c3585cbe02093a4cf27f86a)
1081ebf06SWludzik, Jozef #pragma once
2081ebf06SWludzik, Jozef 
3*b5190062SHieu Huynh #include "app.hpp"
44dbb8aeaSWludzik, Jozef #include "logging.hpp"
54dbb8aeaSWludzik, Jozef 
69ea15c35SEd Tanous #include <algorithm>
74dbb8aeaSWludzik, Jozef #include <charconv>
8081ebf06SWludzik, Jozef #include <chrono>
99ea15c35SEd Tanous #include <cstddef>
10d5c80ad9SNan Zhou #include <cstdint>
11f653730dSEd Tanous #include <format>
124dbb8aeaSWludzik, Jozef #include <optional>
139ea15c35SEd Tanous #include <ratio>
14081ebf06SWludzik, Jozef #include <string>
159ea15c35SEd Tanous #include <string_view>
16081ebf06SWludzik, Jozef 
17081ebf06SWludzik, Jozef namespace redfish
18081ebf06SWludzik, Jozef {
19081ebf06SWludzik, Jozef 
20081ebf06SWludzik, Jozef namespace time_utils
21081ebf06SWludzik, Jozef {
22081ebf06SWludzik, Jozef 
23081ebf06SWludzik, Jozef /**
244dbb8aeaSWludzik, Jozef  * @brief Convert string that represents value in Duration Format to its numeric
254dbb8aeaSWludzik, Jozef  *        equivalent.
264dbb8aeaSWludzik, Jozef  */
274f48d5f6SEd Tanous inline std::optional<std::chrono::milliseconds>
281249e9cbSEd Tanous     fromDurationString(std::string_view v)
294dbb8aeaSWludzik, Jozef {
304dbb8aeaSWludzik, Jozef     std::chrono::milliseconds out = std::chrono::milliseconds::zero();
311249e9cbSEd Tanous     enum class ProcessingStage
324dbb8aeaSWludzik, Jozef     {
331249e9cbSEd Tanous         // P1DT1H1M1.100S
341249e9cbSEd Tanous         P,
351249e9cbSEd Tanous         Days,
361249e9cbSEd Tanous         Hours,
371249e9cbSEd Tanous         Minutes,
381249e9cbSEd Tanous         Seconds,
391249e9cbSEd Tanous         Milliseconds,
401249e9cbSEd Tanous         Done,
411249e9cbSEd Tanous     };
421249e9cbSEd Tanous     ProcessingStage stage = ProcessingStage::P;
431249e9cbSEd Tanous 
441249e9cbSEd Tanous     while (!v.empty())
451249e9cbSEd Tanous     {
461249e9cbSEd Tanous         if (stage == ProcessingStage::P)
471249e9cbSEd Tanous         {
484dbb8aeaSWludzik, Jozef             if (v.front() != 'P')
494dbb8aeaSWludzik, Jozef             {
504dbb8aeaSWludzik, Jozef                 return std::nullopt;
514dbb8aeaSWludzik, Jozef             }
524dbb8aeaSWludzik, Jozef             v.remove_prefix(1);
531249e9cbSEd Tanous             stage = ProcessingStage::Days;
541249e9cbSEd Tanous             continue;
551249e9cbSEd Tanous         }
56c98dbc64SEd Tanous         if (stage == ProcessingStage::Days)
574dbb8aeaSWludzik, Jozef         {
581249e9cbSEd Tanous             if (v.front() == 'T')
591249e9cbSEd Tanous             {
604dbb8aeaSWludzik, Jozef                 v.remove_prefix(1);
611249e9cbSEd Tanous                 stage = ProcessingStage::Hours;
621249e9cbSEd Tanous                 continue;
631249e9cbSEd Tanous             }
641249e9cbSEd Tanous         }
651249e9cbSEd Tanous         uint64_t ticks = 0;
661249e9cbSEd Tanous         auto [ptr, ec] = std::from_chars(v.begin(), v.end(), ticks);
671249e9cbSEd Tanous         if (ec != std::errc())
684dbb8aeaSWludzik, Jozef         {
6962598e31SEd Tanous             BMCWEB_LOG_ERROR("Failed to convert string \"{}\" to decimal", v);
701249e9cbSEd Tanous             return std::nullopt;
711249e9cbSEd Tanous         }
721249e9cbSEd Tanous         size_t charactersRead = static_cast<size_t>(ptr - v.data());
731249e9cbSEd Tanous         if (ptr >= v.end())
741249e9cbSEd Tanous         {
7562598e31SEd Tanous             BMCWEB_LOG_ERROR("Missing postfix");
761249e9cbSEd Tanous             return std::nullopt;
771249e9cbSEd Tanous         }
781249e9cbSEd Tanous         if (*ptr == 'D')
791249e9cbSEd Tanous         {
801249e9cbSEd Tanous             if (stage > ProcessingStage::Days)
811249e9cbSEd Tanous             {
821249e9cbSEd Tanous                 return std::nullopt;
831249e9cbSEd Tanous             }
841249e9cbSEd Tanous             out += std::chrono::days(ticks);
851249e9cbSEd Tanous         }
861249e9cbSEd Tanous         else if (*ptr == 'H')
871249e9cbSEd Tanous         {
881249e9cbSEd Tanous             if (stage > ProcessingStage::Hours)
891249e9cbSEd Tanous             {
901249e9cbSEd Tanous                 return std::nullopt;
911249e9cbSEd Tanous             }
921249e9cbSEd Tanous             out += std::chrono::hours(ticks);
931249e9cbSEd Tanous         }
941249e9cbSEd Tanous         else if (*ptr == 'M')
951249e9cbSEd Tanous         {
961249e9cbSEd Tanous             if (stage > ProcessingStage::Minutes)
971249e9cbSEd Tanous             {
981249e9cbSEd Tanous                 return std::nullopt;
991249e9cbSEd Tanous             }
1001249e9cbSEd Tanous             out += std::chrono::minutes(ticks);
1011249e9cbSEd Tanous         }
1021249e9cbSEd Tanous         else if (*ptr == '.')
1031249e9cbSEd Tanous         {
1041249e9cbSEd Tanous             if (stage > ProcessingStage::Seconds)
1051249e9cbSEd Tanous             {
1061249e9cbSEd Tanous                 return std::nullopt;
1071249e9cbSEd Tanous             }
1081249e9cbSEd Tanous             out += std::chrono::seconds(ticks);
1091249e9cbSEd Tanous             stage = ProcessingStage::Milliseconds;
1101249e9cbSEd Tanous         }
1111249e9cbSEd Tanous         else if (*ptr == 'S')
1121249e9cbSEd Tanous         {
1131249e9cbSEd Tanous             // We could be seeing seconds for the first time, (as is the case in
1141249e9cbSEd Tanous             // 1S) or for the second time (in the case of 1.1S).
1151249e9cbSEd Tanous             if (stage <= ProcessingStage::Seconds)
1161249e9cbSEd Tanous             {
1171249e9cbSEd Tanous                 out += std::chrono::seconds(ticks);
1181249e9cbSEd Tanous                 stage = ProcessingStage::Milliseconds;
1191249e9cbSEd Tanous             }
1201249e9cbSEd Tanous             else if (stage > ProcessingStage::Milliseconds)
1211249e9cbSEd Tanous             {
12262598e31SEd Tanous                 BMCWEB_LOG_ERROR("Got unexpected information at end of parse");
1231249e9cbSEd Tanous                 return std::nullopt;
1241249e9cbSEd Tanous             }
1251249e9cbSEd Tanous             else
1261249e9cbSEd Tanous             {
1271249e9cbSEd Tanous                 // Seconds could be any form of (1S, 1.1S, 1.11S, 1.111S);
1281249e9cbSEd Tanous                 // Handle them all milliseconds are after the decimal point,
1291249e9cbSEd Tanous                 // so they need right padded.
1301249e9cbSEd Tanous                 if (charactersRead == 1)
1311249e9cbSEd Tanous                 {
1321249e9cbSEd Tanous                     ticks *= 100;
1331249e9cbSEd Tanous                 }
1341249e9cbSEd Tanous                 else if (charactersRead == 2)
1351249e9cbSEd Tanous                 {
1361249e9cbSEd Tanous                     ticks *= 10;
1371249e9cbSEd Tanous                 }
1381249e9cbSEd Tanous                 out += std::chrono::milliseconds(ticks);
1391249e9cbSEd Tanous                 stage = ProcessingStage::Milliseconds;
1401249e9cbSEd Tanous             }
1411249e9cbSEd Tanous         }
1421249e9cbSEd Tanous         else
1431249e9cbSEd Tanous         {
14462598e31SEd Tanous             BMCWEB_LOG_ERROR("Unknown postfix {}", *ptr);
1454dbb8aeaSWludzik, Jozef             return std::nullopt;
1464dbb8aeaSWludzik, Jozef         }
1474dbb8aeaSWludzik, Jozef 
1481249e9cbSEd Tanous         v.remove_prefix(charactersRead + 1U);
1494dbb8aeaSWludzik, Jozef     }
1504dbb8aeaSWludzik, Jozef     return out;
1514dbb8aeaSWludzik, Jozef }
1524dbb8aeaSWludzik, Jozef 
1534dbb8aeaSWludzik, Jozef /**
154081ebf06SWludzik, Jozef  * @brief Convert time value into duration format that is based on ISO 8601.
155081ebf06SWludzik, Jozef  *        Example output: "P12DT1M5.5S"
156081ebf06SWludzik, Jozef  *        Ref: Redfish Specification, Section 9.4.4. Duration values
157081ebf06SWludzik, Jozef  */
158b00dcc27SEd Tanous inline std::string toDurationString(std::chrono::milliseconds ms)
159081ebf06SWludzik, Jozef {
160081ebf06SWludzik, Jozef     if (ms < std::chrono::milliseconds::zero())
161081ebf06SWludzik, Jozef     {
162081ebf06SWludzik, Jozef         return "";
163081ebf06SWludzik, Jozef     }
164081ebf06SWludzik, Jozef 
1651249e9cbSEd Tanous     std::chrono::days days = std::chrono::floor<std::chrono::days>(ms);
166081ebf06SWludzik, Jozef     ms -= days;
167081ebf06SWludzik, Jozef 
168081ebf06SWludzik, Jozef     std::chrono::hours hours = std::chrono::floor<std::chrono::hours>(ms);
169081ebf06SWludzik, Jozef     ms -= hours;
170081ebf06SWludzik, Jozef 
171081ebf06SWludzik, Jozef     std::chrono::minutes minutes = std::chrono::floor<std::chrono::minutes>(ms);
172081ebf06SWludzik, Jozef     ms -= minutes;
173081ebf06SWludzik, Jozef 
174081ebf06SWludzik, Jozef     std::chrono::seconds seconds = std::chrono::floor<std::chrono::seconds>(ms);
175081ebf06SWludzik, Jozef     ms -= seconds;
176f653730dSEd Tanous     std::string daysStr;
177081ebf06SWludzik, Jozef     if (days.count() > 0)
178081ebf06SWludzik, Jozef     {
179f653730dSEd Tanous         daysStr = std::format("{}D", days.count());
180081ebf06SWludzik, Jozef     }
181f653730dSEd Tanous     std::string hoursStr;
182081ebf06SWludzik, Jozef     if (hours.count() > 0)
183081ebf06SWludzik, Jozef     {
184f653730dSEd Tanous         hoursStr = std::format("{}H", hours.count());
185081ebf06SWludzik, Jozef     }
186f653730dSEd Tanous     std::string minStr;
187081ebf06SWludzik, Jozef     if (minutes.count() > 0)
188081ebf06SWludzik, Jozef     {
189f653730dSEd Tanous         minStr = std::format("{}M", minutes.count());
190081ebf06SWludzik, Jozef     }
191f653730dSEd Tanous     std::string secStr;
192081ebf06SWludzik, Jozef     if (seconds.count() != 0 || ms.count() != 0)
193081ebf06SWludzik, Jozef     {
194f653730dSEd Tanous         secStr = std::format("{}.{:03}S", seconds.count(), ms.count());
195081ebf06SWludzik, Jozef     }
196081ebf06SWludzik, Jozef 
197f653730dSEd Tanous     return std::format("P{}T{}{}{}", daysStr, hoursStr, minStr, secStr);
198081ebf06SWludzik, Jozef }
199081ebf06SWludzik, Jozef 
2001b7e696bSLukasz Kazmierczak inline std::optional<std::string>
2011b7e696bSLukasz Kazmierczak     toDurationStringFromUint(const uint64_t timeMs)
2021b7e696bSLukasz Kazmierczak {
203f653730dSEd Tanous     constexpr uint64_t maxTimeMs =
2041b7e696bSLukasz Kazmierczak         static_cast<uint64_t>(std::chrono::milliseconds::max().count());
2051b7e696bSLukasz Kazmierczak 
2061b7e696bSLukasz Kazmierczak     if (maxTimeMs < timeMs)
2071b7e696bSLukasz Kazmierczak     {
2081b7e696bSLukasz Kazmierczak         return std::nullopt;
2091b7e696bSLukasz Kazmierczak     }
2101b7e696bSLukasz Kazmierczak 
2111b7e696bSLukasz Kazmierczak     std::string duration = toDurationString(std::chrono::milliseconds(timeMs));
2121b7e696bSLukasz Kazmierczak     if (duration.empty())
2131b7e696bSLukasz Kazmierczak     {
2141b7e696bSLukasz Kazmierczak         return std::nullopt;
2151b7e696bSLukasz Kazmierczak     }
2161b7e696bSLukasz Kazmierczak 
2171b7e696bSLukasz Kazmierczak     return std::make_optional(duration);
2181b7e696bSLukasz Kazmierczak }
2191b7e696bSLukasz Kazmierczak 
2202b82937eSEd Tanous namespace details
2212b82937eSEd Tanous {
2222b82937eSEd Tanous // Returns year/month/day triple in civil calendar
2232b82937eSEd Tanous // Preconditions:  z is number of days since 1970-01-01 and is in the range:
2242b82937eSEd Tanous //                   [numeric_limits<Int>::min(),
2252b82937eSEd Tanous //                   numeric_limits<Int>::max()-719468].
2262b82937eSEd Tanous // Algorithm sourced from
2272b82937eSEd Tanous // https://howardhinnant.github.io/date_algorithms.html#civil_from_days
2282b82937eSEd Tanous // All constants are explained in the above
2292b82937eSEd Tanous template <class IntType>
2302b82937eSEd Tanous constexpr std::tuple<IntType, unsigned, unsigned>
2312b82937eSEd Tanous     civilFromDays(IntType z) noexcept
2322b82937eSEd Tanous {
2332b82937eSEd Tanous     z += 719468;
2342b82937eSEd Tanous     IntType era = (z >= 0 ? z : z - 146096) / 146097;
2352b82937eSEd Tanous     unsigned doe = static_cast<unsigned>(z - era * 146097); // [0, 146096]
23689492a15SPatrick Williams     unsigned yoe = (doe - doe / 1460 + doe / 36524 - doe / 146096) /
23789492a15SPatrick Williams                    365;                                     // [0, 399]
2382b82937eSEd Tanous     IntType y = static_cast<IntType>(yoe) + era * 400;
2392b82937eSEd Tanous     unsigned doy = doe - (365 * yoe + yoe / 4 - yoe / 100); // [0, 365]
2402b82937eSEd Tanous     unsigned mp = (5 * doy + 2) / 153;                      // [0, 11]
2412b82937eSEd Tanous     unsigned d = doy - (153 * mp + 2) / 5 + 1;              // [1, 31]
2422b82937eSEd Tanous     unsigned m = mp < 10 ? mp + 3 : mp - 9;                 // [1, 12]
2432b82937eSEd Tanous 
2442b82937eSEd Tanous     return std::tuple<IntType, unsigned, unsigned>(y + (m <= 2), m, d);
2452b82937eSEd Tanous }
2462b82937eSEd Tanous 
2472b82937eSEd Tanous template <typename IntType, typename Period>
2482b82937eSEd Tanous std::string toISO8061ExtendedStr(std::chrono::duration<IntType, Period> t)
2492b82937eSEd Tanous {
2502b82937eSEd Tanous     using seconds = std::chrono::duration<int>;
2512b82937eSEd Tanous     using minutes = std::chrono::duration<int, std::ratio<60>>;
2522b82937eSEd Tanous     using hours = std::chrono::duration<int, std::ratio<3600>>;
2532b82937eSEd Tanous     using days = std::chrono::duration<
2542b82937eSEd Tanous         IntType, std::ratio_multiply<hours::period, std::ratio<24>>>;
2552b82937eSEd Tanous 
2562b82937eSEd Tanous     // d is days since 1970-01-01
2572b82937eSEd Tanous     days d = std::chrono::duration_cast<days>(t);
2582b82937eSEd Tanous 
2592b82937eSEd Tanous     // t is now time duration since midnight of day d
2602b82937eSEd Tanous     t -= d;
2612b82937eSEd Tanous 
2622b82937eSEd Tanous     // break d down into year/month/day
2632b82937eSEd Tanous     int year = 0;
2642b82937eSEd Tanous     int month = 0;
2652b82937eSEd Tanous     int day = 0;
2662b82937eSEd Tanous     std::tie(year, month, day) = details::civilFromDays(d.count());
2672b82937eSEd Tanous     // Check against limits.  Can't go above year 9999, and can't go below epoch
2682b82937eSEd Tanous     // (1970)
2692b82937eSEd Tanous     if (year >= 10000)
2702b82937eSEd Tanous     {
2712b82937eSEd Tanous         year = 9999;
2722b82937eSEd Tanous         month = 12;
2732b82937eSEd Tanous         day = 31;
2742b82937eSEd Tanous         t = days(1) - std::chrono::duration<IntType, Period>(1);
2752b82937eSEd Tanous     }
2762b82937eSEd Tanous     else if (year < 1970)
2772b82937eSEd Tanous     {
2782b82937eSEd Tanous         year = 1970;
2792b82937eSEd Tanous         month = 1;
2802b82937eSEd Tanous         day = 1;
2812b82937eSEd Tanous         t = std::chrono::duration<IntType, Period>::zero();
2822b82937eSEd Tanous     }
2832b82937eSEd Tanous 
2842b82937eSEd Tanous     hours hr = duration_cast<hours>(t);
2852b82937eSEd Tanous     t -= hr;
2862b82937eSEd Tanous 
2872b82937eSEd Tanous     minutes mt = duration_cast<minutes>(t);
2882b82937eSEd Tanous     t -= mt;
2892b82937eSEd Tanous 
2902b82937eSEd Tanous     seconds se = duration_cast<seconds>(t);
291f653730dSEd Tanous 
2922b82937eSEd Tanous     t -= se;
2932b82937eSEd Tanous 
294f653730dSEd Tanous     std::string subseconds;
2952b82937eSEd Tanous     if constexpr (std::is_same_v<typename decltype(t)::period, std::milli>)
2962b82937eSEd Tanous     {
2972b82937eSEd Tanous         using MilliDuration = std::chrono::duration<int, std::milli>;
2982b82937eSEd Tanous         MilliDuration subsec = duration_cast<MilliDuration>(t);
299f653730dSEd Tanous         subseconds = std::format(".{:03}", subsec.count());
3002b82937eSEd Tanous     }
3012b82937eSEd Tanous     else if constexpr (std::is_same_v<typename decltype(t)::period, std::micro>)
3022b82937eSEd Tanous     {
3032b82937eSEd Tanous         using MicroDuration = std::chrono::duration<int, std::micro>;
3042b82937eSEd Tanous         MicroDuration subsec = duration_cast<MicroDuration>(t);
305f653730dSEd Tanous         subseconds = std::format(".{:06}", subsec.count());
3062b82937eSEd Tanous     }
3072b82937eSEd Tanous 
308f653730dSEd Tanous     return std::format("{:04}-{:02}-{:02}T{:02}:{:02}:{:02}{}+00:00", year,
309f653730dSEd Tanous                        month, day, hr.count(), mt.count(), se.count(),
310f653730dSEd Tanous                        subseconds);
3112b82937eSEd Tanous }
3122b82937eSEd Tanous } // namespace details
3132b82937eSEd Tanous 
3142b82937eSEd Tanous // Returns the formatted date time string.
3152b82937eSEd Tanous // Note that the maximum supported date is 9999-12-31T23:59:59+00:00, if
3162b82937eSEd Tanous // the given |secondsSinceEpoch| is too large, we return the maximum supported
3172b82937eSEd Tanous // date.
3182b82937eSEd Tanous inline std::string getDateTimeUint(uint64_t secondsSinceEpoch)
3192b82937eSEd Tanous {
3202b82937eSEd Tanous     using DurationType = std::chrono::duration<uint64_t>;
3212b82937eSEd Tanous     DurationType sinceEpoch(secondsSinceEpoch);
3222b82937eSEd Tanous     return details::toISO8061ExtendedStr(sinceEpoch);
3232b82937eSEd Tanous }
3242b82937eSEd Tanous 
3252b82937eSEd Tanous // Returns the formatted date time string with millisecond precision
3262b82937eSEd Tanous // Note that the maximum supported date is 9999-12-31T23:59:59+00:00, if
3272b82937eSEd Tanous // the given |secondsSinceEpoch| is too large, we return the maximum supported
3282b82937eSEd Tanous // date.
3292b82937eSEd Tanous inline std::string getDateTimeUintMs(uint64_t milliSecondsSinceEpoch)
3302b82937eSEd Tanous {
3312b82937eSEd Tanous     using DurationType = std::chrono::duration<uint64_t, std::milli>;
3322b82937eSEd Tanous     DurationType sinceEpoch(milliSecondsSinceEpoch);
3332b82937eSEd Tanous     return details::toISO8061ExtendedStr(sinceEpoch);
3342b82937eSEd Tanous }
3352b82937eSEd Tanous 
3362b82937eSEd Tanous // Returns the formatted date time string with microsecond precision
3372b82937eSEd Tanous inline std::string getDateTimeUintUs(uint64_t microSecondsSinceEpoch)
3382b82937eSEd Tanous {
3392b82937eSEd Tanous     using DurationType = std::chrono::duration<uint64_t, std::micro>;
3402b82937eSEd Tanous     DurationType sinceEpoch(microSecondsSinceEpoch);
3412b82937eSEd Tanous     return details::toISO8061ExtendedStr(sinceEpoch);
3422b82937eSEd Tanous }
3432b82937eSEd Tanous 
3442b82937eSEd Tanous inline std::string getDateTimeStdtime(std::time_t secondsSinceEpoch)
3452b82937eSEd Tanous {
3462b82937eSEd Tanous     using DurationType = std::chrono::duration<std::time_t>;
3472b82937eSEd Tanous     DurationType sinceEpoch(secondsSinceEpoch);
3482b82937eSEd Tanous     return details::toISO8061ExtendedStr(sinceEpoch);
3492b82937eSEd Tanous }
3502b82937eSEd Tanous 
3512b82937eSEd Tanous /**
3522b82937eSEd Tanous  * Returns the current Date, Time & the local Time Offset
3538ece0e45SEd Tanous  * information in a pair
3542b82937eSEd Tanous  *
3552b82937eSEd Tanous  * @param[in] None
3562b82937eSEd Tanous  *
3572b82937eSEd Tanous  * @return std::pair<std::string, std::string>, which consist
3582b82937eSEd Tanous  * of current DateTime & the TimeOffset strings respectively.
3592b82937eSEd Tanous  */
3602b82937eSEd Tanous inline std::pair<std::string, std::string> getDateTimeOffsetNow()
3612b82937eSEd Tanous {
3622b82937eSEd Tanous     std::time_t time = std::time(nullptr);
3632b82937eSEd Tanous     std::string dateTime = getDateTimeStdtime(time);
3642b82937eSEd Tanous 
3652b82937eSEd Tanous     /* extract the local Time Offset value from the
3668ece0e45SEd Tanous      * received dateTime string.
3672b82937eSEd Tanous      */
3682b82937eSEd Tanous     std::string timeOffset("Z00:00");
3692b82937eSEd Tanous     std::size_t lastPos = dateTime.size();
3702b82937eSEd Tanous     std::size_t len = timeOffset.size();
3712b82937eSEd Tanous     if (lastPos > len)
3722b82937eSEd Tanous     {
3732b82937eSEd Tanous         timeOffset = dateTime.substr(lastPos - len);
3742b82937eSEd Tanous     }
3752b82937eSEd Tanous 
3762b82937eSEd Tanous     return std::make_pair(dateTime, timeOffset);
3772b82937eSEd Tanous }
3782b82937eSEd Tanous 
379c51afd54SEd Tanous using usSinceEpoch = std::chrono::duration<int64_t, std::micro>;
3801b8b02a4SEd Tanous std::optional<usSinceEpoch> dateStringToEpoch(std::string_view datetime);
381*b5190062SHieu Huynh 
382*b5190062SHieu Huynh /**
383*b5190062SHieu Huynh  * @brief Returns the datetime in ISO 8601 format
384*b5190062SHieu Huynh  *
385*b5190062SHieu Huynh  * @param[in] std::string_view the date of item manufacture in ISO 8601 format,
386*b5190062SHieu Huynh  *            either as YYYYMMDD or YYYYMMDDThhmmssZ
387*b5190062SHieu Huynh  * Ref: https://github.com/openbmc/phosphor-dbus-interfaces/blob/master/yaml/
388*b5190062SHieu Huynh  *      xyz/openbmc_project/Inventory/Decorator/Asset.interface.yaml#L16
389*b5190062SHieu Huynh  *
390*b5190062SHieu Huynh  * @return std::string which consist the datetime
391*b5190062SHieu Huynh  */
392*b5190062SHieu Huynh inline std::optional<std::string> getDateTimeIso8601(std::string_view datetime)
393*b5190062SHieu Huynh {
394*b5190062SHieu Huynh     std::optional<redfish::time_utils::usSinceEpoch> us =
395*b5190062SHieu Huynh         redfish::time_utils::dateStringToEpoch(datetime);
396*b5190062SHieu Huynh     if (!us)
397*b5190062SHieu Huynh     {
398*b5190062SHieu Huynh         return std::nullopt;
399*b5190062SHieu Huynh     }
400*b5190062SHieu Huynh     auto secondsDuration =
401*b5190062SHieu Huynh         std::chrono::duration_cast<std::chrono::seconds>(*us);
402*b5190062SHieu Huynh 
403*b5190062SHieu Huynh     return std::make_optional(redfish::time_utils::getDateTimeUint(
404*b5190062SHieu Huynh         static_cast<uint64_t>(secondsDuration.count())));
405*b5190062SHieu Huynh }
406*b5190062SHieu Huynh 
407*b5190062SHieu Huynh /**
408*b5190062SHieu Huynh  * @brief ProductionDate report
409*b5190062SHieu Huynh  */
410*b5190062SHieu Huynh inline void productionDateReport(crow::Response& res,
411*b5190062SHieu Huynh                                  const std::string& buildDate)
412*b5190062SHieu Huynh {
413*b5190062SHieu Huynh     std::optional<std::string> valueStr =
414*b5190062SHieu Huynh         redfish::time_utils::getDateTimeIso8601(buildDate);
415*b5190062SHieu Huynh     if (!valueStr)
416*b5190062SHieu Huynh     {
417*b5190062SHieu Huynh         messages::internalError();
418*b5190062SHieu Huynh         return;
419*b5190062SHieu Huynh     }
420*b5190062SHieu Huynh     res.jsonValue["ProductionDate"] = *valueStr;
421*b5190062SHieu Huynh }
422*b5190062SHieu Huynh 
423081ebf06SWludzik, Jozef } // namespace time_utils
424081ebf06SWludzik, Jozef } // namespace redfish
425