util: fix Y2038 problem in time::toString()
Avoid using time_t, which cannot correctly represent
dates after 19 January 2038 on 32-bit systems.
Change-Id: I04e8d31a1c04a112cbc81810143fe2233e6dcc22
Refs: #3915
diff --git a/src/util/time.cpp b/src/util/time.cpp
index d3e546a..64b43e3 100644
--- a/src/util/time.cpp
+++ b/src/util/time.cpp
@@ -105,7 +105,7 @@
const system_clock::TimePoint&
getUnixEpoch()
{
- static auto epoch = system_clock::from_time_t(0);
+ static constexpr system_clock::TimePoint epoch(seconds::zero());
return epoch;
}
@@ -121,23 +121,30 @@
return getUnixEpoch() + duration;
}
-std::string
-toIsoString(const system_clock::TimePoint& timePoint)
+static boost::posix_time::ptime
+convertToPosixTime(const system_clock::TimePoint& timePoint)
{
namespace bpt = boost::posix_time;
static bpt::ptime epoch(boost::gregorian::date(1970, 1, 1));
-#ifdef BOOST_DATE_TIME_POSIX_TIME_STD_CONFIG
- using BptResolutionUnit = nanoseconds;
+ using BptResolution =
+#if defined(BOOST_DATE_TIME_HAS_NANOSECONDS)
+ nanoseconds;
+#elif defined(BOOST_DATE_TIME_HAS_MICROSECONDS)
+ microseconds;
#else
- using BptResolutionUnit = microseconds;
+ milliseconds;
#endif
- constexpr auto unitsPerHour = duration_cast<BptResolutionUnit>(hours(1)).count();
+ constexpr auto unitsPerHour = duration_cast<BptResolution>(1_h).count();
- auto sinceEpoch = duration_cast<BptResolutionUnit>(timePoint - getUnixEpoch()).count();
- bpt::ptime ptime = epoch + bpt::time_duration(sinceEpoch / unitsPerHour, 0, 0, sinceEpoch % unitsPerHour);
+ auto sinceEpoch = duration_cast<BptResolution>(timePoint - getUnixEpoch()).count();
+ return epoch + bpt::time_duration(sinceEpoch / unitsPerHour, 0, 0, sinceEpoch % unitsPerHour);
+}
- return bpt::to_iso_string(ptime);
+std::string
+toIsoString(const system_clock::TimePoint& timePoint)
+{
+ return boost::posix_time::to_iso_string(convertToPosixTime(timePoint));
}
static system_clock::TimePoint
@@ -146,11 +153,11 @@
namespace bpt = boost::posix_time;
static bpt::ptime epoch(boost::gregorian::date(1970, 1, 1));
- // .total_seconds() has issue with large dates until Boost 1.66. See Issue #4478
- // from_time_t has issues with large dates on 32-bit platforms
- auto point = system_clock::time_point(seconds((ptime - epoch).ticks() / bpt::time_duration::ticks_per_second()));
- point += microseconds((ptime - epoch).total_microseconds() % 1000000);
- return point;
+ // .total_seconds() has an issue with large dates until Boost 1.66, see #4478.
+ // time_t overflows for large dates on 32-bit platforms (Y2038 problem).
+ auto sinceEpoch = ptime - epoch;
+ auto point = system_clock::TimePoint(seconds(sinceEpoch.ticks() / bpt::time_duration::ticks_per_second()));
+ return point + microseconds(sinceEpoch.total_microseconds() % 1000000);
}
system_clock::TimePoint
@@ -165,17 +172,13 @@
const std::locale& locale/* = std::locale("C")*/)
{
namespace bpt = boost::posix_time;
- bpt::ptime ptime = bpt::from_time_t(system_clock::to_time_t(timePoint));
- uint64_t micro = duration_cast<microseconds>(timePoint - getUnixEpoch()).count() % 1000000;
- ptime += bpt::microseconds(micro);
+ std::ostringstream os;
+ auto* facet = new bpt::time_facet(format.data());
+ os.imbue(std::locale(locale, facet));
+ os << convertToPosixTime(timePoint);
- bpt::time_facet* facet = new bpt::time_facet(format.c_str());
- std::ostringstream formattedTimePoint;
- formattedTimePoint.imbue(std::locale(locale, facet));
- formattedTimePoint << ptime;
-
- return formattedTimePoint.str();
+ return os.str();
}
system_clock::TimePoint
@@ -185,8 +188,8 @@
{
namespace bpt = boost::posix_time;
- bpt::time_input_facet* facet = new bpt::time_input_facet(format);
std::istringstream is(timePointStr);
+ auto* facet = new bpt::time_input_facet(format);
is.imbue(std::locale(locale, facet));
bpt::ptime ptime;
is >> ptime;
diff --git a/tests/unit-tests/util/time.t.cpp b/tests/unit-tests/util/time.t.cpp
index b2988f2..a60e2d9 100644
--- a/tests/unit-tests/util/time.t.cpp
+++ b/tests/unit-tests/util/time.t.cpp
@@ -52,15 +52,14 @@
BOOST_CHECK_EQUAL(fromIsoString("20140129T034247.032000"), referenceTime);
BOOST_CHECK_EQUAL(fromIsoString("20140129T034247.032000Z"), referenceTime);
- BOOST_CHECK_EQUAL(fromString("2014-01-29 03:42:47"),
- fromUnixTimestamp(seconds(1390966967)));
+ BOOST_CHECK_EQUAL(fromString("2014-01-29 03:42:47"), fromUnixTimestamp(1390966967_s));
// Unfortunately, not all systems has lv_LV locale installed :(
// BOOST_CHECK_EQUAL(fromString("2014. gada 29. JanvÄris", "%Y. gada %d. %B", std::locale("lv_LV.UTF-8")),
- // fromUnixTimestamp(seconds(1390953600)));
+ // fromUnixTimestamp(1390953600_s));
BOOST_CHECK_EQUAL(fromString("2014 -- 29 -- January", "%Y -- %d -- %B", std::locale("C")),
- fromUnixTimestamp(seconds(1390953600)));
+ fromUnixTimestamp(1390953600_s));
}
BOOST_AUTO_TEST_CASE(SteadyClock)
@@ -80,13 +79,17 @@
BOOST_AUTO_TEST_CASE(LargeDates)
{
- auto value = fromUnixTimestamp(milliseconds(1390966967032LL));
+ auto value = fromUnixTimestamp(1390966967032_ms);
BOOST_CHECK_EQUAL(toIsoString(value), "20140129T034247.032000");
BOOST_CHECK_EQUAL(fromIsoString("20140129T034247.032000"), value);
+ BOOST_CHECK_EQUAL(toString(value, "%Y-%m-%d %H:%M:%S%F"), "2014-01-29 03:42:47.032000");
+ BOOST_CHECK_EQUAL(fromString("2014-01-29 03:42:47.032000", "%Y-%m-%d %H:%M:%S%F"), value);
value += 36524_days;
BOOST_CHECK_EQUAL(toIsoString(value), "21140129T034247.032000");
BOOST_CHECK_EQUAL(fromIsoString("21140129T034247.032000"), value);
+ BOOST_CHECK_EQUAL(toString(value, "%Y-%m-%d %H:%M:%S%F"), "2114-01-29 03:42:47.032000");
+ BOOST_CHECK_EQUAL(fromString("2114-01-29 03:42:47.03200", "%Y-%m-%d %H:%M:%S%F"), value);
}
BOOST_AUTO_TEST_CASE(Literals)