1081ebf06SWludzik, Jozef #pragma once 2081ebf06SWludzik, Jozef 3*4dbb8aeaSWludzik, Jozef #include "logging.hpp" 4*4dbb8aeaSWludzik, Jozef 5*4dbb8aeaSWludzik, Jozef #include <charconv> 6081ebf06SWludzik, Jozef #include <chrono> 7*4dbb8aeaSWludzik, Jozef #include <cmath> 8*4dbb8aeaSWludzik, Jozef #include <optional> 9081ebf06SWludzik, Jozef #include <string> 10*4dbb8aeaSWludzik, 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 21*4dbb8aeaSWludzik, Jozef using Days = std::chrono::duration<long long, std::ratio<24 * 60 * 60>>; 22*4dbb8aeaSWludzik, Jozef 23081ebf06SWludzik, Jozef inline void leftZeroPadding(std::string& str, const std::size_t padding) 24081ebf06SWludzik, Jozef { 25081ebf06SWludzik, Jozef if (str.size() < padding) 26081ebf06SWludzik, Jozef { 27081ebf06SWludzik, Jozef str.insert(0, padding - str.size(), '0'); 28081ebf06SWludzik, Jozef } 29081ebf06SWludzik, Jozef } 30*4dbb8aeaSWludzik, Jozef 31*4dbb8aeaSWludzik, Jozef template <typename FromTime> 32*4dbb8aeaSWludzik, Jozef bool fromDurationItem(std::string_view& fmt, const char postfix, 33*4dbb8aeaSWludzik, Jozef std::chrono::milliseconds& out) 34*4dbb8aeaSWludzik, Jozef { 35*4dbb8aeaSWludzik, Jozef const size_t pos = fmt.find(postfix); 36*4dbb8aeaSWludzik, Jozef if (pos == std::string::npos) 37*4dbb8aeaSWludzik, Jozef { 38*4dbb8aeaSWludzik, Jozef return true; 39*4dbb8aeaSWludzik, Jozef } 40*4dbb8aeaSWludzik, Jozef if ((pos + 1U) > fmt.size()) 41*4dbb8aeaSWludzik, Jozef { 42*4dbb8aeaSWludzik, Jozef return false; 43*4dbb8aeaSWludzik, Jozef } 44*4dbb8aeaSWludzik, Jozef 45*4dbb8aeaSWludzik, Jozef const char* end; 46*4dbb8aeaSWludzik, Jozef std::chrono::milliseconds::rep ticks = 0; 47*4dbb8aeaSWludzik, Jozef if constexpr (std::is_same_v<FromTime, std::chrono::milliseconds>) 48*4dbb8aeaSWludzik, Jozef { 49*4dbb8aeaSWludzik, Jozef end = fmt.data() + std::min<size_t>(pos, 3U); 50*4dbb8aeaSWludzik, Jozef } 51*4dbb8aeaSWludzik, Jozef else 52*4dbb8aeaSWludzik, Jozef { 53*4dbb8aeaSWludzik, Jozef end = fmt.data() + pos; 54*4dbb8aeaSWludzik, Jozef } 55*4dbb8aeaSWludzik, Jozef 56*4dbb8aeaSWludzik, Jozef auto [ptr, ec] = std::from_chars(fmt.data(), end, ticks); 57*4dbb8aeaSWludzik, Jozef if (ptr != end || ec != std::errc()) 58*4dbb8aeaSWludzik, Jozef { 59*4dbb8aeaSWludzik, Jozef BMCWEB_LOG_ERROR << "Failed to convert string to decimal with err: " 60*4dbb8aeaSWludzik, Jozef << static_cast<int>(ec) << "(" 61*4dbb8aeaSWludzik, Jozef << std::make_error_code(ec).message() << "), ptr{" 62*4dbb8aeaSWludzik, Jozef << static_cast<const void*>(ptr) << "} != end{" 63*4dbb8aeaSWludzik, Jozef << static_cast<const void*>(end) << "})"; 64*4dbb8aeaSWludzik, Jozef return false; 65*4dbb8aeaSWludzik, Jozef } 66*4dbb8aeaSWludzik, Jozef 67*4dbb8aeaSWludzik, Jozef if constexpr (std::is_same_v<FromTime, std::chrono::milliseconds>) 68*4dbb8aeaSWludzik, Jozef { 69*4dbb8aeaSWludzik, Jozef ticks *= static_cast<std::chrono::milliseconds::rep>( 70*4dbb8aeaSWludzik, Jozef std::pow(10, 3 - std::min<size_t>(pos, 3U))); 71*4dbb8aeaSWludzik, Jozef } 72*4dbb8aeaSWludzik, Jozef if (ticks < 0) 73*4dbb8aeaSWludzik, Jozef { 74*4dbb8aeaSWludzik, Jozef return false; 75*4dbb8aeaSWludzik, Jozef } 76*4dbb8aeaSWludzik, Jozef 77*4dbb8aeaSWludzik, Jozef out += FromTime(ticks); 78*4dbb8aeaSWludzik, Jozef const auto maxConversionRange = 79*4dbb8aeaSWludzik, Jozef std::chrono::duration_cast<FromTime>(std::chrono::milliseconds::max()) 80*4dbb8aeaSWludzik, Jozef .count(); 81*4dbb8aeaSWludzik, Jozef if (out < FromTime(ticks) || maxConversionRange < ticks) 82*4dbb8aeaSWludzik, Jozef { 83*4dbb8aeaSWludzik, Jozef return false; 84*4dbb8aeaSWludzik, Jozef } 85*4dbb8aeaSWludzik, Jozef 86*4dbb8aeaSWludzik, Jozef fmt.remove_prefix(pos + 1U); 87*4dbb8aeaSWludzik, Jozef return true; 88*4dbb8aeaSWludzik, Jozef } 89081ebf06SWludzik, Jozef } // namespace details 90081ebf06SWludzik, Jozef 91081ebf06SWludzik, Jozef /** 92*4dbb8aeaSWludzik, Jozef * @brief Convert string that represents value in Duration Format to its numeric 93*4dbb8aeaSWludzik, Jozef * equivalent. 94*4dbb8aeaSWludzik, Jozef */ 95*4dbb8aeaSWludzik, Jozef std::optional<std::chrono::milliseconds> 96*4dbb8aeaSWludzik, Jozef fromDurationString(const std::string& str) 97*4dbb8aeaSWludzik, Jozef { 98*4dbb8aeaSWludzik, Jozef std::chrono::milliseconds out = std::chrono::milliseconds::zero(); 99*4dbb8aeaSWludzik, Jozef std::string_view v = str; 100*4dbb8aeaSWludzik, Jozef 101*4dbb8aeaSWludzik, Jozef if (v.empty()) 102*4dbb8aeaSWludzik, Jozef { 103*4dbb8aeaSWludzik, Jozef return out; 104*4dbb8aeaSWludzik, Jozef } 105*4dbb8aeaSWludzik, Jozef if (v.front() != 'P') 106*4dbb8aeaSWludzik, Jozef { 107*4dbb8aeaSWludzik, Jozef BMCWEB_LOG_ERROR << "Invalid duration format: " << str; 108*4dbb8aeaSWludzik, Jozef return std::nullopt; 109*4dbb8aeaSWludzik, Jozef } 110*4dbb8aeaSWludzik, Jozef 111*4dbb8aeaSWludzik, Jozef v.remove_prefix(1); 112*4dbb8aeaSWludzik, Jozef if (!details::fromDurationItem<details::Days>(v, 'D', out)) 113*4dbb8aeaSWludzik, Jozef { 114*4dbb8aeaSWludzik, Jozef BMCWEB_LOG_ERROR << "Invalid duration format: " << str; 115*4dbb8aeaSWludzik, Jozef return std::nullopt; 116*4dbb8aeaSWludzik, Jozef } 117*4dbb8aeaSWludzik, Jozef 118*4dbb8aeaSWludzik, Jozef if (v.empty()) 119*4dbb8aeaSWludzik, Jozef { 120*4dbb8aeaSWludzik, Jozef return out; 121*4dbb8aeaSWludzik, Jozef } 122*4dbb8aeaSWludzik, Jozef if (v.front() != 'T') 123*4dbb8aeaSWludzik, Jozef { 124*4dbb8aeaSWludzik, Jozef BMCWEB_LOG_ERROR << "Invalid duration format: " << str; 125*4dbb8aeaSWludzik, Jozef return std::nullopt; 126*4dbb8aeaSWludzik, Jozef } 127*4dbb8aeaSWludzik, Jozef 128*4dbb8aeaSWludzik, Jozef v.remove_prefix(1); 129*4dbb8aeaSWludzik, Jozef if (!details::fromDurationItem<std::chrono::hours>(v, 'H', out) || 130*4dbb8aeaSWludzik, Jozef !details::fromDurationItem<std::chrono::minutes>(v, 'M', out)) 131*4dbb8aeaSWludzik, Jozef { 132*4dbb8aeaSWludzik, Jozef BMCWEB_LOG_ERROR << "Invalid duration format: " << str; 133*4dbb8aeaSWludzik, Jozef return std::nullopt; 134*4dbb8aeaSWludzik, Jozef } 135*4dbb8aeaSWludzik, Jozef 136*4dbb8aeaSWludzik, Jozef if (v.find('.') != std::string::npos && v.find('S') != std::string::npos) 137*4dbb8aeaSWludzik, Jozef { 138*4dbb8aeaSWludzik, Jozef if (!details::fromDurationItem<std::chrono::seconds>(v, '.', out) || 139*4dbb8aeaSWludzik, Jozef !details::fromDurationItem<std::chrono::milliseconds>(v, 'S', out)) 140*4dbb8aeaSWludzik, Jozef { 141*4dbb8aeaSWludzik, Jozef BMCWEB_LOG_ERROR << "Invalid duration format: " << str; 142*4dbb8aeaSWludzik, Jozef return std::nullopt; 143*4dbb8aeaSWludzik, Jozef } 144*4dbb8aeaSWludzik, Jozef } 145*4dbb8aeaSWludzik, Jozef else if (!details::fromDurationItem<std::chrono::seconds>(v, 'S', out)) 146*4dbb8aeaSWludzik, Jozef { 147*4dbb8aeaSWludzik, Jozef BMCWEB_LOG_ERROR << "Invalid duration format: " << str; 148*4dbb8aeaSWludzik, Jozef return std::nullopt; 149*4dbb8aeaSWludzik, Jozef } 150*4dbb8aeaSWludzik, Jozef 151*4dbb8aeaSWludzik, Jozef if (!v.empty()) 152*4dbb8aeaSWludzik, Jozef { 153*4dbb8aeaSWludzik, Jozef BMCWEB_LOG_ERROR << "Invalid duration format: " << str; 154*4dbb8aeaSWludzik, Jozef return std::nullopt; 155*4dbb8aeaSWludzik, Jozef } 156*4dbb8aeaSWludzik, Jozef return out; 157*4dbb8aeaSWludzik, Jozef } 158*4dbb8aeaSWludzik, Jozef 159*4dbb8aeaSWludzik, Jozef /** 160081ebf06SWludzik, Jozef * @brief Convert time value into duration format that is based on ISO 8601. 161081ebf06SWludzik, Jozef * Example output: "P12DT1M5.5S" 162081ebf06SWludzik, Jozef * Ref: Redfish Specification, Section 9.4.4. Duration values 163081ebf06SWludzik, Jozef */ 164b00dcc27SEd Tanous inline std::string toDurationString(std::chrono::milliseconds ms) 165081ebf06SWludzik, Jozef { 166081ebf06SWludzik, Jozef if (ms < std::chrono::milliseconds::zero()) 167081ebf06SWludzik, Jozef { 168081ebf06SWludzik, Jozef return ""; 169081ebf06SWludzik, Jozef } 170081ebf06SWludzik, Jozef 171081ebf06SWludzik, Jozef std::string fmt; 172081ebf06SWludzik, Jozef fmt.reserve(sizeof("PxxxxxxxxxxxxDTxxHxxMxx.xxxxxxS")); 173081ebf06SWludzik, Jozef 174*4dbb8aeaSWludzik, Jozef details::Days days = std::chrono::floor<details::Days>(ms); 175081ebf06SWludzik, Jozef ms -= days; 176081ebf06SWludzik, Jozef 177081ebf06SWludzik, Jozef std::chrono::hours hours = std::chrono::floor<std::chrono::hours>(ms); 178081ebf06SWludzik, Jozef ms -= hours; 179081ebf06SWludzik, Jozef 180081ebf06SWludzik, Jozef std::chrono::minutes minutes = std::chrono::floor<std::chrono::minutes>(ms); 181081ebf06SWludzik, Jozef ms -= minutes; 182081ebf06SWludzik, Jozef 183081ebf06SWludzik, Jozef std::chrono::seconds seconds = std::chrono::floor<std::chrono::seconds>(ms); 184081ebf06SWludzik, Jozef ms -= seconds; 185081ebf06SWludzik, Jozef 186081ebf06SWludzik, Jozef fmt = "P"; 187081ebf06SWludzik, Jozef if (days.count() > 0) 188081ebf06SWludzik, Jozef { 189081ebf06SWludzik, Jozef fmt += std::to_string(days.count()) + "D"; 190081ebf06SWludzik, Jozef } 191081ebf06SWludzik, Jozef fmt += "T"; 192081ebf06SWludzik, Jozef if (hours.count() > 0) 193081ebf06SWludzik, Jozef { 194081ebf06SWludzik, Jozef fmt += std::to_string(hours.count()) + "H"; 195081ebf06SWludzik, Jozef } 196081ebf06SWludzik, Jozef if (minutes.count() > 0) 197081ebf06SWludzik, Jozef { 198081ebf06SWludzik, Jozef fmt += std::to_string(minutes.count()) + "M"; 199081ebf06SWludzik, Jozef } 200081ebf06SWludzik, Jozef if (seconds.count() != 0 || ms.count() != 0) 201081ebf06SWludzik, Jozef { 202081ebf06SWludzik, Jozef fmt += std::to_string(seconds.count()) + "."; 203081ebf06SWludzik, Jozef std::string msStr = std::to_string(ms.count()); 204081ebf06SWludzik, Jozef details::leftZeroPadding(msStr, 3); 205081ebf06SWludzik, Jozef fmt += msStr + "S"; 206081ebf06SWludzik, Jozef } 207081ebf06SWludzik, Jozef 208081ebf06SWludzik, Jozef return fmt; 209081ebf06SWludzik, Jozef } 210081ebf06SWludzik, Jozef 211081ebf06SWludzik, Jozef } // namespace time_utils 212081ebf06SWludzik, Jozef } // namespace redfish 213