diff options
-rw-r--r-- | http/http_server.hpp | 1 | ||||
-rw-r--r-- | http/ut/utility_test.cpp | 4 | ||||
-rw-r--r-- | http/utility.hpp | 143 | ||||
-rw-r--r-- | redfish-core/lib/log_services.hpp | 1 |
4 files changed, 114 insertions, 35 deletions
diff --git a/http/http_server.hpp b/http/http_server.hpp index 050a3f000e..0d224a10d4 100644 --- a/http/http_server.hpp +++ b/http/http_server.hpp @@ -9,7 +9,6 @@ #include <boost/asio/ssl/context.hpp> #include <boost/asio/steady_timer.hpp> #include <boost/beast/ssl/ssl_stream.hpp> -#include <boost/date_time/posix_time/posix_time.hpp> #include <ssl_key_handler.hpp> #include <atomic> diff --git a/http/ut/utility_test.cpp b/http/ut/utility_test.cpp index c774996283..a1125b4bee 100644 --- a/http/ut/utility_test.cpp +++ b/http/ut/utility_test.cpp @@ -85,7 +85,7 @@ TEST(Utility, GetDateTimeStdtime) { // some time before the epoch EXPECT_EQ(getDateTimeStdtime(std::time_t{-1234567}), - "1969-12-17T17:03:53+00:00"); + "1970-01-01T00:00:00+00:00"); // epoch EXPECT_EQ(getDateTimeStdtime(std::time_t{0}), "1970-01-01T00:00:00+00:00"); @@ -120,7 +120,7 @@ TEST(Utility, GetDateTimeUintMs) { // returns the maximum Redfish date EXPECT_EQ(getDateTimeUintMs(std::numeric_limits<uint64_t>::max()), - "9999-12-31T23:59:59.999000+00:00"); + "9999-12-31T23:59:59+00:00"); EXPECT_EQ(getDateTimeUintMs(std::numeric_limits<uint64_t>::min()), "1970-01-01T00:00:00+00:00"); } 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 <openssl/crypto.h> #include <boost/callable_traits.hpp> -#include <boost/date_time/posix_time/posix_time.hpp> #include <boost/url/url.hpp> #include <nlohmann/json.hpp> @@ -14,6 +13,7 @@ #include <cstdint> #include <ctime> #include <functional> +#include <iomanip> #include <limits> #include <stdexcept> #include <string> @@ -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<Int>::min(), +// numeric_limits<Int>::max()-719468]. +// Algorithm sourced from +// https://howardhinnant.github.io/date_algorithms.html#civil_from_days +// All constants are explained in the above +template <class IntType> +constexpr std::tuple<IntType, unsigned, unsigned> + civilFromDays(IntType z) noexcept +{ + z += 719468; + IntType era = (z >= 0 ? z : z - 146096) / 146097; + unsigned doe = static_cast<unsigned>(z - era * 146097); // [0, 146096] + unsigned yoe = + (doe - doe / 1460 + doe / 36524 - doe / 146096) / 365; // [0, 399] + IntType y = static_cast<IntType>(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<IntType, unsigned, unsigned>(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<char>('0' + val % 10); + val /= 10; + } + return result; +} + +template <typename IntType, typename Period> +std::string toISO8061ExtendedStr(std::chrono::duration<IntType, Period> t) +{ + using seconds = std::chrono::duration<int>; + using minutes = std::chrono::duration<int, std::ratio<60>>; + using hours = std::chrono::duration<int, std::ratio<3600>>; + using days = std::chrono::duration< + IntType, std::ratio_multiply<hours::period, std::ratio<24>>>; + + // d is days since 1970-01-01 + days d = std::chrono::duration_cast<days>(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<IntType, Period>(1); + } + else if (year < 1970) + { + year = 1970; + month = 1; + day = 1; + t = std::chrono::duration<IntType, Period>::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<hours>(t); + out += details::padZeros(hr.count(), 2); + t -= hr; + out += ':'; + + minutes mt = duration_cast<minutes>(t); + out += details::padZeros(mt.count(), 2); + t -= mt; + out += ':'; + + seconds se = duration_cast<seconds>(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<uint64_t>; + 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<uint64_t, std::milli>; + 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<std::time_t>::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<std::time_t>; + DurationType sinceEpoch(secondsSinceEpoch); + return details::toISO8061ExtendedStr(sinceEpoch); } /** diff --git a/redfish-core/lib/log_services.hpp b/redfish-core/lib/log_services.hpp index a04dfb55b8..7d511e2bb1 100644 --- a/redfish-core/lib/log_services.hpp +++ b/redfish-core/lib/log_services.hpp @@ -27,6 +27,7 @@ #include <unistd.h> #include <app.hpp> +#include <boost/algorithm/string/case_conv.hpp> #include <boost/algorithm/string/classification.hpp> #include <boost/algorithm/string/replace.hpp> #include <boost/algorithm/string/split.hpp> |