140e9b92eSEd Tanous // SPDX-License-Identifier: Apache-2.0 240e9b92eSEd Tanous // SPDX-FileCopyrightText: Copyright OpenBMC Authors 31b8b02a4SEd Tanous #include "utils/time_utils.hpp" 41b8b02a4SEd Tanous 5a93e9c77SEd Tanous #include "error_messages.hpp" 6a93e9c77SEd Tanous #include "http_response.hpp" 7a93e9c77SEd Tanous #include "logging.hpp" 8a93e9c77SEd Tanous 967b2e53bSEd Tanous #include <version> 1067b2e53bSEd Tanous 11a8770740SEd Tanous #if __cpp_lib_chrono < 201907L 121b8b02a4SEd Tanous #include "utils/extern/date.h" 13a8770740SEd Tanous #endif 141b8b02a4SEd Tanous #include <array> 15a93e9c77SEd Tanous #include <charconv> 161b8b02a4SEd Tanous #include <chrono> 17a93e9c77SEd Tanous #include <cstddef> 18a93e9c77SEd Tanous #include <cstdint> 19a93e9c77SEd Tanous #include <ctime> 20a93e9c77SEd Tanous #include <format> 211b8b02a4SEd Tanous #include <optional> 22a93e9c77SEd Tanous #include <ratio> 231b8b02a4SEd Tanous #include <sstream> 241b8b02a4SEd Tanous #include <string> 251b8b02a4SEd Tanous #include <string_view> 26a93e9c77SEd Tanous #include <system_error> 27a93e9c77SEd Tanous #include <utility> 281b8b02a4SEd Tanous 291b8b02a4SEd Tanous namespace redfish::time_utils 301b8b02a4SEd Tanous { 31c51afd54SEd Tanous 32a93e9c77SEd Tanous /** 33a93e9c77SEd Tanous * @brief Convert string that represents value in Duration Format to its numeric 34a93e9c77SEd Tanous * equivalent. 35a93e9c77SEd Tanous */ 36a93e9c77SEd Tanous std::optional<std::chrono::milliseconds> fromDurationString(std::string_view v) 37a93e9c77SEd Tanous { 38a93e9c77SEd Tanous std::chrono::milliseconds out = std::chrono::milliseconds::zero(); 39a93e9c77SEd Tanous enum class ProcessingStage 40a93e9c77SEd Tanous { 41a93e9c77SEd Tanous // P1DT1H1M1.100S 42a93e9c77SEd Tanous P, 43a93e9c77SEd Tanous Days, 44a93e9c77SEd Tanous Hours, 45a93e9c77SEd Tanous Minutes, 46a93e9c77SEd Tanous Seconds, 47a93e9c77SEd Tanous Milliseconds, 48a93e9c77SEd Tanous Done, 49a93e9c77SEd Tanous }; 50a93e9c77SEd Tanous ProcessingStage stage = ProcessingStage::P; 51a93e9c77SEd Tanous 52a93e9c77SEd Tanous while (!v.empty()) 53a93e9c77SEd Tanous { 54a93e9c77SEd Tanous if (stage == ProcessingStage::P) 55a93e9c77SEd Tanous { 56a93e9c77SEd Tanous if (v.front() != 'P') 57a93e9c77SEd Tanous { 58a93e9c77SEd Tanous return std::nullopt; 59a93e9c77SEd Tanous } 60a93e9c77SEd Tanous v.remove_prefix(1); 61a93e9c77SEd Tanous stage = ProcessingStage::Days; 62a93e9c77SEd Tanous continue; 63a93e9c77SEd Tanous } 64a93e9c77SEd Tanous if (stage == ProcessingStage::Days) 65a93e9c77SEd Tanous { 66a93e9c77SEd Tanous if (v.front() == 'T') 67a93e9c77SEd Tanous { 68a93e9c77SEd Tanous v.remove_prefix(1); 69a93e9c77SEd Tanous stage = ProcessingStage::Hours; 70a93e9c77SEd Tanous continue; 71a93e9c77SEd Tanous } 72a93e9c77SEd Tanous } 73a93e9c77SEd Tanous uint64_t ticks = 0; 74a93e9c77SEd Tanous auto [ptr, ec] = std::from_chars(v.begin(), v.end(), ticks); 75a93e9c77SEd Tanous if (ec != std::errc()) 76a93e9c77SEd Tanous { 77a93e9c77SEd Tanous BMCWEB_LOG_ERROR("Failed to convert string \"{}\" to decimal", v); 78a93e9c77SEd Tanous return std::nullopt; 79a93e9c77SEd Tanous } 80a93e9c77SEd Tanous size_t charactersRead = static_cast<size_t>(ptr - v.data()); 81a93e9c77SEd Tanous if (ptr >= v.end()) 82a93e9c77SEd Tanous { 83a93e9c77SEd Tanous BMCWEB_LOG_ERROR("Missing postfix"); 84a93e9c77SEd Tanous return std::nullopt; 85a93e9c77SEd Tanous } 86a93e9c77SEd Tanous if (*ptr == 'D') 87a93e9c77SEd Tanous { 88a93e9c77SEd Tanous if (stage > ProcessingStage::Days) 89a93e9c77SEd Tanous { 90a93e9c77SEd Tanous return std::nullopt; 91a93e9c77SEd Tanous } 92a93e9c77SEd Tanous out += std::chrono::days(ticks); 93a93e9c77SEd Tanous } 94a93e9c77SEd Tanous else if (*ptr == 'H') 95a93e9c77SEd Tanous { 96a93e9c77SEd Tanous if (stage > ProcessingStage::Hours) 97a93e9c77SEd Tanous { 98a93e9c77SEd Tanous return std::nullopt; 99a93e9c77SEd Tanous } 100a93e9c77SEd Tanous out += std::chrono::hours(ticks); 101a93e9c77SEd Tanous } 102a93e9c77SEd Tanous else if (*ptr == 'M') 103a93e9c77SEd Tanous { 104a93e9c77SEd Tanous if (stage > ProcessingStage::Minutes) 105a93e9c77SEd Tanous { 106a93e9c77SEd Tanous return std::nullopt; 107a93e9c77SEd Tanous } 108a93e9c77SEd Tanous out += std::chrono::minutes(ticks); 109a93e9c77SEd Tanous } 110a93e9c77SEd Tanous else if (*ptr == '.') 111a93e9c77SEd Tanous { 112a93e9c77SEd Tanous if (stage > ProcessingStage::Seconds) 113a93e9c77SEd Tanous { 114a93e9c77SEd Tanous return std::nullopt; 115a93e9c77SEd Tanous } 116a93e9c77SEd Tanous out += std::chrono::seconds(ticks); 117a93e9c77SEd Tanous stage = ProcessingStage::Milliseconds; 118a93e9c77SEd Tanous } 119a93e9c77SEd Tanous else if (*ptr == 'S') 120a93e9c77SEd Tanous { 121a93e9c77SEd Tanous // We could be seeing seconds for the first time, (as is the case in 122a93e9c77SEd Tanous // 1S) or for the second time (in the case of 1.1S). 123a93e9c77SEd Tanous if (stage <= ProcessingStage::Seconds) 124a93e9c77SEd Tanous { 125a93e9c77SEd Tanous out += std::chrono::seconds(ticks); 126a93e9c77SEd Tanous stage = ProcessingStage::Milliseconds; 127a93e9c77SEd Tanous } 128a93e9c77SEd Tanous else if (stage > ProcessingStage::Milliseconds) 129a93e9c77SEd Tanous { 130a93e9c77SEd Tanous BMCWEB_LOG_ERROR("Got unexpected information at end of parse"); 131a93e9c77SEd Tanous return std::nullopt; 132a93e9c77SEd Tanous } 133a93e9c77SEd Tanous else 134a93e9c77SEd Tanous { 135a93e9c77SEd Tanous // Seconds could be any form of (1S, 1.1S, 1.11S, 1.111S); 136a93e9c77SEd Tanous // Handle them all milliseconds are after the decimal point, 137a93e9c77SEd Tanous // so they need right padded. 138a93e9c77SEd Tanous if (charactersRead == 1) 139a93e9c77SEd Tanous { 140a93e9c77SEd Tanous ticks *= 100; 141a93e9c77SEd Tanous } 142a93e9c77SEd Tanous else if (charactersRead == 2) 143a93e9c77SEd Tanous { 144a93e9c77SEd Tanous ticks *= 10; 145a93e9c77SEd Tanous } 146a93e9c77SEd Tanous out += std::chrono::milliseconds(ticks); 147a93e9c77SEd Tanous stage = ProcessingStage::Milliseconds; 148a93e9c77SEd Tanous } 149a93e9c77SEd Tanous } 150a93e9c77SEd Tanous else 151a93e9c77SEd Tanous { 152a93e9c77SEd Tanous BMCWEB_LOG_ERROR("Unknown postfix {}", *ptr); 153a93e9c77SEd Tanous return std::nullopt; 154a93e9c77SEd Tanous } 155a93e9c77SEd Tanous 156a93e9c77SEd Tanous v.remove_prefix(charactersRead + 1U); 157a93e9c77SEd Tanous } 158a93e9c77SEd Tanous return out; 159a93e9c77SEd Tanous } 160a93e9c77SEd Tanous 161a93e9c77SEd Tanous /** 162a93e9c77SEd Tanous * @brief Convert time value into duration format that is based on ISO 8601. 163a93e9c77SEd Tanous * Example output: "P12DT1M5.5S" 164a93e9c77SEd Tanous * Ref: Redfish Specification, Section 9.4.4. Duration values 165a93e9c77SEd Tanous */ 166a93e9c77SEd Tanous std::string toDurationString(std::chrono::milliseconds ms) 167a93e9c77SEd Tanous { 168a93e9c77SEd Tanous if (ms < std::chrono::milliseconds::zero()) 169a93e9c77SEd Tanous { 170a93e9c77SEd Tanous return ""; 171a93e9c77SEd Tanous } 172a93e9c77SEd Tanous 173a93e9c77SEd Tanous std::chrono::days days = std::chrono::floor<std::chrono::days>(ms); 174a93e9c77SEd Tanous ms -= days; 175a93e9c77SEd Tanous 176a93e9c77SEd Tanous std::chrono::hours hours = std::chrono::floor<std::chrono::hours>(ms); 177a93e9c77SEd Tanous ms -= hours; 178a93e9c77SEd Tanous 179a93e9c77SEd Tanous std::chrono::minutes minutes = std::chrono::floor<std::chrono::minutes>(ms); 180a93e9c77SEd Tanous ms -= minutes; 181a93e9c77SEd Tanous 182a93e9c77SEd Tanous std::chrono::seconds seconds = std::chrono::floor<std::chrono::seconds>(ms); 183a93e9c77SEd Tanous ms -= seconds; 184a93e9c77SEd Tanous std::string daysStr; 185a93e9c77SEd Tanous if (days.count() > 0) 186a93e9c77SEd Tanous { 187a93e9c77SEd Tanous daysStr = std::format("{}D", days.count()); 188a93e9c77SEd Tanous } 189a93e9c77SEd Tanous std::string hoursStr; 190a93e9c77SEd Tanous if (hours.count() > 0) 191a93e9c77SEd Tanous { 192a93e9c77SEd Tanous hoursStr = std::format("{}H", hours.count()); 193a93e9c77SEd Tanous } 194a93e9c77SEd Tanous std::string minStr; 195a93e9c77SEd Tanous if (minutes.count() > 0) 196a93e9c77SEd Tanous { 197a93e9c77SEd Tanous minStr = std::format("{}M", minutes.count()); 198a93e9c77SEd Tanous } 199a93e9c77SEd Tanous std::string secStr; 200a93e9c77SEd Tanous if (seconds.count() != 0 || ms.count() != 0) 201a93e9c77SEd Tanous { 202a93e9c77SEd Tanous secStr = std::format("{}.{:03}S", seconds.count(), ms.count()); 203a93e9c77SEd Tanous } 204a93e9c77SEd Tanous 205a93e9c77SEd Tanous return std::format("P{}T{}{}{}", daysStr, hoursStr, minStr, secStr); 206a93e9c77SEd Tanous } 207a93e9c77SEd Tanous 208a93e9c77SEd Tanous std::optional<std::string> toDurationStringFromUint(uint64_t timeMs) 209a93e9c77SEd Tanous { 210a93e9c77SEd Tanous constexpr uint64_t maxTimeMs = 211a93e9c77SEd Tanous static_cast<uint64_t>(std::chrono::milliseconds::max().count()); 212a93e9c77SEd Tanous 213a93e9c77SEd Tanous if (maxTimeMs < timeMs) 214a93e9c77SEd Tanous { 215a93e9c77SEd Tanous return std::nullopt; 216a93e9c77SEd Tanous } 217a93e9c77SEd Tanous 218a93e9c77SEd Tanous std::string duration = toDurationString(std::chrono::milliseconds(timeMs)); 219a93e9c77SEd Tanous if (duration.empty()) 220a93e9c77SEd Tanous { 221a93e9c77SEd Tanous return std::nullopt; 222a93e9c77SEd Tanous } 223a93e9c77SEd Tanous 224a93e9c77SEd Tanous return std::make_optional(duration); 225a93e9c77SEd Tanous } 226a93e9c77SEd Tanous 227a93e9c77SEd Tanous namespace details 228a93e9c77SEd Tanous { 229a93e9c77SEd Tanous // This code is left for support of gcc < 13 which didn't have support for 230a93e9c77SEd Tanous // timezones. It should be removed at some point in the future. 231a93e9c77SEd Tanous #if __cpp_lib_chrono < 201907L 232a93e9c77SEd Tanous 233a93e9c77SEd Tanous // Returns year/month/day triple in civil calendar 234a93e9c77SEd Tanous // Preconditions: z is number of days since 1970-01-01 and is in the range: 235a93e9c77SEd Tanous // [numeric_limits<Int>::min(), 236a93e9c77SEd Tanous // numeric_limits<Int>::max()-719468]. 237a93e9c77SEd Tanous // Algorithm sourced from 238a93e9c77SEd Tanous // https://howardhinnant.github.io/date_algorithms.html#civil_from_days 239a93e9c77SEd Tanous // All constants are explained in the above 240a93e9c77SEd Tanous template <class IntType> 241*504af5a0SPatrick Williams constexpr std::tuple<IntType, unsigned, unsigned> civilFromDays( 242*504af5a0SPatrick Williams IntType z) noexcept 243a93e9c77SEd Tanous { 244a93e9c77SEd Tanous z += 719468; 245a93e9c77SEd Tanous IntType era = (z >= 0 ? z : z - 146096) / 146097; 246a93e9c77SEd Tanous unsigned doe = static_cast<unsigned>(z - era * 146097); // [0, 146096] 247a93e9c77SEd Tanous unsigned yoe = (doe - doe / 1460 + doe / 36524 - doe / 146096) / 248a93e9c77SEd Tanous 365; // [0, 399] 249a93e9c77SEd Tanous IntType y = static_cast<IntType>(yoe) + era * 400; 250a93e9c77SEd Tanous unsigned doy = doe - (365 * yoe + yoe / 4 - yoe / 100); // [0, 365] 251a93e9c77SEd Tanous unsigned mp = (5 * doy + 2) / 153; // [0, 11] 252a93e9c77SEd Tanous unsigned d = doy - (153 * mp + 2) / 5 + 1; // [1, 31] 253a93e9c77SEd Tanous unsigned m = mp < 10 ? mp + 3 : mp - 9; // [1, 12] 254a93e9c77SEd Tanous 255a93e9c77SEd Tanous return std::tuple<IntType, unsigned, unsigned>(y + (m <= 2), m, d); 256a93e9c77SEd Tanous } 257a93e9c77SEd Tanous 258a93e9c77SEd Tanous template <typename IntType, typename Period> 259a93e9c77SEd Tanous std::string toISO8061ExtendedStr(std::chrono::duration<IntType, Period> t) 260a93e9c77SEd Tanous { 261a93e9c77SEd Tanous using seconds = std::chrono::duration<int>; 262a93e9c77SEd Tanous using minutes = std::chrono::duration<int, std::ratio<60>>; 263a93e9c77SEd Tanous using hours = std::chrono::duration<int, std::ratio<3600>>; 264a93e9c77SEd Tanous using days = std::chrono::duration< 265a93e9c77SEd Tanous IntType, std::ratio_multiply<hours::period, std::ratio<24>>>; 266a93e9c77SEd Tanous 267a93e9c77SEd Tanous // d is days since 1970-01-01 268a93e9c77SEd Tanous days d = std::chrono::duration_cast<days>(t); 269a93e9c77SEd Tanous 270a93e9c77SEd Tanous // t is now time duration since midnight of day d 271a93e9c77SEd Tanous t -= d; 272a93e9c77SEd Tanous 273a93e9c77SEd Tanous // break d down into year/month/day 274a93e9c77SEd Tanous int year = 0; 275a93e9c77SEd Tanous int month = 0; 276a93e9c77SEd Tanous int day = 0; 277a93e9c77SEd Tanous std::tie(year, month, day) = details::civilFromDays(d.count()); 278a93e9c77SEd Tanous // Check against limits. Can't go above year 9999, and can't go below epoch 279a93e9c77SEd Tanous // (1970) 280a93e9c77SEd Tanous if (year >= 10000) 281a93e9c77SEd Tanous { 282a93e9c77SEd Tanous year = 9999; 283a93e9c77SEd Tanous month = 12; 284a93e9c77SEd Tanous day = 31; 285a93e9c77SEd Tanous t = days(1) - std::chrono::duration<IntType, Period>(1); 286a93e9c77SEd Tanous } 287a93e9c77SEd Tanous else if (year < 1970) 288a93e9c77SEd Tanous { 289a93e9c77SEd Tanous year = 1970; 290a93e9c77SEd Tanous month = 1; 291a93e9c77SEd Tanous day = 1; 292a93e9c77SEd Tanous t = std::chrono::duration<IntType, Period>::zero(); 293a93e9c77SEd Tanous } 294a93e9c77SEd Tanous 295a93e9c77SEd Tanous hours hr = std::chrono::duration_cast<hours>(t); 296a93e9c77SEd Tanous t -= hr; 297a93e9c77SEd Tanous 298a93e9c77SEd Tanous minutes mt = std::chrono::duration_cast<minutes>(t); 299a93e9c77SEd Tanous t -= mt; 300a93e9c77SEd Tanous 301a93e9c77SEd Tanous seconds se = std::chrono::duration_cast<seconds>(t); 302a93e9c77SEd Tanous 303a93e9c77SEd Tanous t -= se; 304a93e9c77SEd Tanous 305a93e9c77SEd Tanous std::string subseconds; 306a93e9c77SEd Tanous if constexpr (std::is_same_v<typename decltype(t)::period, std::milli>) 307a93e9c77SEd Tanous { 308a93e9c77SEd Tanous using MilliDuration = std::chrono::duration<int, std::milli>; 309a93e9c77SEd Tanous MilliDuration subsec = std::chrono::duration_cast<MilliDuration>(t); 310a93e9c77SEd Tanous subseconds = std::format(".{:03}", subsec.count()); 311a93e9c77SEd Tanous } 312a93e9c77SEd Tanous else if constexpr (std::is_same_v<typename decltype(t)::period, std::micro>) 313a93e9c77SEd Tanous { 314a93e9c77SEd Tanous using MicroDuration = std::chrono::duration<int, std::micro>; 315a93e9c77SEd Tanous MicroDuration subsec = std::chrono::duration_cast<MicroDuration>(t); 316a93e9c77SEd Tanous subseconds = std::format(".{:06}", subsec.count()); 317a93e9c77SEd Tanous } 318a93e9c77SEd Tanous 319a93e9c77SEd Tanous return std::format("{:04}-{:02}-{:02}T{:02}:{:02}:{:02}{}+00:00", year, 320a93e9c77SEd Tanous month, day, hr.count(), mt.count(), se.count(), 321a93e9c77SEd Tanous subseconds); 322a93e9c77SEd Tanous } 323a93e9c77SEd Tanous 324a93e9c77SEd Tanous #else 325a93e9c77SEd Tanous 326a93e9c77SEd Tanous template <typename IntType, typename Period> 327a93e9c77SEd Tanous 328a93e9c77SEd Tanous std::string toISO8061ExtendedStr(std::chrono::duration<IntType, Period> dur) 329a93e9c77SEd Tanous { 330a93e9c77SEd Tanous using namespace std::literals::chrono_literals; 331a93e9c77SEd Tanous 332a93e9c77SEd Tanous using SubType = std::chrono::duration<IntType, Period>; 333a93e9c77SEd Tanous 334a93e9c77SEd Tanous // d is days since 1970-01-01 335a93e9c77SEd Tanous std::chrono::days days = std::chrono::floor<std::chrono::days>(dur); 336a93e9c77SEd Tanous std::chrono::sys_days sysDays(days); 337a93e9c77SEd Tanous std::chrono::year_month_day ymd(sysDays); 338a93e9c77SEd Tanous 339a93e9c77SEd Tanous // Enforce 3 constraints 340a93e9c77SEd Tanous // the result cant under or overflow the calculation 341a93e9c77SEd Tanous // the resulting string needs to be representable as 4 digits 342a93e9c77SEd Tanous // The resulting string can't be before epoch 343a93e9c77SEd Tanous if (dur.count() <= 0) 344a93e9c77SEd Tanous { 345a93e9c77SEd Tanous BMCWEB_LOG_WARNING("Underflow from value {}", dur.count()); 346a93e9c77SEd Tanous ymd = 1970y / std::chrono::January / 1d; 347a93e9c77SEd Tanous dur = std::chrono::duration<IntType, Period>::zero(); 348a93e9c77SEd Tanous } 349a93e9c77SEd Tanous else if (dur > SubType::max() - std::chrono::days(1)) 350a93e9c77SEd Tanous { 351a93e9c77SEd Tanous BMCWEB_LOG_WARNING("Overflow from value {}", dur.count()); 352a93e9c77SEd Tanous ymd = 9999y / std::chrono::December / 31d; 353a93e9c77SEd Tanous dur = std::chrono::days(1) - SubType(1); 354a93e9c77SEd Tanous } 355a93e9c77SEd Tanous else if (ymd.year() >= 10000y) 356a93e9c77SEd Tanous { 357a93e9c77SEd Tanous BMCWEB_LOG_WARNING("Year {} not representable", ymd.year()); 358a93e9c77SEd Tanous ymd = 9999y / std::chrono::December / 31d; 359a93e9c77SEd Tanous dur = std::chrono::days(1) - SubType(1); 360a93e9c77SEd Tanous } 361a93e9c77SEd Tanous else if (ymd.year() < 1970y) 362a93e9c77SEd Tanous { 363a93e9c77SEd Tanous BMCWEB_LOG_WARNING("Year {} not representable", ymd.year()); 364a93e9c77SEd Tanous ymd = 1970y / std::chrono::January / 1d; 365a93e9c77SEd Tanous dur = SubType::zero(); 366a93e9c77SEd Tanous } 367a93e9c77SEd Tanous else 368a93e9c77SEd Tanous { 369a93e9c77SEd Tanous // t is now time duration since midnight of day d 370a93e9c77SEd Tanous dur -= days; 371a93e9c77SEd Tanous } 372a93e9c77SEd Tanous std::chrono::hh_mm_ss<SubType> hms(dur); 373a93e9c77SEd Tanous 374a93e9c77SEd Tanous return std::format("{}T{}+00:00", ymd, hms); 375a93e9c77SEd Tanous } 376a93e9c77SEd Tanous 377a93e9c77SEd Tanous #endif 378a93e9c77SEd Tanous } // namespace details 379a93e9c77SEd Tanous 380a93e9c77SEd Tanous // Returns the formatted date time string. 381a93e9c77SEd Tanous // Note that the maximum supported date is 9999-12-31T23:59:59+00:00, if 382a93e9c77SEd Tanous // the given |secondsSinceEpoch| is too large, we return the maximum supported 383a93e9c77SEd Tanous // date. 384a93e9c77SEd Tanous std::string getDateTimeUint(uint64_t secondsSinceEpoch) 385a93e9c77SEd Tanous { 386a93e9c77SEd Tanous using DurationType = std::chrono::duration<uint64_t>; 387a93e9c77SEd Tanous DurationType sinceEpoch(secondsSinceEpoch); 388a93e9c77SEd Tanous return details::toISO8061ExtendedStr(sinceEpoch); 389a93e9c77SEd Tanous } 390a93e9c77SEd Tanous 391a93e9c77SEd Tanous // Returns the formatted date time string with millisecond precision 392a93e9c77SEd Tanous // Note that the maximum supported date is 9999-12-31T23:59:59+00:00, if 393a93e9c77SEd Tanous // the given |secondsSinceEpoch| is too large, we return the maximum supported 394a93e9c77SEd Tanous // date. 395a93e9c77SEd Tanous std::string getDateTimeUintMs(uint64_t milliSecondsSinceEpoch) 396a93e9c77SEd Tanous { 397a93e9c77SEd Tanous using DurationType = std::chrono::duration<uint64_t, std::milli>; 398a93e9c77SEd Tanous DurationType sinceEpoch(milliSecondsSinceEpoch); 399a93e9c77SEd Tanous return details::toISO8061ExtendedStr(sinceEpoch); 400a93e9c77SEd Tanous } 401a93e9c77SEd Tanous 402a93e9c77SEd Tanous // Returns the formatted date time string with microsecond precision 403a93e9c77SEd Tanous std::string getDateTimeUintUs(uint64_t microSecondsSinceEpoch) 404a93e9c77SEd Tanous { 405a93e9c77SEd Tanous using DurationType = std::chrono::duration<uint64_t, std::micro>; 406a93e9c77SEd Tanous DurationType sinceEpoch(microSecondsSinceEpoch); 407a93e9c77SEd Tanous return details::toISO8061ExtendedStr(sinceEpoch); 408a93e9c77SEd Tanous } 409a93e9c77SEd Tanous 410a93e9c77SEd Tanous std::string getDateTimeStdtime(std::time_t secondsSinceEpoch) 411a93e9c77SEd Tanous { 412a93e9c77SEd Tanous using DurationType = std::chrono::duration<std::time_t>; 413a93e9c77SEd Tanous DurationType sinceEpoch(secondsSinceEpoch); 414a93e9c77SEd Tanous return details::toISO8061ExtendedStr(sinceEpoch); 415a93e9c77SEd Tanous } 416a93e9c77SEd Tanous 417a93e9c77SEd Tanous /** 418a93e9c77SEd Tanous * Returns the current Date, Time & the local Time Offset 419a93e9c77SEd Tanous * information in a pair 420a93e9c77SEd Tanous * 421a93e9c77SEd Tanous * @param[in] None 422a93e9c77SEd Tanous * 423a93e9c77SEd Tanous * @return std::pair<std::string, std::string>, which consist 424a93e9c77SEd Tanous * of current DateTime & the TimeOffset strings respectively. 425a93e9c77SEd Tanous */ 426a93e9c77SEd Tanous std::pair<std::string, std::string> getDateTimeOffsetNow() 427a93e9c77SEd Tanous { 428a93e9c77SEd Tanous std::time_t time = std::time(nullptr); 429a93e9c77SEd Tanous std::string dateTime = getDateTimeStdtime(time); 430a93e9c77SEd Tanous 431a93e9c77SEd Tanous /* extract the local Time Offset value from the 432a93e9c77SEd Tanous * received dateTime string. 433a93e9c77SEd Tanous */ 434a93e9c77SEd Tanous std::string timeOffset("Z00:00"); 435a93e9c77SEd Tanous std::size_t lastPos = dateTime.size(); 436a93e9c77SEd Tanous std::size_t len = timeOffset.size(); 437a93e9c77SEd Tanous if (lastPos > len) 438a93e9c77SEd Tanous { 439a93e9c77SEd Tanous timeOffset = dateTime.substr(lastPos - len); 440a93e9c77SEd Tanous } 441a93e9c77SEd Tanous 442a93e9c77SEd Tanous return std::make_pair(dateTime, timeOffset); 443a93e9c77SEd Tanous } 444a93e9c77SEd Tanous 445a93e9c77SEd Tanous using usSinceEpoch = std::chrono::duration<int64_t, std::micro>; 446a93e9c77SEd Tanous 447a93e9c77SEd Tanous /** 448a93e9c77SEd Tanous * @brief Returns the datetime in ISO 8601 format 449a93e9c77SEd Tanous * 450a93e9c77SEd Tanous * @param[in] std::string_view the date of item manufacture in ISO 8601 format, 451a93e9c77SEd Tanous * either as YYYYMMDD or YYYYMMDDThhmmssZ 452a93e9c77SEd Tanous * Ref: https://github.com/openbmc/phosphor-dbus-interfaces/blob/master/yaml/ 453a93e9c77SEd Tanous * xyz/openbmc_project/Inventory/Decorator/Asset.interface.yaml#L16 454a93e9c77SEd Tanous * 455a93e9c77SEd Tanous * @return std::string which consist the datetime 456a93e9c77SEd Tanous */ 457a93e9c77SEd Tanous std::optional<std::string> getDateTimeIso8601(std::string_view datetime) 458a93e9c77SEd Tanous { 459a93e9c77SEd Tanous std::optional<usSinceEpoch> us = dateStringToEpoch(datetime); 460a93e9c77SEd Tanous if (!us) 461a93e9c77SEd Tanous { 462a93e9c77SEd Tanous return std::nullopt; 463a93e9c77SEd Tanous } 464a93e9c77SEd Tanous auto secondsDuration = 465a93e9c77SEd Tanous std::chrono::duration_cast<std::chrono::seconds>(*us); 466a93e9c77SEd Tanous 467a93e9c77SEd Tanous return std::make_optional( 468a93e9c77SEd Tanous getDateTimeUint(static_cast<uint64_t>(secondsDuration.count()))); 469a93e9c77SEd Tanous } 470a93e9c77SEd Tanous 471a93e9c77SEd Tanous /** 472a93e9c77SEd Tanous * @brief ProductionDate report 473a93e9c77SEd Tanous */ 474a93e9c77SEd Tanous void productionDateReport(crow::Response& res, const std::string& buildDate) 475a93e9c77SEd Tanous { 476a93e9c77SEd Tanous std::optional<std::string> valueStr = getDateTimeIso8601(buildDate); 477a93e9c77SEd Tanous if (!valueStr) 478a93e9c77SEd Tanous { 479a93e9c77SEd Tanous messages::internalError(); 480a93e9c77SEd Tanous return; 481a93e9c77SEd Tanous } 482a93e9c77SEd Tanous res.jsonValue["ProductionDate"] = *valueStr; 483a93e9c77SEd Tanous } 484a93e9c77SEd Tanous 4851b8b02a4SEd Tanous std::optional<usSinceEpoch> dateStringToEpoch(std::string_view datetime) 4861b8b02a4SEd Tanous { 487352e3b78SHieu Huynh for (const char* format : std::to_array( 488352e3b78SHieu Huynh {"%FT%T%Ez", "%FT%TZ", "%FT%T", "%Y%m%d", "%Y%m%dT%H%M%SZ"})) 4891b8b02a4SEd Tanous { 4901b8b02a4SEd Tanous // Parse using signed so we can detect negative dates 491c51afd54SEd Tanous std::chrono::sys_time<usSinceEpoch> date; 4921b8b02a4SEd Tanous std::istringstream iss(std::string{datetime}); 4931b8b02a4SEd Tanous #if __cpp_lib_chrono >= 201907L 4941b8b02a4SEd Tanous namespace chrono_from_stream = std::chrono; 4951b8b02a4SEd Tanous #else 4961b8b02a4SEd Tanous namespace chrono_from_stream = date; 4971b8b02a4SEd Tanous #endif 4981b8b02a4SEd Tanous if (chrono_from_stream::from_stream(iss, format, date)) 4991b8b02a4SEd Tanous { 5001b8b02a4SEd Tanous if (date.time_since_epoch().count() < 0) 5011b8b02a4SEd Tanous { 5021b8b02a4SEd Tanous return std::nullopt; 5031b8b02a4SEd Tanous } 5041b8b02a4SEd Tanous if (iss.rdbuf()->in_avail() != 0) 5051b8b02a4SEd Tanous { 5061b8b02a4SEd Tanous // More information left at end of string. 5071b8b02a4SEd Tanous continue; 5081b8b02a4SEd Tanous } 509c51afd54SEd Tanous return date.time_since_epoch(); 5101b8b02a4SEd Tanous } 5111b8b02a4SEd Tanous } 5121b8b02a4SEd Tanous return std::nullopt; 5131b8b02a4SEd Tanous } 5141b8b02a4SEd Tanous } // namespace redfish::time_utils 515