Support ProductionDate report

DSP0268 Redfish Data Model Specification provides ProductionDate
property for various schema. phosphor-dbus-interface [1] also
supports BuildDate property which we can map from FRU data, like
`Board Manufacturer Date`.
This adds code to support which gets the date from BuildDate
property and report via Redfish.

[1] https://github.com/openbmc/phosphor-dbus-interfaces/blob/master/yaml/xyz/openbmc_project/Inventory/Decorator/Asset.interface.yaml#L16

Tested: Redfish Validator passes

Signed-off-by: Hieu Huynh <hieuh@os.amperecomputing.com>
Change-Id: Ie4ae564fa00218e7896f04ec61941fa92b23b912
diff --git a/redfish-core/include/utils/time_utils.hpp b/redfish-core/include/utils/time_utils.hpp
index fbcd39e..a962695 100644
--- a/redfish-core/include/utils/time_utils.hpp
+++ b/redfish-core/include/utils/time_utils.hpp
@@ -1,5 +1,6 @@
 #pragma once
 
+#include "app.hpp"
 #include "logging.hpp"
 
 #include <algorithm>
@@ -377,5 +378,47 @@
 
 using usSinceEpoch = std::chrono::duration<int64_t, std::micro>;
 std::optional<usSinceEpoch> dateStringToEpoch(std::string_view datetime);
+
+/**
+ * @brief Returns the datetime in ISO 8601 format
+ *
+ * @param[in] std::string_view the date of item manufacture in ISO 8601 format,
+ *            either as YYYYMMDD or YYYYMMDDThhmmssZ
+ * Ref: https://github.com/openbmc/phosphor-dbus-interfaces/blob/master/yaml/
+ *      xyz/openbmc_project/Inventory/Decorator/Asset.interface.yaml#L16
+ *
+ * @return std::string which consist the datetime
+ */
+inline std::optional<std::string> getDateTimeIso8601(std::string_view datetime)
+{
+    std::optional<redfish::time_utils::usSinceEpoch> us =
+        redfish::time_utils::dateStringToEpoch(datetime);
+    if (!us)
+    {
+        return std::nullopt;
+    }
+    auto secondsDuration =
+        std::chrono::duration_cast<std::chrono::seconds>(*us);
+
+    return std::make_optional(redfish::time_utils::getDateTimeUint(
+        static_cast<uint64_t>(secondsDuration.count())));
+}
+
+/**
+ * @brief ProductionDate report
+ */
+inline void productionDateReport(crow::Response& res,
+                                 const std::string& buildDate)
+{
+    std::optional<std::string> valueStr =
+        redfish::time_utils::getDateTimeIso8601(buildDate);
+    if (!valueStr)
+    {
+        messages::internalError();
+        return;
+    }
+    res.jsonValue["ProductionDate"] = *valueStr;
+}
+
 } // namespace time_utils
 } // namespace redfish
diff --git a/redfish-core/lib/power_supply.hpp b/redfish-core/lib/power_supply.hpp
index 26ea26c..f934a3b 100644
--- a/redfish-core/lib/power_supply.hpp
+++ b/redfish-core/lib/power_supply.hpp
@@ -8,6 +8,7 @@
 #include "utils/chassis_utils.hpp"
 #include "utils/dbus_utils.hpp"
 #include "utils/json_utils.hpp"
+#include "utils/time_utils.hpp"
 
 #include <boost/system/error_code.hpp>
 #include <boost/url/format.hpp>
@@ -272,12 +273,13 @@
             const std::string* manufacturer = nullptr;
             const std::string* model = nullptr;
             const std::string* sparePartNumber = nullptr;
+            const std::string* buildDate = nullptr;
 
             const bool success = sdbusplus::unpackPropertiesNoThrow(
                 dbus_utils::UnpackErrorPrinter(), propertiesList, "PartNumber",
                 partNumber, "SerialNumber", serialNumber, "Manufacturer",
                 manufacturer, "Model", model, "SparePartNumber",
-                sparePartNumber);
+                sparePartNumber, "BuildDate", buildDate);
 
             if (!success)
             {
@@ -310,6 +312,11 @@
             {
                 asyncResp->res.jsonValue["SparePartNumber"] = *sparePartNumber;
             }
+
+            if (buildDate != nullptr)
+            {
+                time_utils::productionDateReport(asyncResp->res, *buildDate);
+            }
         });
 }
 
diff --git a/test/redfish-core/include/utils/time_utils_test.cpp b/test/redfish-core/include/utils/time_utils_test.cpp
index aa6f53b..882182a 100644
--- a/test/redfish-core/include/utils/time_utils_test.cpp
+++ b/test/redfish-core/include/utils/time_utils_test.cpp
@@ -218,5 +218,16 @@
     EXPECT_EQ(dateStringToEpoch("19991399"), std::nullopt);
 }
 
+TEST(Utility, GetDateTimeIso8601)
+{
+    EXPECT_EQ(getDateTimeIso8601("20230531"), "2023-05-31T00:00:00+00:00");
+
+    EXPECT_EQ(getDateTimeIso8601("20230531T000000Z"),
+              "2023-05-31T00:00:00+00:00");
+
+    // invalid datetime
+    EXPECT_EQ(getDateTimeIso8601("202305"), std::nullopt);
+}
+
 } // namespace
 } // namespace redfish::time_utils