1081ebf06SWludzik, Jozef #pragma once 2081ebf06SWludzik, Jozef 34dbb8aeaSWludzik, Jozef #include "logging.hpp" 44dbb8aeaSWludzik, Jozef 54dbb8aeaSWludzik, Jozef #include <charconv> 6081ebf06SWludzik, Jozef #include <chrono> 74dbb8aeaSWludzik, Jozef #include <cmath> 84dbb8aeaSWludzik, Jozef #include <optional> 9081ebf06SWludzik, Jozef #include <string> 104dbb8aeaSWludzik, Jozef #include <system_error> 11081ebf06SWludzik, Jozef 12081ebf06SWludzik, Jozef namespace redfish 13081ebf06SWludzik, Jozef { 14081ebf06SWludzik, Jozef 15081ebf06SWludzik, Jozef namespace time_utils 16081ebf06SWludzik, Jozef { 17081ebf06SWludzik, Jozef 18081ebf06SWludzik, Jozef namespace details 19081ebf06SWludzik, Jozef { 20081ebf06SWludzik, Jozef 216de264ccSEd Tanous constexpr intmax_t dayDuration = static_cast<intmax_t>(24 * 60 * 60); 226de264ccSEd Tanous using Days = std::chrono::duration<long long, std::ratio<dayDuration>>; 234dbb8aeaSWludzik, Jozef 24081ebf06SWludzik, Jozef inline void leftZeroPadding(std::string& str, const std::size_t padding) 25081ebf06SWludzik, Jozef { 26081ebf06SWludzik, Jozef if (str.size() < padding) 27081ebf06SWludzik, Jozef { 28081ebf06SWludzik, Jozef str.insert(0, padding - str.size(), '0'); 29081ebf06SWludzik, Jozef } 30081ebf06SWludzik, Jozef } 314dbb8aeaSWludzik, Jozef 324dbb8aeaSWludzik, Jozef template <typename FromTime> 334dbb8aeaSWludzik, Jozef bool fromDurationItem(std::string_view& fmt, const char postfix, 344dbb8aeaSWludzik, Jozef std::chrono::milliseconds& out) 354dbb8aeaSWludzik, Jozef { 364dbb8aeaSWludzik, Jozef const size_t pos = fmt.find(postfix); 374dbb8aeaSWludzik, Jozef if (pos == std::string::npos) 384dbb8aeaSWludzik, Jozef { 394dbb8aeaSWludzik, Jozef return true; 404dbb8aeaSWludzik, Jozef } 414dbb8aeaSWludzik, Jozef if ((pos + 1U) > fmt.size()) 424dbb8aeaSWludzik, Jozef { 434dbb8aeaSWludzik, Jozef return false; 444dbb8aeaSWludzik, Jozef } 454dbb8aeaSWludzik, Jozef 46*543f4400SEd Tanous const char* end = nullptr; 474dbb8aeaSWludzik, Jozef std::chrono::milliseconds::rep ticks = 0; 484dbb8aeaSWludzik, Jozef if constexpr (std::is_same_v<FromTime, std::chrono::milliseconds>) 494dbb8aeaSWludzik, Jozef { 504dbb8aeaSWludzik, Jozef end = fmt.data() + std::min<size_t>(pos, 3U); 514dbb8aeaSWludzik, Jozef } 524dbb8aeaSWludzik, Jozef else 534dbb8aeaSWludzik, Jozef { 544dbb8aeaSWludzik, Jozef end = fmt.data() + pos; 554dbb8aeaSWludzik, Jozef } 564dbb8aeaSWludzik, Jozef 574dbb8aeaSWludzik, Jozef auto [ptr, ec] = std::from_chars(fmt.data(), end, ticks); 584dbb8aeaSWludzik, Jozef if (ptr != end || ec != std::errc()) 594dbb8aeaSWludzik, Jozef { 604dbb8aeaSWludzik, Jozef BMCWEB_LOG_ERROR << "Failed to convert string to decimal with err: " 614dbb8aeaSWludzik, Jozef << static_cast<int>(ec) << "(" 624dbb8aeaSWludzik, Jozef << std::make_error_code(ec).message() << "), ptr{" 634dbb8aeaSWludzik, Jozef << static_cast<const void*>(ptr) << "} != end{" 644dbb8aeaSWludzik, Jozef << static_cast<const void*>(end) << "})"; 654dbb8aeaSWludzik, Jozef return false; 664dbb8aeaSWludzik, Jozef } 674dbb8aeaSWludzik, Jozef 684dbb8aeaSWludzik, Jozef if constexpr (std::is_same_v<FromTime, std::chrono::milliseconds>) 694dbb8aeaSWludzik, Jozef { 704dbb8aeaSWludzik, Jozef ticks *= static_cast<std::chrono::milliseconds::rep>( 714dbb8aeaSWludzik, Jozef std::pow(10, 3 - std::min<size_t>(pos, 3U))); 724dbb8aeaSWludzik, Jozef } 734dbb8aeaSWludzik, Jozef if (ticks < 0) 744dbb8aeaSWludzik, Jozef { 754dbb8aeaSWludzik, Jozef return false; 764dbb8aeaSWludzik, Jozef } 774dbb8aeaSWludzik, Jozef 784dbb8aeaSWludzik, Jozef out += FromTime(ticks); 794dbb8aeaSWludzik, Jozef const auto maxConversionRange = 804dbb8aeaSWludzik, Jozef std::chrono::duration_cast<FromTime>(std::chrono::milliseconds::max()) 814dbb8aeaSWludzik, Jozef .count(); 824dbb8aeaSWludzik, Jozef if (out < FromTime(ticks) || maxConversionRange < ticks) 834dbb8aeaSWludzik, Jozef { 844dbb8aeaSWludzik, Jozef return false; 854dbb8aeaSWludzik, Jozef } 864dbb8aeaSWludzik, Jozef 874dbb8aeaSWludzik, Jozef fmt.remove_prefix(pos + 1U); 884dbb8aeaSWludzik, Jozef return true; 894dbb8aeaSWludzik, Jozef } 90081ebf06SWludzik, Jozef } // namespace details 91081ebf06SWludzik, Jozef 92081ebf06SWludzik, Jozef /** 934dbb8aeaSWludzik, Jozef * @brief Convert string that represents value in Duration Format to its numeric 944dbb8aeaSWludzik, Jozef * equivalent. 954dbb8aeaSWludzik, Jozef */ 964f48d5f6SEd Tanous inline std::optional<std::chrono::milliseconds> 974dbb8aeaSWludzik, Jozef fromDurationString(const std::string& str) 984dbb8aeaSWludzik, Jozef { 994dbb8aeaSWludzik, Jozef std::chrono::milliseconds out = std::chrono::milliseconds::zero(); 1004dbb8aeaSWludzik, Jozef std::string_view v = str; 1014dbb8aeaSWludzik, Jozef 1024dbb8aeaSWludzik, Jozef if (v.empty()) 1034dbb8aeaSWludzik, Jozef { 1044dbb8aeaSWludzik, Jozef return out; 1054dbb8aeaSWludzik, Jozef } 1064dbb8aeaSWludzik, Jozef if (v.front() != 'P') 1074dbb8aeaSWludzik, Jozef { 1084dbb8aeaSWludzik, Jozef BMCWEB_LOG_ERROR << "Invalid duration format: " << str; 1094dbb8aeaSWludzik, Jozef return std::nullopt; 1104dbb8aeaSWludzik, Jozef } 1114dbb8aeaSWludzik, Jozef 1124dbb8aeaSWludzik, Jozef v.remove_prefix(1); 1134dbb8aeaSWludzik, Jozef if (!details::fromDurationItem<details::Days>(v, 'D', out)) 1144dbb8aeaSWludzik, Jozef { 1154dbb8aeaSWludzik, Jozef BMCWEB_LOG_ERROR << "Invalid duration format: " << str; 1164dbb8aeaSWludzik, Jozef return std::nullopt; 1174dbb8aeaSWludzik, Jozef } 1184dbb8aeaSWludzik, Jozef 1194dbb8aeaSWludzik, Jozef if (v.empty()) 1204dbb8aeaSWludzik, Jozef { 1214dbb8aeaSWludzik, Jozef return out; 1224dbb8aeaSWludzik, Jozef } 1234dbb8aeaSWludzik, Jozef if (v.front() != 'T') 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<std::chrono::hours>(v, 'H', out) || 1314dbb8aeaSWludzik, Jozef !details::fromDurationItem<std::chrono::minutes>(v, 'M', out)) 1324dbb8aeaSWludzik, Jozef { 1334dbb8aeaSWludzik, Jozef BMCWEB_LOG_ERROR << "Invalid duration format: " << str; 1344dbb8aeaSWludzik, Jozef return std::nullopt; 1354dbb8aeaSWludzik, Jozef } 1364dbb8aeaSWludzik, Jozef 1374dbb8aeaSWludzik, Jozef if (v.find('.') != std::string::npos && v.find('S') != std::string::npos) 1384dbb8aeaSWludzik, Jozef { 1394dbb8aeaSWludzik, Jozef if (!details::fromDurationItem<std::chrono::seconds>(v, '.', out) || 1404dbb8aeaSWludzik, Jozef !details::fromDurationItem<std::chrono::milliseconds>(v, 'S', out)) 1414dbb8aeaSWludzik, Jozef { 1424dbb8aeaSWludzik, Jozef BMCWEB_LOG_ERROR << "Invalid duration format: " << str; 1434dbb8aeaSWludzik, Jozef return std::nullopt; 1444dbb8aeaSWludzik, Jozef } 1454dbb8aeaSWludzik, Jozef } 1464dbb8aeaSWludzik, Jozef else if (!details::fromDurationItem<std::chrono::seconds>(v, 'S', out)) 1474dbb8aeaSWludzik, Jozef { 1484dbb8aeaSWludzik, Jozef BMCWEB_LOG_ERROR << "Invalid duration format: " << str; 1494dbb8aeaSWludzik, Jozef return std::nullopt; 1504dbb8aeaSWludzik, Jozef } 1514dbb8aeaSWludzik, Jozef 1524dbb8aeaSWludzik, Jozef if (!v.empty()) 1534dbb8aeaSWludzik, Jozef { 1544dbb8aeaSWludzik, Jozef BMCWEB_LOG_ERROR << "Invalid duration format: " << str; 1554dbb8aeaSWludzik, Jozef return std::nullopt; 1564dbb8aeaSWludzik, Jozef } 1574dbb8aeaSWludzik, Jozef return out; 1584dbb8aeaSWludzik, Jozef } 1594dbb8aeaSWludzik, Jozef 1604dbb8aeaSWludzik, Jozef /** 161081ebf06SWludzik, Jozef * @brief Convert time value into duration format that is based on ISO 8601. 162081ebf06SWludzik, Jozef * Example output: "P12DT1M5.5S" 163081ebf06SWludzik, Jozef * Ref: Redfish Specification, Section 9.4.4. Duration values 164081ebf06SWludzik, Jozef */ 165b00dcc27SEd Tanous inline std::string toDurationString(std::chrono::milliseconds ms) 166081ebf06SWludzik, Jozef { 167081ebf06SWludzik, Jozef if (ms < std::chrono::milliseconds::zero()) 168081ebf06SWludzik, Jozef { 169081ebf06SWludzik, Jozef return ""; 170081ebf06SWludzik, Jozef } 171081ebf06SWludzik, Jozef 172081ebf06SWludzik, Jozef std::string fmt; 173081ebf06SWludzik, Jozef fmt.reserve(sizeof("PxxxxxxxxxxxxDTxxHxxMxx.xxxxxxS")); 174081ebf06SWludzik, Jozef 1754dbb8aeaSWludzik, Jozef details::Days days = std::chrono::floor<details::Days>(ms); 176081ebf06SWludzik, Jozef ms -= days; 177081ebf06SWludzik, Jozef 178081ebf06SWludzik, Jozef std::chrono::hours hours = std::chrono::floor<std::chrono::hours>(ms); 179081ebf06SWludzik, Jozef ms -= hours; 180081ebf06SWludzik, Jozef 181081ebf06SWludzik, Jozef std::chrono::minutes minutes = std::chrono::floor<std::chrono::minutes>(ms); 182081ebf06SWludzik, Jozef ms -= minutes; 183081ebf06SWludzik, Jozef 184081ebf06SWludzik, Jozef std::chrono::seconds seconds = std::chrono::floor<std::chrono::seconds>(ms); 185081ebf06SWludzik, Jozef ms -= seconds; 186081ebf06SWludzik, Jozef 187081ebf06SWludzik, Jozef fmt = "P"; 188081ebf06SWludzik, Jozef if (days.count() > 0) 189081ebf06SWludzik, Jozef { 190081ebf06SWludzik, Jozef fmt += std::to_string(days.count()) + "D"; 191081ebf06SWludzik, Jozef } 192081ebf06SWludzik, Jozef fmt += "T"; 193081ebf06SWludzik, Jozef if (hours.count() > 0) 194081ebf06SWludzik, Jozef { 195081ebf06SWludzik, Jozef fmt += std::to_string(hours.count()) + "H"; 196081ebf06SWludzik, Jozef } 197081ebf06SWludzik, Jozef if (minutes.count() > 0) 198081ebf06SWludzik, Jozef { 199081ebf06SWludzik, Jozef fmt += std::to_string(minutes.count()) + "M"; 200081ebf06SWludzik, Jozef } 201081ebf06SWludzik, Jozef if (seconds.count() != 0 || ms.count() != 0) 202081ebf06SWludzik, Jozef { 203081ebf06SWludzik, Jozef fmt += std::to_string(seconds.count()) + "."; 204081ebf06SWludzik, Jozef std::string msStr = std::to_string(ms.count()); 205081ebf06SWludzik, Jozef details::leftZeroPadding(msStr, 3); 206081ebf06SWludzik, Jozef fmt += msStr + "S"; 207081ebf06SWludzik, Jozef } 208081ebf06SWludzik, Jozef 209081ebf06SWludzik, Jozef return fmt; 210081ebf06SWludzik, Jozef } 211081ebf06SWludzik, Jozef 2121b7e696bSLukasz Kazmierczak inline std::optional<std::string> 2131b7e696bSLukasz Kazmierczak toDurationStringFromUint(const uint64_t timeMs) 2141b7e696bSLukasz Kazmierczak { 2151b7e696bSLukasz Kazmierczak static const uint64_t maxTimeMs = 2161b7e696bSLukasz Kazmierczak static_cast<uint64_t>(std::chrono::milliseconds::max().count()); 2171b7e696bSLukasz Kazmierczak 2181b7e696bSLukasz Kazmierczak if (maxTimeMs < timeMs) 2191b7e696bSLukasz Kazmierczak { 2201b7e696bSLukasz Kazmierczak return std::nullopt; 2211b7e696bSLukasz Kazmierczak } 2221b7e696bSLukasz Kazmierczak 2231b7e696bSLukasz Kazmierczak std::string duration = toDurationString(std::chrono::milliseconds(timeMs)); 2241b7e696bSLukasz Kazmierczak if (duration.empty()) 2251b7e696bSLukasz Kazmierczak { 2261b7e696bSLukasz Kazmierczak return std::nullopt; 2271b7e696bSLukasz Kazmierczak } 2281b7e696bSLukasz Kazmierczak 2291b7e696bSLukasz Kazmierczak return std::make_optional(duration); 2301b7e696bSLukasz Kazmierczak } 2311b7e696bSLukasz Kazmierczak 232081ebf06SWludzik, Jozef } // namespace time_utils 233081ebf06SWludzik, Jozef } // namespace redfish 234