Fix Boost exceptions in GetDateTime series

In cases when the input time is too large, we return the maximum
supported date in the ISO Extended format.

Tested: Unit tests pass
Signed-off-by: Nan Zhou <nanzhoumails@gmail.com>
Change-Id: I0dcd60d10d4357bd8700f0dbc1ef86d94bcc82bb
diff --git a/http/ut/utility_test.cpp b/http/ut/utility_test.cpp
index 4748c25..5dafdce 100644
--- a/http/ut/utility_test.cpp
+++ b/http/ut/utility_test.cpp
@@ -2,11 +2,16 @@
 
 #include "gmock/gmock.h"
 
+namespace crow::utility
+{
+namespace
+{
+
 TEST(Utility, Base64DecodeAuthString)
 {
     std::string authString("dXNlcm40bWU6cGFzc3cwcmQ=");
     std::string result;
-    EXPECT_TRUE(crow::utility::base64Decode(authString, result));
+    EXPECT_TRUE(base64Decode(authString, result));
     EXPECT_EQ(result, "usern4me:passw0rd");
 }
 
@@ -14,7 +19,7 @@
 {
     std::string junkString("\xff\xee\xdd\xcc\x01\x11\x22\x33");
     std::string result;
-    EXPECT_FALSE(crow::utility::base64Decode(junkString, result));
+    EXPECT_FALSE(base64Decode(junkString, result));
 }
 
 TEST(Utility, Base64EncodeString)
@@ -22,28 +27,28 @@
     using namespace std::string_literals;
     std::string encoded;
 
-    encoded = crow::utility::base64encode("");
+    encoded = base64encode("");
     EXPECT_EQ(encoded, "");
 
-    encoded = crow::utility::base64encode("f");
+    encoded = base64encode("f");
     EXPECT_EQ(encoded, "Zg==");
 
-    encoded = crow::utility::base64encode("f0");
+    encoded = base64encode("f0");
     EXPECT_EQ(encoded, "ZjA=");
 
-    encoded = crow::utility::base64encode("f0\0"s);
+    encoded = base64encode("f0\0"s);
     EXPECT_EQ(encoded, "ZjAA");
 
-    encoded = crow::utility::base64encode("f0\0 "s);
+    encoded = base64encode("f0\0 "s);
     EXPECT_EQ(encoded, "ZjAAIA==");
 
-    encoded = crow::utility::base64encode("f0\0 B"s);
+    encoded = base64encode("f0\0 B"s);
     EXPECT_EQ(encoded, "ZjAAIEI=");
 
-    encoded = crow::utility::base64encode("f0\0 Ba"s);
+    encoded = base64encode("f0\0 Ba"s);
     EXPECT_EQ(encoded, "ZjAAIEJh");
 
-    encoded = crow::utility::base64encode("f0\0 Bar"s);
+    encoded = base64encode("f0\0 Bar"s);
     EXPECT_EQ(encoded, "ZjAAIEJhcg==");
 }
 
@@ -51,9 +56,9 @@
 {
     using namespace std::string_literals;
     std::string data("Data fr\0m 90 reading a \nFile"s);
-    std::string encoded = crow::utility::base64encode(data);
+    std::string encoded = base64encode(data);
     std::string decoded;
-    EXPECT_TRUE(crow::utility::base64Decode(encoded, decoded));
+    EXPECT_TRUE(base64Decode(encoded, decoded));
     EXPECT_EQ(data, decoded);
 }
 
@@ -67,19 +72,16 @@
 
     // epoch
     EXPECT_EQ(getDateTimeStdtime(std::time_t{0}), "1970-01-01T00:00:00+00:00");
-    // some time in the past after the epoch
 
     // Limits
     EXPECT_EQ(getDateTimeStdtime(std::numeric_limits<std::time_t>::max()),
-              "1969-12-31T23:59:59+00:00");
+              "9999-12-31T23:59:59+00:00");
     EXPECT_EQ(getDateTimeStdtime(std::numeric_limits<std::time_t>::min()),
               "1970-01-01T00:00:00+00:00");
 }
 
-TEST(Utility, getDateTimeUint)
+TEST(Utility, GetDateTimeUint)
 {
-    using crow::utility::getDateTimeUint;
-
     EXPECT_EQ(getDateTimeUint(uint64_t{1638312095}),
               "2021-11-30T22:41:35+00:00");
     // some time in the future, beyond 2038
@@ -89,22 +91,21 @@
     EXPECT_EQ(getDateTimeUint(uint64_t{253402300799}),
               "9999-12-31T23:59:59+00:00");
 
-    // This test currently throws an exception
-    // EXPECT_EQ(getDateTimeUint(std::numeric_limits<uint64_t>::max()), "");
+    // returns the maximum Redfish date
+    EXPECT_EQ(getDateTimeUint(std::numeric_limits<uint64_t>::max()),
+              "9999-12-31T23:59:59+00:00");
 
     EXPECT_EQ(getDateTimeUint(std::numeric_limits<uint64_t>::min()),
               "1970-01-01T00:00:00+00:00");
 }
 
-TEST(Utility, getDateTimeUintMs)
+TEST(Utility, GetDateTimeUintMs)
 {
-    using crow::utility::getDateTimeUintMs;
-
-    // Note, this is the wrong result, but is here to make sure that we don't
-    // have worse behavior (ie seg faults, exceptions) when this happens
+    // returns the maximum Redfish date
     EXPECT_EQ(getDateTimeUintMs(std::numeric_limits<uint64_t>::max()),
-              "1969-12-31T23:59:59.999000+00:00");
-
+              "9999-12-31T23:59:59.999000+00:00");
     EXPECT_EQ(getDateTimeUintMs(std::numeric_limits<uint64_t>::min()),
               "1970-01-01T00:00:00+00:00");
 }
+} // namespace
+} // namespace crow::utility
diff --git a/http/utility.hpp b/http/utility.hpp
index 41182e4..5579267 100644
--- a/http/utility.hpp
+++ b/http/utility.hpp
@@ -577,6 +577,8 @@
 
 namespace details
 {
+constexpr uint64_t maxMilliSeconds = 253402300799999;
+constexpr uint64_t maxSeconds = 253402300799;
 inline std::string getDateTime(boost::posix_time::milliseconds timeSinceEpoch)
 {
     boost::posix_time::ptime epoch(boost::gregorian::date(1970, 1, 1));
@@ -586,23 +588,33 @@
 }
 } // 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.
 inline std::string getDateTimeUint(uint64_t secondsSinceEpoch)
 {
-    boost::posix_time::seconds boostSeconds(secondsSinceEpoch);
+
+    boost::posix_time::seconds boostSeconds(
+        std::min(secondsSinceEpoch, details::maxSeconds));
     return details::getDateTime(
         boost::posix_time::milliseconds(boostSeconds.total_milliseconds()));
 }
 
-inline std::string getDateTimeUintMs(uint64_t millisSecondsSinceEpoch)
+// 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.
+inline std::string getDateTimeUintMs(uint64_t milliSecondsSinceEpoch)
 {
-    return details::getDateTime(
-        boost::posix_time::milliseconds(millisSecondsSinceEpoch));
+    return details::getDateTime(boost::posix_time::milliseconds(
+        std::min(details::maxMilliSeconds, milliSecondsSinceEpoch)));
 }
 
 inline std::string getDateTimeStdtime(std::time_t secondsSinceEpoch)
 {
-    boost::posix_time::ptime time =
-        boost::posix_time::from_time_t(secondsSinceEpoch);
+    boost::posix_time::ptime time = boost::posix_time::from_time_t(
+        std::min(secondsSinceEpoch, std::time_t{details::maxSeconds}));
     return boost::posix_time::to_iso_extended_string(time) + "+00:00";
 }