From 4091724bea1d8d685234c59b9fc2375729cd1332 Mon Sep 17 00:00:00 2001 From: Igor Peshansky Date: Sat, 3 Mar 2018 19:15:24 -0500 Subject: [PATCH 01/15] Add initial unit test for time.cc. Fix a subtle bug exposed by the test. --- src/time.cc | 9 +++++++-- test/Makefile | 7 +++++-- test/time_unittest.cc | 34 ++++++++++++++++++++++++++++++++++ 3 files changed, 46 insertions(+), 4 deletions(-) create mode 100644 test/time_unittest.cc diff --git a/src/time.cc b/src/time.cc index 9f60454c..09167e4b 100644 --- a/src/time.cc +++ b/src/time.cc @@ -31,8 +31,11 @@ namespace { // See http://stackoverflow.com/questions/4137748. int UTCOffset() { const std::time_t local = std::time(nullptr); - const std::time_t utc = std::mktime(std::gmtime(&local)); - return utc - local; + std::tm utc_time = safe_gmtime(&local); + // Since we're only using this for conversion to UTC, always turn off DST. + utc_time.tm_isdst = 0; + const std::time_t utc = std::mktime(&utc_time); + return local - utc; } const int kUtcOffset = UTCOffset(); @@ -68,6 +71,8 @@ std::chrono::system_clock::time_point FromString(const std::string& s) { // TODO return std::chrono::system_clock::time_point(); } + // Our UTC offset constant assumes no DST. + tm.tm_isdst = 0; const std::time_t local_time = std::mktime(&tm); const std::time_t utc_time = local_time + kUtcOffset; std::chrono::system_clock::time_point sec = diff --git a/test/Makefile b/test/Makefile index 5b33fc2a..efea1665 100644 --- a/test/Makefile +++ b/test/Makefile @@ -21,8 +21,9 @@ TEST_DIR=. TEST_SOURCES=$(wildcard $(TEST_DIR)/*_unittest.cc) TEST_OBJS=$(TEST_SOURCES:$(TEST_DIR)/%.cc=%.o) TESTS=\ + base64_unittest \ format_unittest \ - base64_unittest + time_unittest GTEST_LIB=gtest_lib.a @@ -47,7 +48,6 @@ init-submodules: git submodule update --init $(GTEST_MODULE) touch init-submodules - $(SRC_DIR)/%.o: $(SRC_DIR)/%.cc cd $(SRC_DIR) && $(MAKE) $(@:$(SRC_DIR)/%=%) @@ -67,4 +67,7 @@ format_unittest: $(GTEST_LIB) format_unittest.o $(SRC_DIR)/format.o base64_unittest: $(GTEST_LIB) base64_unittest.o $(SRC_DIR)/base64.o $(CXX) $(LDFLAGS) $^ $(LDLIBS) -o $@ +time_unittest: $(GTEST_LIB) time_unittest.o $(SRC_DIR)/time.o + $(CXX) $(LDFLAGS) $^ $(LDLIBS) -o $@ + .PHONY: all test clean purge diff --git a/test/time_unittest.cc b/test/time_unittest.cc new file mode 100644 index 00000000..3749d39c --- /dev/null +++ b/test/time_unittest.cc @@ -0,0 +1,34 @@ +#include "../src/time.h" +#include "gtest/gtest.h" + +using namespace google; + +namespace { + +TEST(TimeTest, EpochToString) { + const std::chrono::system_clock::time_point epoch; + EXPECT_EQ( + "1970-01-01T00:00:00.000000000Z", + rfc3339::ToString(epoch) + ); +} + +TEST(TimeTest, RoundtripViaTimePoint) { + const std::chrono::system_clock::time_point t = + rfc3339::FromString("2018-03-03T01:23:45.678901234Z"); + EXPECT_EQ( + "2018-03-03T01:23:45.678901234Z", + rfc3339::ToString(t) + ); +} + +TEST(TimeTest, RoundtripViaString) { + const std::chrono::system_clock::time_point t = + std::chrono::system_clock::now(); + EXPECT_EQ( + t, + rfc3339::FromString(rfc3339::ToString(t)) + ); +} + +} From 6eaf96aa5d67084e90bc9a4f87311f69d2f6210a Mon Sep 17 00:00:00 2001 From: Igor Peshansky Date: Sat, 3 Mar 2018 19:17:47 -0500 Subject: [PATCH 02/15] Add test/.gitignore; normalize src/.gitignore. --- src/.gitignore | 2 +- test/.gitignore | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) create mode 100644 test/.gitignore diff --git a/src/.gitignore b/src/.gitignore index 7ab262d8..4dee5759 100644 --- a/src/.gitignore +++ b/src/.gitignore @@ -1,4 +1,4 @@ -metadatad init-submodules build-cpp-netlib build-yaml-cpp +metadatad diff --git a/test/.gitignore b/test/.gitignore new file mode 100644 index 00000000..d16f2f56 --- /dev/null +++ b/test/.gitignore @@ -0,0 +1,2 @@ +init-submodules +*_unittest From 35701a5729a8c417e99b89c2c9f939ac9f0857b1 Mon Sep 17 00:00:00 2001 From: Igor Peshansky Date: Sat, 3 Mar 2018 19:18:55 -0500 Subject: [PATCH 03/15] Allow running "make test" in src/. --- src/Makefile | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Makefile b/src/Makefile index da123cd7..afc41b43 100644 --- a/src/Makefile +++ b/src/Makefile @@ -60,6 +60,9 @@ INSTALL_DATA=$(INSTALL) -m 644 metadatad: $(LIBS) $(OBJS) $(CXX) -o $@ $(LDFLAGS) $^ $(LDLIBS) +test: + cd ../test && $(MAKE) test + $(OBJS): init-submodules install: metadatad From 615e6c69100c6a1249aa846a33b1b06c70775b9f Mon Sep 17 00:00:00 2001 From: Igor Peshansky Date: Fri, 2 Mar 2018 15:44:06 -0500 Subject: [PATCH 04/15] Handle timestamps without nanoseconds. --- src/time.cc | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/src/time.cc b/src/time.cc index 09167e4b..a9493299 100644 --- a/src/time.cc +++ b/src/time.cc @@ -60,14 +60,25 @@ std::string ToString(const std::chrono::system_clock::time_point& t) { std::chrono::system_clock::time_point FromString(const std::string& s) { std::tm tm; - const char* end = strptime(s.c_str(), "%Y-%m-%dT%H:%M:%S", &tm); - if (end == nullptr || end - s.c_str() != s.find('.')) { + char* const end = strptime(s.c_str(), "%Y-%m-%dT%H:%M:%S", &tm); + if (end == nullptr) { // TODO return std::chrono::system_clock::time_point(); } char* zone; - long ns = std::strtol(end + 1, &zone, 10); - if (zone <= end + 1 || *zone != 'Z' || *(zone+1) != '\0') { + long ns; + // Nanoseconds are optional. + if (end - s.c_str() == s.find('.')) { + ns = std::strtol(end + 1, &zone, 10); + if (zone <= end + 1) { + // TODO + return std::chrono::system_clock::time_point(); + } + } else { + zone = end; + ns = 0; + } + if (*zone != 'Z' || *(zone+1) != '\0') { // TODO return std::chrono::system_clock::time_point(); } From b0501e0bec3cb11e49e89c67d46e63784297921b Mon Sep 17 00:00:00 2001 From: Igor Peshansky Date: Fri, 2 Mar 2018 17:08:33 -0500 Subject: [PATCH 05/15] Ensure fractional seconds are converted to nanoseconds. --- src/time.cc | 31 ++++++++++++++++++++++++++----- 1 file changed, 26 insertions(+), 5 deletions(-) diff --git a/src/time.cc b/src/time.cc index a9493299..0e8b6346 100644 --- a/src/time.cc +++ b/src/time.cc @@ -58,28 +58,49 @@ std::string ToString(const std::chrono::system_clock::time_point& t) { return out.str(); } +namespace { + +long Exp10(std::size_t n) { + long result = 1; + for (size_t i = 0; i < n; ++i) { + result *= 10; + } + return result; +} + +} + std::chrono::system_clock::time_point FromString(const std::string& s) { std::tm tm; char* const end = strptime(s.c_str(), "%Y-%m-%dT%H:%M:%S", &tm); if (end == nullptr) { - // TODO + // TODO: Invalid time format. return std::chrono::system_clock::time_point(); } char* zone; long ns; - // Nanoseconds are optional. + // Fractional seconds are optional. if (end - s.c_str() == s.find('.')) { ns = std::strtol(end + 1, &zone, 10); - if (zone <= end + 1) { - // TODO + std::size_t length = zone - (end + 1); + if (length == 0) { + // TODO: Unable to parse fractional seconds. return std::chrono::system_clock::time_point(); } + if (length > 9) { + // More digits than can be stored as nanoseconds. + // TODO: Should this round (std::lround)? + ns /= Exp10(length - 9); + } else if (length < 9) { + // Need to add trailing zeros. + ns *= Exp10(9 - length); + } } else { zone = end; ns = 0; } if (*zone != 'Z' || *(zone+1) != '\0') { - // TODO + // TODO: Invalid timezone. return std::chrono::system_clock::time_point(); } // Our UTC offset constant assumes no DST. From bd90968e33a13579a14e7fc678ca255fdd4a4af1 Mon Sep 17 00:00:00 2001 From: Igor Peshansky Date: Sun, 4 Mar 2018 00:40:03 -0500 Subject: [PATCH 06/15] Ensure nanos start with a digit. --- src/time.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/time.cc b/src/time.cc index 0e8b6346..8d54d484 100644 --- a/src/time.cc +++ b/src/time.cc @@ -16,6 +16,7 @@ #include "time.h" +#include #include #include #include @@ -80,7 +81,7 @@ std::chrono::system_clock::time_point FromString(const std::string& s) { char* zone; long ns; // Fractional seconds are optional. - if (end - s.c_str() == s.find('.')) { + if (end - s.c_str() == s.find('.') && std::isdigit(*(end + 1))) { ns = std::strtol(end + 1, &zone, 10); std::size_t length = zone - (end + 1); if (length == 0) { From d0d054a5b4b36581a29117f4afc7e4b154728b58 Mon Sep 17 00:00:00 2001 From: Igor Peshansky Date: Sat, 3 Mar 2018 23:26:15 -0500 Subject: [PATCH 07/15] Add more tests for timestamp parsing. --- test/time_unittest.cc | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/test/time_unittest.cc b/test/time_unittest.cc index 3749d39c..db66d9e6 100644 --- a/test/time_unittest.cc +++ b/test/time_unittest.cc @@ -31,4 +31,31 @@ TEST(TimeTest, RoundtripViaString) { ); } +TEST(TimeTest, FromStringNoNanos) { + const std::chrono::system_clock::time_point t = + rfc3339::FromString("2018-03-03T01:23:45Z"); + EXPECT_EQ( + "2018-03-03T01:23:45.000000000Z", + rfc3339::ToString(t) + ); +} + +TEST(TimeTest, FromStringFewerDigits) { + const std::chrono::system_clock::time_point t = + rfc3339::FromString("2018-03-03T01:23:45.6789Z"); + EXPECT_EQ( + "2018-03-03T01:23:45.678900000Z", + rfc3339::ToString(t) + ); +} + +TEST(TimeTest, FromStringMoreDigits) { + const std::chrono::system_clock::time_point t = + rfc3339::FromString("2018-03-03T01:23:45.67890123456789Z"); + EXPECT_EQ( + "2018-03-03T01:23:45.678901234Z", + rfc3339::ToString(t) + ); +} + } From f715f2dd289dceb64129101089bb1ca678cd141f Mon Sep 17 00:00:00 2001 From: Igor Peshansky Date: Sun, 4 Mar 2018 00:15:07 -0500 Subject: [PATCH 08/15] Even more tests. --- test/time_unittest.cc | 81 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 81 insertions(+) diff --git a/test/time_unittest.cc b/test/time_unittest.cc index db66d9e6..c72d4ef3 100644 --- a/test/time_unittest.cc +++ b/test/time_unittest.cc @@ -58,4 +58,85 @@ TEST(TimeTest, FromStringMoreDigits) { ); } +TEST(TimeTest, FromStringPositiveNanos) { + const std::chrono::system_clock::time_point t = + rfc3339::FromString("2018-03-03T01:23:45.+678901234Z"); + EXPECT_EQ( + "1970-01-01T00:00:00.000000000Z", + rfc3339::ToString(t) + ); +} + +TEST(TimeTest, FromStringNegativeNanos) { + const std::chrono::system_clock::time_point t = + rfc3339::FromString("2018-03-03T01:23:45.-678901234Z"); + EXPECT_EQ( + "1970-01-01T00:00:00.000000000Z", + rfc3339::ToString(t) + ); +} + +TEST(TimeTest, FromStringPositiveSeconds) { + const std::chrono::system_clock::time_point t = + rfc3339::FromString("2018-03-03T01:23:+45.678901234Z"); + EXPECT_EQ( + "1970-01-01T00:00:00.000000000Z", + rfc3339::ToString(t) + ); +} + +TEST(TimeTest, FromStringNegativeSeconds) { + const std::chrono::system_clock::time_point t = + rfc3339::FromString("2018-03-03T01:23:-45.678901234Z"); + EXPECT_EQ( + "1970-01-01T00:00:00.000000000Z", + rfc3339::ToString(t) + ); +} + +TEST(TimeTest, FromStringHexSeconds) { + const std::chrono::system_clock::time_point t = + rfc3339::FromString("2018-03-03T01:23:0x45.678901234Z"); + EXPECT_EQ( + "1970-01-01T00:00:00.000000000Z", + rfc3339::ToString(t) + ); +} + +TEST(TimeTest, FromStringInfSeconds) { + const std::chrono::system_clock::time_point t1 = + rfc3339::FromString("2018-03-03T01:23:+infZ"); + EXPECT_EQ( + "1970-01-01T00:00:00.000000000Z", + rfc3339::ToString(t1) + ); + const std::chrono::system_clock::time_point t2 = + rfc3339::FromString("2018-03-03T01:23:-infZ"); + EXPECT_EQ( + "1970-01-01T00:00:00.000000000Z", + rfc3339::ToString(t2) + ); + const std::chrono::system_clock::time_point t3 = + rfc3339::FromString("2018-03-03T01:23:+nanZ"); + EXPECT_EQ( + "1970-01-01T00:00:00.000000000Z", + rfc3339::ToString(t3) + ); + const std::chrono::system_clock::time_point t4 = + rfc3339::FromString("2018-03-03T01:23:-nanZ"); + EXPECT_EQ( + "1970-01-01T00:00:00.000000000Z", + rfc3339::ToString(t4) + ); +} + +TEST(TimeTest, FromStringTooManySeconds) { + const std::chrono::system_clock::time_point t = + rfc3339::FromString("2018-03-03T01:23:1045.678901234Z"); + EXPECT_EQ( + "1970-01-01T00:00:00.000000000Z", + rfc3339::ToString(t) + ); +} + } From a45f57a03f1fcdcd98aaf3417354c177f206c1d6 Mon Sep 17 00:00:00 2001 From: Igor Peshansky Date: Mon, 5 Mar 2018 23:19:13 -0500 Subject: [PATCH 09/15] Add testing instructions to README.md. --- README.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/README.md b/README.md index db2a4433..b82e4233 100644 --- a/README.md +++ b/README.md @@ -89,6 +89,19 @@ This is the Stackdriver metadata agent. $ cd src $ make -j10 +# Testing + +1. Run all tests: + + $ cd src + $ make test + +2. Run individual tests: + + $ cd test + $ make + $ ./ + # Packaging 1. Build the DEB package: From 51ba2f31b09adc277efc6db4f5e3b3069aa92719 Mon Sep 17 00:00:00 2001 From: Igor Peshansky Date: Sat, 3 Mar 2018 23:27:24 -0500 Subject: [PATCH 10/15] An alternate implementation that parses seconds and nanoseconds as a double. --- src/time.cc | 46 +++++++++++----------------------------------- 1 file changed, 11 insertions(+), 35 deletions(-) diff --git a/src/time.cc b/src/time.cc index 8d54d484..47a14376 100644 --- a/src/time.cc +++ b/src/time.cc @@ -17,6 +17,7 @@ #include "time.h" #include +#include #include #include #include @@ -59,51 +60,26 @@ std::string ToString(const std::chrono::system_clock::time_point& t) { return out.str(); } -namespace { - -long Exp10(std::size_t n) { - long result = 1; - for (size_t i = 0; i < n; ++i) { - result *= 10; - } - return result; -} - -} - std::chrono::system_clock::time_point FromString(const std::string& s) { std::tm tm; - char* const end = strptime(s.c_str(), "%Y-%m-%dT%H:%M:%S", &tm); - if (end == nullptr) { + const char* end = strptime(s.c_str(), "%Y-%m-%dT%H:%M:", &tm); + if (end == nullptr || !std::isdigit(*end)) { // TODO: Invalid time format. return std::chrono::system_clock::time_point(); } char* zone; - long ns; - // Fractional seconds are optional. - if (end - s.c_str() == s.find('.') && std::isdigit(*(end + 1))) { - ns = std::strtol(end + 1, &zone, 10); - std::size_t length = zone - (end + 1); - if (length == 0) { - // TODO: Unable to parse fractional seconds. - return std::chrono::system_clock::time_point(); - } - if (length > 9) { - // More digits than can be stored as nanoseconds. - // TODO: Should this round (std::lround)? - ns /= Exp10(length - 9); - } else if (length < 9) { - // Need to add trailing zeros. - ns *= Exp10(9 - length); - } - } else { - zone = end; - ns = 0; + long sec_i = std::strtol(end, nullptr, 10); + double seconds = std::strtod(end, &zone); + if (sec_i < 0 || sec_i != static_cast(seconds)) { + // TODO: Seconds weren't decimal. + return std::chrono::system_clock::time_point(); } - if (*zone != 'Z' || *(zone+1) != '\0') { + if (zone <= end || *zone != 'Z' || *(zone+1) != '\0') { // TODO: Invalid timezone. return std::chrono::system_clock::time_point(); } + tm.tm_sec = sec_i; + long ns = std::lround((seconds - sec_i) * 10000000000) / 10; // Our UTC offset constant assumes no DST. tm.tm_isdst = 0; const std::time_t local_time = std::mktime(&tm); From 9a46457497448de1a56cced2c39a9dc5d1c7cc4c Mon Sep 17 00:00:00 2001 From: Igor Peshansky Date: Mon, 5 Mar 2018 22:09:39 -0500 Subject: [PATCH 11/15] Ensure that seconds is exactly 2 digits. --- src/time.cc | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/time.cc b/src/time.cc index 47a14376..76f8e4db 100644 --- a/src/time.cc +++ b/src/time.cc @@ -67,8 +67,13 @@ std::chrono::system_clock::time_point FromString(const std::string& s) { // TODO: Invalid time format. return std::chrono::system_clock::time_point(); } + char* point; + long sec_i = std::strtol(end, &point, 10); + if ((point - end) != 2) { + // TODO: Seconds wasn't 2 digits. + return std::chrono::system_clock::time_point(); + } char* zone; - long sec_i = std::strtol(end, nullptr, 10); double seconds = std::strtod(end, &zone); if (sec_i < 0 || sec_i != static_cast(seconds)) { // TODO: Seconds weren't decimal. From 256eb2b2c083304d25c6a258e12646a5512904b0 Mon Sep 17 00:00:00 2001 From: Igor Peshansky Date: Wed, 7 Mar 2018 18:18:18 -0500 Subject: [PATCH 12/15] Add more tests. --- test/time_unittest.cc | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/test/time_unittest.cc b/test/time_unittest.cc index c72d4ef3..cc478b1a 100644 --- a/test/time_unittest.cc +++ b/test/time_unittest.cc @@ -58,6 +58,15 @@ TEST(TimeTest, FromStringMoreDigits) { ); } +TEST(TimeTest, FromStringLargeNanos) { + const std::chrono::system_clock::time_point t = + rfc3339::FromString("2018-03-03T01:23:45.9876543210987Z"); + EXPECT_EQ( + "2018-03-03T01:23:45.987654321Z", + rfc3339::ToString(t) + ); +} + TEST(TimeTest, FromStringPositiveNanos) { const std::chrono::system_clock::time_point t = rfc3339::FromString("2018-03-03T01:23:45.+678901234Z"); @@ -78,7 +87,7 @@ TEST(TimeTest, FromStringNegativeNanos) { TEST(TimeTest, FromStringPositiveSeconds) { const std::chrono::system_clock::time_point t = - rfc3339::FromString("2018-03-03T01:23:+45.678901234Z"); + rfc3339::FromString("2018-03-03T01:23:+4.567890123Z"); EXPECT_EQ( "1970-01-01T00:00:00.000000000Z", rfc3339::ToString(t) @@ -87,7 +96,7 @@ TEST(TimeTest, FromStringPositiveSeconds) { TEST(TimeTest, FromStringNegativeSeconds) { const std::chrono::system_clock::time_point t = - rfc3339::FromString("2018-03-03T01:23:-45.678901234Z"); + rfc3339::FromString("2018-03-03T01:23:-4.567890123Z"); EXPECT_EQ( "1970-01-01T00:00:00.000000000Z", rfc3339::ToString(t) @@ -139,4 +148,13 @@ TEST(TimeTest, FromStringTooManySeconds) { ); } +TEST(TimeTest, FromStringScientificSeconds) { + const std::chrono::system_clock::time_point t = + rfc3339::FromString("2018-03-03T01:23:45e+0Z"); + EXPECT_EQ( + "1970-01-01T00:00:00.000000000Z", + rfc3339::ToString(t) + ); +} + } From bcc75b6d8e186eac46c23265e04adaef97c27dd6 Mon Sep 17 00:00:00 2001 From: Igor Peshansky Date: Wed, 7 Mar 2018 18:18:26 -0500 Subject: [PATCH 13/15] Remove useless check and add a clarifying comment. --- src/time.cc | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/time.cc b/src/time.cc index 76f8e4db..ef23ac82 100644 --- a/src/time.cc +++ b/src/time.cc @@ -75,7 +75,7 @@ std::chrono::system_clock::time_point FromString(const std::string& s) { } char* zone; double seconds = std::strtod(end, &zone); - if (sec_i < 0 || sec_i != static_cast(seconds)) { + if (sec_i != static_cast(seconds)) { // TODO: Seconds weren't decimal. return std::chrono::system_clock::time_point(); } @@ -84,6 +84,8 @@ std::chrono::system_clock::time_point FromString(const std::string& s) { return std::chrono::system_clock::time_point(); } tm.tm_sec = sec_i; + static_assert(sizeof(long) == 8, "long is too small"); + // Truncate to 9 digits by rounding to 10 and discarding the last one. long ns = std::lround((seconds - sec_i) * 10000000000) / 10; // Our UTC offset constant assumes no DST. tm.tm_isdst = 0; From 603e1ac03900dbe5385f0b064aedf6707d98bef6 Mon Sep 17 00:00:00 2001 From: Igor Peshansky Date: Wed, 7 Mar 2018 20:04:17 -0500 Subject: [PATCH 14/15] Ensure that the time is in the right format before parsing it as a double. --- src/time.cc | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/src/time.cc b/src/time.cc index ef23ac82..e5f841db 100644 --- a/src/time.cc +++ b/src/time.cc @@ -74,15 +74,25 @@ std::chrono::system_clock::time_point FromString(const std::string& s) { return std::chrono::system_clock::time_point(); } char* zone; - double seconds = std::strtod(end, &zone); - if (sec_i != static_cast(seconds)) { - // TODO: Seconds weren't decimal. - return std::chrono::system_clock::time_point(); + if (*point == '.') { + long nanos = std::strtol(point + 1, &zone, 10); + if (zone <= point + 1) { + // TODO: Missing nanoseconds. + return std::chrono::system_clock::time_point(); + } + } else { + zone = point; } - if (zone <= end || *zone != 'Z' || *(zone+1) != '\0') { + if (*zone != 'Z' || *(zone+1) != '\0') { // TODO: Invalid timezone. return std::chrono::system_clock::time_point(); } + char* d_end; + double seconds = std::strtod(end, &d_end); + if (d_end != zone) { + // TODO: Internal error. + return std::chrono::system_clock::time_point(); + } tm.tm_sec = sec_i; static_assert(sizeof(long) == 8, "long is too small"); // Truncate to 9 digits by rounding to 10 and discarding the last one. From e70bb6c2cb14603214679a27e21fba4ae1e3e6df Mon Sep 17 00:00:00 2001 From: Igor Peshansky Date: Fri, 9 Mar 2018 15:10:24 -0500 Subject: [PATCH 15/15] Keep the original time format. --- src/time.cc | 23 ++++++++--------------- 1 file changed, 8 insertions(+), 15 deletions(-) diff --git a/src/time.cc b/src/time.cc index e5f841db..687fad8c 100644 --- a/src/time.cc +++ b/src/time.cc @@ -62,41 +62,34 @@ std::string ToString(const std::chrono::system_clock::time_point& t) { std::chrono::system_clock::time_point FromString(const std::string& s) { std::tm tm; - const char* end = strptime(s.c_str(), "%Y-%m-%dT%H:%M:", &tm); - if (end == nullptr || !std::isdigit(*end)) { + char* const end = strptime(s.c_str(), "%Y-%m-%dT%H:%M:%S", &tm); + if (end == nullptr || !std::isdigit(*(end - 2))) { // TODO: Invalid time format. return std::chrono::system_clock::time_point(); } - char* point; - long sec_i = std::strtol(end, &point, 10); - if ((point - end) != 2) { - // TODO: Seconds wasn't 2 digits. - return std::chrono::system_clock::time_point(); - } char* zone; - if (*point == '.') { - long nanos = std::strtol(point + 1, &zone, 10); - if (zone <= point + 1) { + if (*end == '.') { + (void)std::strtol(end + 1, &zone, 10); + if (zone <= end + 1) { // TODO: Missing nanoseconds. return std::chrono::system_clock::time_point(); } } else { - zone = point; + zone = end; } if (*zone != 'Z' || *(zone+1) != '\0') { // TODO: Invalid timezone. return std::chrono::system_clock::time_point(); } char* d_end; - double seconds = std::strtod(end, &d_end); + double seconds = std::strtod(end - 2, &d_end); if (d_end != zone) { // TODO: Internal error. return std::chrono::system_clock::time_point(); } - tm.tm_sec = sec_i; static_assert(sizeof(long) == 8, "long is too small"); // Truncate to 9 digits by rounding to 10 and discarding the last one. - long ns = std::lround((seconds - sec_i) * 10000000000) / 10; + long ns = std::lround((seconds - tm.tm_sec) * 10000000000) / 10; // Our UTC offset constant assumes no DST. tm.tm_isdst = 0; const std::time_t local_time = std::mktime(&tm);