Call systemd SetTime directly

Internally inside phosphor-time-manager, the elapsed(uint64) dbus call
just forwards the request directly to systemd after static casting to
int64_t (signed).

bmcweb should just call systemd directly, for several reasons.

phosphor-timesyncd might block on other calls, given it's a single
threaded blocking design, due to bugs like #264.  Calling systemd
directly means that calls that don't require phosphor networkd won't be
blocked.

Calling systemd directly allows bmcweb to drop some code that parses a
date as int64_t, then converts it to uint64_t to fulfill the phosphor
datetime interface.  We can now keep int64_t all the way through.

Calling systemd directly allows bmcweb to give a more specific error
code in the case there NTP is enabled, registering a
PropertyValueConflict error, instead of a 500 InternalError.

Tested:
Patching DateTime property with NTP enabled returns 400,
PropertyValueConflict
```
curl -vvvv  -k --user "root:0penBmc" -H "Content-Type: application/json" -X PATCH -d '{"DateTime":"2020-12-15T15:40:52+00:00"}' https://192.168.7.2/redfish/v1/Managers/bmc
```

Disabling NTP using the following command:
```
curl -vvvv  -k --user "root:0penBmc" -H "Content-Type: application/json" -X PATCH -d '{"NTP":{"ProtocolEnabled":false}}' https://192.168.7.2/redfish/v1/Managers/bmc/NetworkProtocol
```

Allows the prior command to succeed.

[1] https://github.com/openbmc/phosphor-time-manager/blob/5ce9ac0e56440312997b25771507585905e8b360/bmc_epoch.cpp#L126

Change-Id: I6fbb6f63e17de8ab847ca5ed4eadc2bd313586d2
Signed-off-by: Ed Tanous <ed@tanous.net>
diff --git a/redfish-core/include/utils/time_utils.hpp b/redfish-core/include/utils/time_utils.hpp
index ee7dd4a..30011e3 100644
--- a/redfish-core/include/utils/time_utils.hpp
+++ b/redfish-core/include/utils/time_utils.hpp
@@ -417,7 +417,7 @@
     return std::make_pair(dateTime, timeOffset);
 }
 
-using usSinceEpoch = std::chrono::duration<uint64_t, std::micro>;
+using usSinceEpoch = std::chrono::duration<int64_t, std::micro>;
 std::optional<usSinceEpoch> dateStringToEpoch(std::string_view datetime);
 } // namespace time_utils
 } // namespace redfish
diff --git a/redfish-core/lib/managers.hpp b/redfish-core/lib/managers.hpp
index 527076f..26abec0 100644
--- a/redfish-core/lib/managers.hpp
+++ b/redfish-core/lib/managers.hpp
@@ -1857,8 +1857,37 @@
     });
 }
 
-inline void setDateTime(std::shared_ptr<bmcweb::AsyncResp> asyncResp,
-                        std::string datetime)
+inline void
+    afterSetDateTime(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
+                     const boost::system::error_code& ec,
+                     const sdbusplus::message_t& msg)
+{
+    if (ec)
+    {
+        BMCWEB_LOG_DEBUG("Failed to set elapsed time. DBUS response error {}",
+                         ec);
+        const sd_bus_error* dbusError = msg.get_error();
+        if (dbusError != nullptr)
+        {
+            std::string_view errorName(dbusError->name);
+            if (errorName ==
+                "org.freedesktop.timedate1.AutomaticTimeSyncEnabled")
+            {
+                BMCWEB_LOG_DEBUG("Setting conflict");
+                messages::propertyValueConflict(
+                    asyncResp->res, "DateTime",
+                    "Managers/NetworkProtocol/NTPProcotolEnabled");
+                return;
+            }
+        }
+        messages::internalError(asyncResp->res);
+        return;
+    }
+    asyncResp->res.result(boost::beast::http::status::no_content);
+}
+
+inline void setDateTime(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
+                        const std::string& datetime)
 {
     BMCWEB_LOG_DEBUG("Set date time: {}", datetime);
 
@@ -1870,22 +1899,17 @@
                                            "DateTime");
         return;
     }
-    sdbusplus::asio::setProperty(
-        *crow::connections::systemBus, "xyz.openbmc_project.Time.Manager",
-        "/xyz/openbmc_project/time/bmc", "xyz.openbmc_project.Time.EpochTime",
-        "Elapsed", us->count(),
-        [asyncResp{std::move(asyncResp)},
-         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(asyncResp->res);
-            return;
-        }
-        asyncResp->res.jsonValue["DateTime"] = datetime;
-    });
+    // Set the absolute datetime
+    bool relative = false;
+    bool interactive = false;
+    crow::connections::systemBus->async_method_call(
+        [asyncResp](const boost::system::error_code& ec,
+                    const sdbusplus::message_t& msg) {
+        afterSetDateTime(asyncResp, ec, msg);
+    },
+        "org.freedesktop.timedate1", "/org/freedesktop/timedate1",
+        "org.freedesktop.timedate1", "SetTime", us->count(), relative,
+        interactive);
 }
 
 inline void
@@ -2259,7 +2283,7 @@
         }
         if (datetime)
         {
-            setDateTime(asyncResp, std::move(*datetime));
+            setDateTime(asyncResp, *datetime);
         }
     });
 }
diff --git a/redfish-core/src/utils/time_utils.cpp b/redfish-core/src/utils/time_utils.cpp
index f427ba6..3aa3e79 100644
--- a/redfish-core/src/utils/time_utils.cpp
+++ b/redfish-core/src/utils/time_utils.cpp
@@ -12,13 +12,13 @@
 
 namespace redfish::time_utils
 {
-using usSinceEpoch = std::chrono::duration<uint64_t, std::micro>;
+
 std::optional<usSinceEpoch> dateStringToEpoch(std::string_view datetime)
 {
     for (const char* format : std::to_array({"%FT%T%Ez", "%FT%TZ", "%FT%T"}))
     {
         // Parse using signed so we can detect negative dates
-        std::chrono::sys_time<std::chrono::duration<int64_t, std::micro>> date;
+        std::chrono::sys_time<usSinceEpoch> date;
         std::istringstream iss(std::string{datetime});
 #if __cpp_lib_chrono >= 201907L
         namespace chrono_from_stream = std::chrono;
@@ -36,7 +36,7 @@
                 // More information left at end of string.
                 continue;
             }
-            return usSinceEpoch{date.time_since_epoch().count()};
+            return date.time_since_epoch();
         }
     }
     return std::nullopt;