Break out set time function and unit test it
This function is something that's easily unit tested. Do it.
Signed-off-by: Ed Tanous <edtanous@google.com>
Change-Id: I8d664c77ec4b3a9886128597449c5f9c041b86b3
diff --git a/redfish-core/include/utils/time_utils.hpp b/redfish-core/include/utils/time_utils.hpp
index 9b13147..f4fb0c0 100644
--- a/redfish-core/include/utils/time_utils.hpp
+++ b/redfish-core/include/utils/time_utils.hpp
@@ -2,6 +2,8 @@
#include "logging.hpp"
+#include <boost/date_time.hpp>
+
#include <algorithm>
#include <charconv>
#include <chrono>
@@ -414,5 +416,31 @@
return std::make_pair(dateTime, timeOffset);
}
+using usSinceEpoch = std::chrono::duration<uint64_t, std::micro>;
+inline std::optional<usSinceEpoch> dateStringToEpoch(std::string_view datetime)
+{
+ std::string date(datetime);
+ std::stringstream stream(date);
+ // Convert from ISO 8601 to boost local_time
+ // (BMC only has time in UTC)
+ boost::posix_time::ptime posixTime;
+ boost::posix_time::ptime epoch(boost::gregorian::date(1970, 1, 1));
+ // Facet gets deleted with the stringsteam
+ auto ifc = std::make_unique<boost::local_time::local_time_input_facet>(
+ "%Y-%m-%d %H:%M:%S%F %ZP");
+ stream.imbue(std::locale(stream.getloc(), ifc.release()));
+
+ boost::local_time::local_date_time ldt(boost::local_time::not_a_date_time);
+
+ if (!(stream >> ldt))
+ {
+ return std::nullopt;
+ }
+ posixTime = ldt.utc_time();
+ boost::posix_time::time_duration dur = posixTime - epoch;
+ uint64_t durMicroSecs = static_cast<uint64_t>(dur.total_microseconds());
+ return std::chrono::duration<uint64_t, std::micro>{durMicroSecs};
+}
+
} // namespace time_utils
} // namespace redfish
diff --git a/redfish-core/lib/managers.hpp b/redfish-core/lib/managers.hpp
index 74008cb..e8fa14b 100644
--- a/redfish-core/lib/managers.hpp
+++ b/redfish-core/lib/managers.hpp
@@ -27,7 +27,6 @@
#include "utils/systemd_utils.hpp"
#include "utils/time_utils.hpp"
-#include <boost/date_time.hpp>
#include <sdbusplus/asio/property.hpp>
#include <sdbusplus/unpack_properties.hpp>
@@ -1887,46 +1886,30 @@
{
BMCWEB_LOG_DEBUG << "Set date time: " << datetime;
- std::stringstream stream(datetime);
- // Convert from ISO 8601 to boost local_time
- // (BMC only has time in UTC)
- boost::posix_time::ptime posixTime;
- boost::posix_time::ptime epoch(boost::gregorian::date(1970, 1, 1));
- // Facet gets deleted with the stringsteam
- auto ifc = std::make_unique<boost::local_time::local_time_input_facet>(
- "%Y-%m-%d %H:%M:%S%F %ZP");
- stream.imbue(std::locale(stream.getloc(), ifc.release()));
-
- boost::local_time::local_date_time ldt(boost::local_time::not_a_date_time);
-
- if (stream >> ldt)
- {
- posixTime = ldt.utc_time();
- boost::posix_time::time_duration dur = posixTime - epoch;
- uint64_t durMicroSecs = static_cast<uint64_t>(dur.total_microseconds());
- crow::connections::systemBus->async_method_call(
- [aResp{std::move(aResp)}, datetime{std::move(datetime)}](
- const boost::system::error_code ec) {
- if (ec)
- {
- BMCWEB_LOG_DEBUG << "Failed to set elapsed time. "
- "DBUS response error "
- << ec;
- messages::internalError(aResp->res);
- return;
- }
- aResp->res.jsonValue["DateTime"] = datetime;
- },
- "xyz.openbmc_project.Time.Manager", "/xyz/openbmc_project/time/bmc",
- "org.freedesktop.DBus.Properties", "Set",
- "xyz.openbmc_project.Time.EpochTime", "Elapsed",
- dbus::utility::DbusVariantType(durMicroSecs));
- }
- else
+ std::optional<redfish::time_utils::usSinceEpoch> us =
+ redfish::time_utils::dateStringToEpoch(datetime);
+ if (!us)
{
messages::propertyValueFormatError(aResp->res, datetime, "DateTime");
return;
}
+ crow::connections::systemBus->async_method_call(
+ [aResp{std::move(aResp)},
+ datetime{std::move(datetime)}](const boost::system::error_code ec) {
+ if (ec)
+ {
+ BMCWEB_LOG_DEBUG << "Failed to set elapsed time. "
+ "DBUS response error "
+ << ec;
+ messages::internalError(aResp->res);
+ return;
+ }
+ aResp->res.jsonValue["DateTime"] = datetime;
+ },
+ "xyz.openbmc_project.Time.Manager", "/xyz/openbmc_project/time/bmc",
+ "org.freedesktop.DBus.Properties", "Set",
+ "xyz.openbmc_project.Time.EpochTime", "Elapsed",
+ dbus::utility::DbusVariantType(us->count()));
}
inline void requestRoutesManager(App& app)
diff --git a/test/redfish-core/include/utils/time_utils_test.cpp b/test/redfish-core/include/utils/time_utils_test.cpp
index 035d8ce..873a385 100644
--- a/test/redfish-core/include/utils/time_utils_test.cpp
+++ b/test/redfish-core/include/utils/time_utils_test.cpp
@@ -139,5 +139,37 @@
"1970-01-01T00:00:00.000000+00:00");
}
+TEST(Utility, DateStringToEpoch)
+{
+ EXPECT_EQ(dateStringToEpoch("2021-11-30T22:41:35.123456+00:00"),
+ usSinceEpoch{1638312095123456});
+ // no timezone
+ EXPECT_EQ(dateStringToEpoch("2021-11-30T22:41:35.123456"),
+ usSinceEpoch{1638312095123456});
+ // Milliseconds precision
+ EXPECT_EQ(dateStringToEpoch("2021-11-30T22:41:35.123"),
+ usSinceEpoch{1638312095123000});
+ // Seconds precision
+ EXPECT_EQ(dateStringToEpoch("2021-11-30T22:41:35"),
+ usSinceEpoch{1638312095000000});
+
+ // Non zero timezone
+ EXPECT_EQ(dateStringToEpoch("2021-11-30T22:41:35.123456+04:00"),
+ usSinceEpoch{1638297695123456});
+
+ // Epoch
+ EXPECT_EQ(dateStringToEpoch("1970-01-01T00:00:00.000000+00:00"),
+ usSinceEpoch{0});
+
+ // Max time
+ EXPECT_EQ(dateStringToEpoch("9999-12-31T23:59:59.999999+00:00"),
+ usSinceEpoch{253402300799999999});
+
+ // Underflow
+ // Currently gives wrong result
+ // EXPECT_EQ(dateStringToEpoch("1969-12-30T23:59:59.999999+00:00"),
+ // std::nullopt);
+}
+
} // namespace
} // namespace redfish::time_utils