From 52554fd10f66af6c2118babc6e63b14163e5ef65 Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Fri, 26 Apr 2019 09:47:30 +0200 Subject: [PATCH] apply UTC offset during timestamp conversion Glibc strptime(3) does not automatically apply the UTC offset when present but stores it in the member tm_gmtoff. (Probably because the standard mandates different, insufficient means of timezone handling that need to be supported for portability.) Thus we do that right in the constructor immediately after we have obtained a valid struct tm. --- src/timefunc.cpp | 11 ++++++++++- test/test_timefunc.cpp | 37 +++++++++++++++++++++++++++++++++++++ 2 files changed, 47 insertions(+), 1 deletions(-) diff --git a/src/timefunc.cpp b/src/timefunc.cpp index 805ffae..6fe2b98 100644 --- a/src/timefunc.cpp +++ b/src/timefunc.cpp @@ -1110,7 +1110,8 @@ namespace clock { memcpy (&tmp_tm, &tm, sizeof (tmp_tm)); errno = 0; - const time_t t = mktime (&tmp_tm); + + time_t t = mktime (&tmp_tm); if (t == - 1) { /* Glibc does not set errno on out-of-range here! */ const char *datestr = asctime (&tm); throw conversion_error (errno, @@ -1119,6 +1120,14 @@ namespace clock { + "}"); } + /* + * The timezone shenanigans are there to force we end up using UTC + * internally. The trouble with mktime(3) is that the unix timestamp + * is defined as “seconds since epoch” but “expressed as local time”. + */ + t += tm.tm_gmtoff; + t -= timezone; + tmp_time = Time (t, 0l, id, var); this->swap (tmp_time); diff --git a/test/test_timefunc.cpp b/test/test_timefunc.cpp index 1ee6e47..5fc58e5 100644 --- a/test/test_timefunc.cpp +++ b/test/test_timefunc.cpp @@ -1229,6 +1229,43 @@ BOOST_AUTO_TEST_SUITE(Clock) BOOST_CHECK_EQUAL(*t2->format_iso8601 (true, true, false, false), in2); } + BOOST_AUTO_TEST_CASE(FromString_iso8601_offset) + { + const std::string in1 ("2019-04-25T13:41:47Z"); + const std::string in2 ("2019-04-25T13:41:47+0200"); /* = UTC(in1 + 2h) */ + const std::string in3 ("2019-04-25T15:41:47+0000"); /* = UTC(in2) */ + + this->set_utc (); + boost::optional t1 = I2n::clock::time_of_iso8601 (in1, true, true, true); + boost::optional t2 = I2n::clock::time_of_iso8601 (in2, true, true, true); + boost::optional t3 = I2n::clock::time_of_iso8601 (in3, true, true, true); + + BOOST_CHECK(t1); + BOOST_CHECK(t2); + BOOST_CHECK(t3); + + BOOST_CHECK_EQUAL(*t1->format_iso8601 (), "2019-04-25T13:41:47+0000"); + BOOST_CHECK_EQUAL(t1->get_sec (), 1556199707); + BOOST_CHECK_EQUAL(*t2->format_iso8601 (), "2019-04-25T15:41:47+0000"); + BOOST_CHECK_EQUAL(t2->get_sec (), t1->get_sec () + 2 * 60 * 60); + BOOST_CHECK_EQUAL(*t2, *t3); + BOOST_CHECK_EQUAL(*t3->format_iso8601 (), "2019-04-25T15:41:47+0000"); + } + + BOOST_AUTO_TEST_CASE(FromString_iso8601_tzdiff) + { + const std::string in ("2019-04-26T13:45:02+0000"); + + this->set_tz ("UTC"); + boost::optional t1 = I2n::clock::time_of_iso8601 (in, true, true, false); + + this->set_tz ("CET"); + boost::optional t2 = I2n::clock::time_of_iso8601 (in, true, true, false); + + BOOST_CHECK_EQUAL(*t1->format_iso8601 (true, true, true, true), in); + BOOST_CHECK_EQUAL(*t2->format_iso8601 (true, true, true, true), "2019-04-26T13:45:02+0000"); + } + BOOST_AUTO_TEST_CASE(FromString_iso8601_32bit_time_t_err) { const std::string timeless ("11:11:11"); -- 1.7.1