fix the year 2038 problem in getDateTime
The existing codes cast uint64_t into time_t which is int32_t in
most 32-bit systems. It results overflow if the timestamp is larger
than INT_MAX.
time_t will be 64 bits in future releases of glibc. See
https://sourceware.org/bugzilla/show_bug.cgi?id=28182.
This change workarounds the year 2038 problem via boost's ptime.
std::chrono doesn't help since it is still 32 bits.
Tested on QEMU.
Example output for certificate:
{
"Name": "HTTPS Certificate",
"Subject": null,
"ValidNotAfter": "2106-01-28T20:40:31Z",
"ValidNotBefore": "2106-02-06T18:28:16Z"
}
Previously, the format is like "1969-12-31T12:00:00+00:00". Note
that the ending "+00:00" is the time zone, not ms.
Tested the schema on QEMU. No new Redfish Service Validator errors.
Signed-off-by: Nan Zhou <nanzhoumails@gmail.com>
Signed-off-by: Ed Tanous <edtanous@google.com>
Change-Id: I8ef0bee3d724184d96253c23f3919447828d3f82
diff --git a/http/ut/utility_test.cpp b/http/ut/utility_test.cpp
index 191a16a..fc8b90e 100644
--- a/http/ut/utility_test.cpp
+++ b/http/ut/utility_test.cpp
@@ -56,3 +56,22 @@
EXPECT_TRUE(crow::utility::base64Decode(encoded, decoded));
EXPECT_EQ(data, decoded);
}
+
+TEST(Utility, GetDateTime)
+{
+ // some time before the epoch
+ EXPECT_EQ(crow::utility::getDateTimeStdtime(std::time_t{-1234567}),
+ "1969-12-17T17:03:53Z");
+ // epoch
+ EXPECT_EQ(crow::utility::getDateTimeStdtime(std::time_t{0}),
+ "1970-01-01T00:00:00Z");
+ // some time in the past after the epoch
+ EXPECT_EQ(crow::utility::getDateTimeUint(uint64_t{1638312095}),
+ "2021-11-30T22:41:35Z");
+ // some time in the future, beyond 2038
+ EXPECT_EQ(crow::utility::getDateTimeUint(uint64_t{41638312095}),
+ "3289-06-18T21:48:15Z");
+ // the maximum time we support
+ EXPECT_EQ(crow::utility::getDateTimeUint(uint64_t{253402300799}),
+ "9999-12-31T23:59:59Z");
+}
diff --git a/http/utility.hpp b/http/utility.hpp
index 6f81be8..d141056 100644
--- a/http/utility.hpp
+++ b/http/utility.hpp
@@ -1,9 +1,10 @@
#pragma once
-
#include "nlohmann/json.hpp"
#include <openssl/crypto.h>
+#include <boost/date_time/posix_time/posix_time.hpp>
+
#include <chrono>
#include <cstdint>
#include <cstring>
@@ -572,26 +573,28 @@
}
/**
- * Method returns Date Time information according to requested format
+ * Method returns Date Time information in the ISO extended format
*
- * @param[in] time time in second since the Epoch
+ * @param[in] timestamp in second since the Epoch; it can be negative
*
- * @return Date Time according to requested format
+ * @return Date Time in the ISO extended format
*/
-inline std::string getDateTime(const std::time_t& time)
+inline std::string getDateTime(boost::posix_time::seconds secondsSinceEpoch)
{
- std::array<char, 128> dateTime;
- std::string redfishDateTime("0000-00-00T00:00:00Z00:00");
+ boost::posix_time::ptime epoch(boost::gregorian::date(1970, 1, 1));
+ boost::posix_time::ptime time = epoch + secondsSinceEpoch;
+ // append zero offset to the end according to the Redfish spec for Date-Time
+ return boost::posix_time::to_iso_extended_string(time) + 'Z';
+}
- if (std::strftime(dateTime.begin(), dateTime.size(), "%FT%T%z",
- std::localtime(&time)))
- {
- // insert the colon required by the ISO 8601 standard
- redfishDateTime = std::string(dateTime.data());
- redfishDateTime.insert(redfishDateTime.end() - 2, ':');
- }
+inline std::string getDateTimeUint(uint64_t secondsSinceEpoch)
+{
+ return getDateTime(boost::posix_time::seconds(secondsSinceEpoch));
+}
- return redfishDateTime;
+inline std::string getDateTimeStdtime(std::time_t secondsSinceEpoch)
+{
+ return getDateTime(boost::posix_time::seconds(secondsSinceEpoch));
}
/**
@@ -606,7 +609,7 @@
inline std::pair<std::string, std::string> getDateTimeOffsetNow()
{
std::time_t time = std::time(nullptr);
- std::string dateTime = getDateTime(time);
+ std::string dateTime = getDateTimeStdtime(time);
/* extract the local Time Offset value from the
* recevied dateTime string.
diff --git a/redfish-core/lib/certificate_service.hpp b/redfish-core/lib/certificate_service.hpp
index 045426e..8a78317 100644
--- a/redfish-core/lib/certificate_service.hpp
+++ b/redfish-core/lib/certificate_service.hpp
@@ -643,9 +643,8 @@
std::get_if<uint64_t>(&property.second);
if (value)
{
- std::time_t time = static_cast<std::time_t>(*value);
asyncResp->res.jsonValue["ValidNotAfter"] =
- crow::utility::getDateTime(time);
+ crow::utility::getDateTimeUint(*value);
}
}
else if (property.first == "ValidNotBefore")
@@ -654,9 +653,8 @@
std::get_if<uint64_t>(&property.second);
if (value)
{
- std::time_t time = static_cast<std::time_t>(*value);
asyncResp->res.jsonValue["ValidNotBefore"] =
- crow::utility::getDateTime(time);
+ crow::utility::getDateTimeUint(*value);
}
}
}
diff --git a/redfish-core/lib/log_services.hpp b/redfish-core/lib/log_services.hpp
index fb515fe..074c927 100644
--- a/redfish-core/lib/log_services.hpp
+++ b/redfish-core/lib/log_services.hpp
@@ -180,8 +180,7 @@
<< strerror(-ret);
return false;
}
- entryTimestamp = crow::utility::getDateTime(
- static_cast<std::time_t>(timestamp / 1000 / 1000));
+ entryTimestamp = crow::utility::getDateTimeUint(timestamp / 1000 / 1000);
return true;
}
@@ -419,7 +418,7 @@
{
continue;
}
- std::time_t timestamp;
+ uint64_t timestamp = 0;
uint64_t size = 0;
std::string dumpStatus;
nlohmann::json thisEntry;
@@ -485,8 +484,7 @@
messages::internalError(asyncResp->res);
break;
}
- timestamp =
- static_cast<std::time_t>(*usecsTimeStamp);
+ timestamp = (*usecsTimeStamp / 1000 / 1000);
break;
}
}
@@ -505,7 +503,8 @@
thisEntry["@odata.id"] = dumpPath + entryID;
thisEntry["Id"] = entryID;
thisEntry["EntryType"] = "Event";
- thisEntry["Created"] = crow::utility::getDateTime(timestamp);
+ thisEntry["Created"] =
+ crow::utility::getDateTimeUint(timestamp);
thisEntry["Name"] = dumpType + " Dump Entry";
thisEntry["AdditionalDataSizeBytes"] = size;
@@ -578,7 +577,7 @@
}
foundDumpEntry = true;
- std::time_t timestamp;
+ uint64_t timestamp = 0;
uint64_t size = 0;
std::string dumpStatus;
@@ -635,8 +634,7 @@
messages::internalError(asyncResp->res);
break;
}
- timestamp =
- static_cast<std::time_t>(*usecsTimeStamp);
+ timestamp = *usecsTimeStamp / 1000 / 1000;
break;
}
}
@@ -660,7 +658,7 @@
asyncResp->res.jsonValue["Id"] = entryID;
asyncResp->res.jsonValue["EntryType"] = "Event";
asyncResp->res.jsonValue["Created"] =
- crow::utility::getDateTime(timestamp);
+ crow::utility::getDateTimeUint(timestamp);
asyncResp->res.jsonValue["Name"] = dumpType + " Dump Entry";
asyncResp->res.jsonValue["AdditionalDataSizeBytes"] = size;
@@ -1490,9 +1488,9 @@
thisEntry["Severity"] =
translateSeverityDbusToRedfish(*severity);
thisEntry["Created"] =
- crow::utility::getDateTime(timestamp);
+ crow::utility::getDateTimeStdtime(timestamp);
thisEntry["Modified"] =
- crow::utility::getDateTime(updateTimestamp);
+ crow::utility::getDateTimeStdtime(updateTimestamp);
if (filePath != nullptr)
{
thisEntry["AdditionalDataURI"] =
@@ -1628,9 +1626,9 @@
asyncResp->res.jsonValue["Severity"] =
translateSeverityDbusToRedfish(*severity);
asyncResp->res.jsonValue["Created"] =
- crow::utility::getDateTime(timestamp);
+ crow::utility::getDateTimeStdtime(timestamp);
asyncResp->res.jsonValue["Modified"] =
- crow::utility::getDateTime(updateTimestamp);
+ crow::utility::getDateTimeStdtime(updateTimestamp);
if (filePath != nullptr)
{
asyncResp->res.jsonValue["AdditionalDataURI"] =
@@ -3154,8 +3152,8 @@
// Get the Created time from the timestamp
std::string entryTimeStr;
- entryTimeStr = crow::utility::getDateTime(
- static_cast<std::time_t>(usecSinceEpoch / 1000 / 1000));
+ entryTimeStr =
+ crow::utility::getDateTimeUint(usecSinceEpoch / 1000 / 1000);
// assemble messageArgs: BootIndex, TimeOffset(100us), PostCode(hex)
std::ostringstream hexCode;
diff --git a/redfish-core/lib/managers.hpp b/redfish-core/lib/managers.hpp
index 12af99d..ca5d8c5 100644
--- a/redfish-core/lib/managers.hpp
+++ b/redfish-core/lib/managers.hpp
@@ -1739,12 +1739,11 @@
}
// LastRebootTime is epoch time, in milliseconds
// https://github.com/openbmc/phosphor-dbus-interfaces/blob/7f9a128eb9296e926422ddc312c148b625890bb6/xyz/openbmc_project/State/BMC.interface.yaml#L19
- time_t lastResetTimeStamp =
- static_cast<time_t>(*lastResetTimePtr / 1000);
+ uint64_t lastResetTimeStamp = *lastResetTimePtr / 1000;
// Convert to ISO 8601 standard
aResp->res.jsonValue["LastResetTime"] =
- crow::utility::getDateTime(lastResetTimeStamp);
+ crow::utility::getDateTimeUint(lastResetTimeStamp);
},
"xyz.openbmc_project.State.BMC", "/xyz/openbmc_project/state/bmc0",
"org.freedesktop.DBus.Properties", "Get",
diff --git a/redfish-core/lib/metric_report.hpp b/redfish-core/lib/metric_report.hpp
index c959495..5ecab77 100644
--- a/redfish-core/lib/metric_report.hpp
+++ b/redfish-core/lib/metric_report.hpp
@@ -26,8 +26,7 @@
{"MetricId", id},
{"MetricProperty", metadata},
{"MetricValue", std::to_string(sensorValue)},
- {"Timestamp",
- crow::utility::getDateTime(static_cast<time_t>(timestamp))},
+ {"Timestamp", crow::utility::getDateTimeUint(timestamp)},
});
}
@@ -53,8 +52,7 @@
}
const auto& [timestamp, readings] = *timestampReadings;
- json["Timestamp"] =
- crow::utility::getDateTime(static_cast<time_t>(timestamp));
+ json["Timestamp"] = crow::utility::getDateTimeUint(timestamp);
json["MetricValues"] = toMetricValues(readings);
return true;
}
diff --git a/redfish-core/lib/systems.hpp b/redfish-core/lib/systems.hpp
index 2f88a77..8e1a6f4 100644
--- a/redfish-core/lib/systems.hpp
+++ b/redfish-core/lib/systems.hpp
@@ -1171,12 +1171,11 @@
}
// LastStateChangeTime is epoch time, in milliseconds
// https://github.com/openbmc/phosphor-dbus-interfaces/blob/33e8e1dd64da53a66e888d33dc82001305cd0bf9/xyz/openbmc_project/State/Chassis.interface.yaml#L19
- time_t lastResetTimeStamp =
- static_cast<time_t>(*lastResetTimePtr / 1000);
+ uint64_t lastResetTimeStamp = *lastResetTimePtr / 1000;
// Convert to ISO 8601 standard
aResp->res.jsonValue["LastResetTime"] =
- crow::utility::getDateTime(lastResetTimeStamp);
+ crow::utility::getDateTimeUint(lastResetTimeStamp);
},
"xyz.openbmc_project.State.Chassis",
"/xyz/openbmc_project/state/chassis0",
diff --git a/redfish-core/lib/task.hpp b/redfish-core/lib/task.hpp
index 22e9743..b7795a7 100644
--- a/redfish-core/lib/task.hpp
+++ b/redfish-core/lib/task.hpp
@@ -388,11 +388,11 @@
asyncResp->res.jsonValue["Name"] = "Task " + strParam;
asyncResp->res.jsonValue["TaskState"] = ptr->state;
asyncResp->res.jsonValue["StartTime"] =
- crow::utility::getDateTime(ptr->startTime);
+ crow::utility::getDateTimeStdtime(ptr->startTime);
if (ptr->endTime)
{
asyncResp->res.jsonValue["EndTime"] =
- crow::utility::getDateTime(*(ptr->endTime));
+ crow::utility::getDateTimeStdtime(*(ptr->endTime));
}
asyncResp->res.jsonValue["TaskStatus"] = ptr->status;
asyncResp->res.jsonValue["Messages"] = ptr->messages;