summaryrefslogtreecommitdiff
path: root/http/utility.hpp
diff options
context:
space:
mode:
authorEd Tanous <edtanous@google.com>2022-07-24 01:07:33 +0300
committerEd Tanous <ed@tanous.net>2022-08-05 02:44:34 +0300
commit9896eaed88332eca7334a564a2ea2e0baf135639 (patch)
treeeccd40d760bfd0d6e7ed77615af4fd8d5d2d679d /http/utility.hpp
parent81584abe2616373033281ab2ec75407fc50306d1 (diff)
downloadbmcweb-9896eaed88332eca7334a564a2ea2e0baf135639.tar.xz
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 <edtanous@google.com> Change-Id: I78200fb391b601eba8d2bfd2de0dd868e4390d6b
Diffstat (limited to 'http/utility.hpp')
-rw-r--r--http/utility.hpp143
1 files changed, 111 insertions, 32 deletions
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);
}
/**