From 9896eaed88332eca7334a564a2ea2e0baf135639 Mon Sep 17 00:00:00 2001 From: Ed Tanous Date: Sat, 23 Jul 2022 15:07:33 -0700 Subject: Drop boost::posix_time Per the coding standard, if we can support what we need to do with std variants of something, we should prefer that. This commit adds an iso8160 to string method that supports any arbitrary std::chrono::duration object, which allows doing the full range of all of our integer types, and reduces the complexity (and presumably compile times) not pulling in a complex library. Despite the heavy templating, this only appears to add 108 bytes of compressed binary size to bmcweb. This is likely due to the decreased complexity compared to the boost variant (that likely pulls in boost::locale). (Ie 3 template instantiations of the simple one take about the same binary space as 1 complex instantiation). Tested: Unit tests pass (pretty good coverage here) Signed-off-by: Ed Tanous Change-Id: I78200fb391b601eba8d2bfd2de0dd868e4390d6b --- http/utility.hpp | 143 ++++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 111 insertions(+), 32 deletions(-) (limited to 'http/utility.hpp') diff --git a/http/utility.hpp b/http/utility.hpp index d6f31d0717..5ca1dea151 100644 --- a/http/utility.hpp +++ b/http/utility.hpp @@ -4,7 +4,6 @@ #include #include -#include #include #include @@ -14,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -541,55 +541,134 @@ inline bool base64Decode(const std::string_view input, std::string& output) namespace details { -constexpr uint64_t maxMilliSeconds = 253402300799999; -constexpr uint64_t maxSeconds = 253402300799; -inline std::string getDateTime(boost::posix_time::milliseconds timeSinceEpoch) +// Returns year/month/day triple in civil calendar +// Preconditions: z is number of days since 1970-01-01 and is in the range: +// [numeric_limits::min(), +// numeric_limits::max()-719468]. +// Algorithm sourced from +// https://howardhinnant.github.io/date_algorithms.html#civil_from_days +// All constants are explained in the above +template +constexpr std::tuple + civilFromDays(IntType z) noexcept +{ + z += 719468; + IntType era = (z >= 0 ? z : z - 146096) / 146097; + unsigned doe = static_cast(z - era * 146097); // [0, 146096] + unsigned yoe = + (doe - doe / 1460 + doe / 36524 - doe / 146096) / 365; // [0, 399] + IntType y = static_cast(yoe) + era * 400; + unsigned doy = doe - (365 * yoe + yoe / 4 - yoe / 100); // [0, 365] + unsigned mp = (5 * doy + 2) / 153; // [0, 11] + unsigned d = doy - (153 * mp + 2) / 5 + 1; // [1, 31] + unsigned m = mp < 10 ? mp + 3 : mp - 9; // [1, 12] + + return std::tuple(y + (m <= 2), m, d); +} + +// Creates a string from an integer in the most efficient way possible without +// using std::locale. Adds an exact zero pad based on the pad input parameter. +// Does not handle negative numbers. +inline std::string padZeros(int value, size_t pad) { - boost::posix_time::ptime epoch(boost::gregorian::date(1970, 1, 1)); - boost::posix_time::ptime time = epoch + timeSinceEpoch; - // append zero offset to the end according to the Redfish spec for Date-Time - return boost::posix_time::to_iso_extended_string(time) + "+00:00"; + std::string result(pad, '0'); + for (int val = value; pad > 0; pad--) + { + result[pad - 1] = static_cast('0' + val % 10); + val /= 10; + } + return result; +} + +template +std::string toISO8061ExtendedStr(std::chrono::duration t) +{ + using seconds = std::chrono::duration; + using minutes = std::chrono::duration>; + using hours = std::chrono::duration>; + using days = std::chrono::duration< + IntType, std::ratio_multiply>>; + + // d is days since 1970-01-01 + days d = std::chrono::duration_cast(t); + + // t is now time duration since midnight of day d + t -= d; + + // break d down into year/month/day + int year = 0; + int month = 0; + int day = 0; + std::tie(year, month, day) = details::civilFromDays(d.count()); + // Check against limits. Can't go above year 9999, and can't go below epoch + // (1970) + if (year >= 10000) + { + year = 9999; + month = 12; + day = 31; + t = days(1) - std::chrono::duration(1); + } + else if (year < 1970) + { + year = 1970; + month = 1; + day = 1; + t = std::chrono::duration::zero(); + } + std::string out; + out += details::padZeros(year, 4); + out += '-'; + out += details::padZeros(month, 2); + out += '-'; + out += details::padZeros(day, 2); + + out += 'T'; + hours hr = duration_cast(t); + out += details::padZeros(hr.count(), 2); + t -= hr; + out += ':'; + + minutes mt = duration_cast(t); + out += details::padZeros(mt.count(), 2); + t -= mt; + out += ':'; + + seconds se = duration_cast(t); + out += details::padZeros(se.count(), 2); + + out += "+00:00"; + return out; } } // namespace details // Returns the formatted date time string. // Note that the maximum supported date is 9999-12-31T23:59:59+00:00, if // the given |secondsSinceEpoch| is too large, we return the maximum supported -// date. This behavior is to avoid exceptions throwed by Boost. +// date. inline std::string getDateTimeUint(uint64_t secondsSinceEpoch) { - secondsSinceEpoch = std::min(secondsSinceEpoch, details::maxSeconds); - boost::posix_time::seconds boostSeconds(secondsSinceEpoch); - return details::getDateTime( - boost::posix_time::milliseconds(boostSeconds.total_milliseconds())); + using DurationType = std::chrono::duration; + DurationType sinceEpoch(secondsSinceEpoch); + return details::toISO8061ExtendedStr(sinceEpoch); } // Returns the formatted date time string. -// Note that the maximum supported date is 9999-12-31T23:59:59.999+00:00, if -// the given |millisSecondsSinceEpoch| is too large, we return the maximum -// supported date. +// Note that the maximum supported date is 9999-12-31T23:59:59+00:00, if +// the given |secondsSinceEpoch| is too large, we return the maximum supported +// date. inline std::string getDateTimeUintMs(uint64_t milliSecondsSinceEpoch) { - milliSecondsSinceEpoch = - std::min(details::maxMilliSeconds, milliSecondsSinceEpoch); - return details::getDateTime( - boost::posix_time::milliseconds(milliSecondsSinceEpoch)); + using DurationType = std::chrono::duration; + DurationType sinceEpoch(milliSecondsSinceEpoch); + return details::toISO8061ExtendedStr(sinceEpoch); } inline std::string getDateTimeStdtime(std::time_t secondsSinceEpoch) { - // secondsSinceEpoch >= maxSeconds - if constexpr (std::cmp_less_equal(details::maxSeconds, - std::numeric_limits::max())) - { - if (std::cmp_greater_equal(secondsSinceEpoch, details::maxSeconds)) - { - secondsSinceEpoch = details::maxSeconds; - } - } - boost::posix_time::ptime time = - boost::posix_time::from_time_t(secondsSinceEpoch); - return boost::posix_time::to_iso_extended_string(time) + "+00:00"; + using DurationType = std::chrono::duration; + DurationType sinceEpoch(secondsSinceEpoch); + return details::toISO8061ExtendedStr(sinceEpoch); } /** -- cgit v1.2.3