Move to std::chrono

gcc-14 finally has calendar support.  Move the existing helper functions
over to using it, and deprecate the old methods (which involved a lot of
magic numbers).  The old methods are kept behind ifdefs for those still
compiling against gcc-13 for the moment, but will be removed when we
bump the requirements to gcc-14.

Unrelated to the cleanup, this also saves 2Kb on the compressed binary
size, presumably due to relying on implementations in libc++ instead of
in bmcweb.

Tested: Unit tests pass, good coverage.

Change-Id: I739a3e876e84150dc654b9dff5eb05261b1daaf5
Signed-off-by: Ed Tanous <etanous@nvidia.com>
diff --git a/redfish-core/include/utils/time_utils.hpp b/redfish-core/include/utils/time_utils.hpp
index a1f5c70..e2c42be 100644
--- a/redfish-core/include/utils/time_utils.hpp
+++ b/redfish-core/include/utils/time_utils.hpp
@@ -221,6 +221,10 @@
 
 namespace details
 {
+// This code is left for support of gcc < 13 which didn't have support for
+// timezones. It should be removed at some point in the future.
+#if __cpp_lib_chrono < 201907L
+
 // Returns year/month/day triple in civil calendar
 // Preconditions:  z is number of days since 1970-01-01 and is in the range:
 //                   [numeric_limits<Int>::min(),
@@ -311,6 +315,59 @@
                        month, day, hr.count(), mt.count(), se.count(),
                        subseconds);
 }
+
+#else
+
+template <typename IntType, typename Period>
+std::string toISO8061ExtendedStr(std::chrono::duration<IntType, Period> dur)
+{
+    using namespace std::literals::chrono_literals;
+
+    using SubType = std::chrono::duration<IntType, Period>;
+
+    // d is days since 1970-01-01
+    std::chrono::days days = std::chrono::floor<std::chrono::days>(dur);
+    std::chrono::sys_days sysDays(days);
+    std::chrono::year_month_day ymd(sysDays);
+
+    // Enforce 3 constraints
+    // the result cant under or overflow the calculation
+    // the resulting string needs to be representable as 4 digits
+    // The resulting string can't be before epoch
+    if (dur.count() <= 0)
+    {
+        BMCWEB_LOG_WARNING("Underflow from value {}", dur.count());
+        ymd = 1970y / std::chrono::January / 1d;
+        dur = std::chrono::duration<IntType, Period>::zero();
+    }
+    else if (dur > SubType::max() - std::chrono::days(1))
+    {
+        BMCWEB_LOG_WARNING("Overflow from value {}", dur.count());
+        ymd = 9999y / std::chrono::December / 31d;
+        dur = std::chrono::days(1) - SubType(1);
+    }
+    else if (ymd.year() >= 10000y)
+    {
+        BMCWEB_LOG_WARNING("Year {} not representable", ymd.year());
+        ymd = 9999y / std::chrono::December / 31d;
+        dur = std::chrono::days(1) - SubType(1);
+    }
+    else if (ymd.year() < 1970y)
+    {
+        BMCWEB_LOG_WARNING("Year {} not representable", ymd.year());
+        ymd = 1970y / std::chrono::January / 1d;
+        dur = SubType::zero();
+    }
+    else
+    {
+        // t is now time duration since midnight of day d
+        dur -= days;
+    }
+    std::chrono::hh_mm_ss<SubType> hms(dur);
+
+    return std::format("{}T{}+00:00", ymd, hms);
+}
+#endif
 } // namespace details
 
 // Returns the formatted date time string.