diff --git a/Release/include/cpprest/asyncrt_utils.h b/Release/include/cpprest/asyncrt_utils.h index 1b54f1634..cadc6c715 100644 --- a/Release/include/cpprest/asyncrt_utils.h +++ b/Release/include/cpprest/asyncrt_utils.h @@ -603,14 +603,21 @@ public: } } - datetime() : m_interval(0) {} + datetime() : m_interval(0) { } /// - /// Creates datetime from a string representing time in UTC in RFC 1123 format. + /// Creates datetime from a string representing time in UTC in RFC 1123 or ISO 8601 format. /// /// Returns a datetime of zero if not successful. static _ASYNCRTIMP datetime __cdecl from_string(const utility::string_t& timestring, date_format format = RFC_1123); + /// + /// Creates datetime from a string representing time in UTC in RFC 1123 or ISO 8601 format. + /// + /// Returns datetime::maximum() if not successful. + static _ASYNCRTIMP datetime __cdecl from_string_maximum_error(const utility::string_t& timestring, + date_format format = RFC_1123); + /// /// Returns a string representation of the datetime. /// @@ -628,13 +635,13 @@ public: bool operator==(datetime dt) const { return m_interval == dt.m_interval; } bool operator!=(const datetime& dt) const { return !(*this == dt); } - + bool operator>(const datetime& dt) const { return this->m_interval > dt.m_interval; } - + bool operator<(const datetime& dt) const { return this->m_interval < dt.m_interval; } - + bool operator>=(const datetime& dt) const { return this->m_interval >= dt.m_interval; } - + bool operator<=(const datetime& dt) const { return this->m_interval <= dt.m_interval; } static interval_type from_milliseconds(unsigned int milliseconds) { return milliseconds * _msTicks; } @@ -649,6 +656,8 @@ public: bool is_initialized() const { return m_interval != 0; } + static datetime maximum() { return datetime(static_cast(-1)); } + private: friend int operator-(datetime t1, datetime t2); @@ -659,7 +668,7 @@ private: static const interval_type _dayTicks = 24 * 60 * 60 * _secondTicks; // Private constructor. Use static methods to create an instance. - datetime(interval_type interval) : m_interval(interval) {} + datetime(interval_type interval) : m_interval(interval) { } // Storing as hundreds of nanoseconds 10e-7, i.e. 1 here equals 100ns. interval_type m_interval; diff --git a/Release/src/utilities/asyncrt_utils.cpp b/Release/src/utilities/asyncrt_utils.cpp index 7548bc5ea..e392fc15f 100644 --- a/Release/src/utilities/asyncrt_utils.cpp +++ b/Release/src/utilities/asyncrt_utils.cpp @@ -995,10 +995,20 @@ zone = "UT" / "GMT" ; Universal Time ; hours+min. (HHMM) */ - datetime __cdecl datetime::from_string(const utility::string_t& dateString, date_format format) { - datetime result; + auto result = from_string_maximum_error(dateString, format); + if (result == datetime::maximum()) + { + return datetime(); + } + + return result; +} + +datetime __cdecl datetime::from_string_maximum_error(const utility::string_t& dateString, date_format format) +{ + datetime result = datetime::maximum(); int64_t secondsSince1900; uint64_t fracSec = 0; auto str = dateString.c_str(); diff --git a/Release/tests/functional/utils/datetime.cpp b/Release/tests/functional/utils/datetime.cpp index 3949b000b..ab349beee 100644 --- a/Release/tests/functional/utils/datetime.cpp +++ b/Release/tests/functional/utils/datetime.cpp @@ -10,6 +10,7 @@ ****/ #include "stdafx.h" + #include #include @@ -81,6 +82,10 @@ SUITE(datetime) auto dt = utility::datetime::from_string(str, utility::datetime::ISO_8601); utility::string_t str2 = dt.to_string(utility::datetime::ISO_8601); VERIFY_ARE_EQUAL(str2, strExpected); + + auto dt_me = utility::datetime::from_string_maximum_error(str, utility::datetime::ISO_8601); + utility::string_t str3 = dt_me.to_string(utility::datetime::ISO_8601); + VERIFY_ARE_EQUAL(str3, strExpected); } void TestDateTimeRoundtrip(utility::string_t str) { TestDateTimeRoundtrip(str, str); } @@ -123,32 +128,18 @@ SUITE(datetime) TestDateTimeRoundtrip(_XPLATSTR("2013-11-19T14:30:59.5Z")); } - TEST(parsing_time_roundtrip_year_1900) - { - TestDateTimeRoundtrip(_XPLATSTR("1900-01-01T00:00:00Z")); - } + TEST(parsing_time_roundtrip_year_1900) { TestDateTimeRoundtrip(_XPLATSTR("1900-01-01T00:00:00Z")); } - TEST(parsing_time_roundtrip_year_9999) - { - TestDateTimeRoundtrip(_XPLATSTR("9999-12-31T23:59:59Z")); - } + TEST(parsing_time_roundtrip_year_9999) { TestDateTimeRoundtrip(_XPLATSTR("9999-12-31T23:59:59Z")); } - TEST(parsing_time_roundtrip_year_2016) - { - TestDateTimeRoundtrip(_XPLATSTR("2016-12-31T20:59:59Z")); - } + TEST(parsing_time_roundtrip_year_2016) { TestDateTimeRoundtrip(_XPLATSTR("2016-12-31T20:59:59Z")); } - TEST(parsing_time_roundtrip_year_2020) - { - TestDateTimeRoundtrip(_XPLATSTR("2020-12-31T20:59:59Z")); - } + TEST(parsing_time_roundtrip_year_2020) { TestDateTimeRoundtrip(_XPLATSTR("2020-12-31T20:59:59Z")); } - TEST(parsing_time_roundtrip_year_2021) - { - TestDateTimeRoundtrip(_XPLATSTR("2021-01-01T20:59:59Z")); - } + TEST(parsing_time_roundtrip_year_2021) { TestDateTimeRoundtrip(_XPLATSTR("2021-01-01T20:59:59Z")); } - TEST(emitting_time_correct_day) { + TEST(emitting_time_correct_day) + { const auto test = utility::datetime() + UINT64_C(132004507640000000); // 2019-04-22T23:52:44 is a Monday const auto actual = test.to_string(utility::datetime::RFC_1123); const utility::string_t expected(_XPLATSTR("Mon")); @@ -296,13 +287,13 @@ SUITE(datetime) _XPLATSTR("Thu, 01 Jan 1970 00:00:00 G"), _XPLATSTR("Thu, 01 Jan 1970 00:00:00 GM"), _XPLATSTR("Fri, 01 Jan 1970 00:00:00 GMT"), // wrong day - _XPLATSTR("01 Jan 1899 00:00:00 GMT"), // year too small - _XPLATSTR("01 Xxx 1971 00:00:00 GMT"), // month bad - _XPLATSTR("00 Jan 1971 00:00:00 GMT"), // day too small - _XPLATSTR("32 Jan 1971 00:00:00 GMT"), // day too big - _XPLATSTR("30 Feb 1971 00:00:00 GMT"), // day too big for feb - _XPLATSTR("30 Feb 1971 00:00:00 GMT"), // day too big for feb (non-leap year) - _XPLATSTR("32 Mar 1971 00:00:00 GMT"), // other months + _XPLATSTR("01 Jan 1899 00:00:00 GMT"), // year too small + _XPLATSTR("01 Xxx 1971 00:00:00 GMT"), // month bad + _XPLATSTR("00 Jan 1971 00:00:00 GMT"), // day too small + _XPLATSTR("32 Jan 1971 00:00:00 GMT"), // day too big + _XPLATSTR("30 Feb 1971 00:00:00 GMT"), // day too big for feb + _XPLATSTR("30 Feb 1971 00:00:00 GMT"), // day too big for feb (non-leap year) + _XPLATSTR("32 Mar 1971 00:00:00 GMT"), // other months _XPLATSTR("31 Apr 1971 00:00:00 GMT"), _XPLATSTR("32 May 1971 00:00:00 GMT"), _XPLATSTR("31 Jun 1971 00:00:00 GMT"), @@ -317,8 +308,8 @@ SUITE(datetime) _XPLATSTR("01 Jan 1971 00:60:00 GMT"), // minute too big _XPLATSTR("01 Jan 1971 00:00:70 GMT"), // second too big _XPLATSTR("01 Jan 1971 00:00:61 GMT"), - _XPLATSTR("01 Jan 1899 00:00:00 GMT"), // underflow - _XPLATSTR("01 Jan 1969 00:00:00 CEST"), // bad tz + _XPLATSTR("01 Jan 1899 00:00:00 GMT"), // underflow + _XPLATSTR("01 Jan 1969 00:00:00 CEST"), // bad tz _XPLATSTR("14 Jan 2019 23:16:21 G0100"), // bad tzoffsets _XPLATSTR("01 Jan 1970 00:00:00 +2400"), _XPLATSTR("01 Jan 1970 00:00:00 -3000"), @@ -332,6 +323,8 @@ SUITE(datetime) { auto dt = utility::datetime::from_string(str, utility::datetime::RFC_1123); VERIFY_ARE_EQUAL(0, dt.to_interval()); + auto dt_me = utility::datetime::from_string_maximum_error(str, utility::datetime::RFC_1123); + VERIFY_ARE_EQUAL(utility::datetime::maximum(), dt_me); } } @@ -484,7 +477,7 @@ SUITE(datetime) _XPLATSTR("1971-01-01T00:60:00Z"), // minute too big _XPLATSTR("1971-01-01T00:00:70Z"), // second too big _XPLATSTR("1971-01-01T00:00:61Z"), - _XPLATSTR("1899-01-01T00:00:00Z"), // underflow + _XPLATSTR("1899-01-01T00:00:00Z"), // underflow _XPLATSTR("1900-01-01T00:00:00+00:01"), // time zone underflow // _XPLATSTR("1970-01-01T00:00:00.Z"), // accepted as invalid timezone above _XPLATSTR("1970-01-01T00:00:00+24:00"), // bad tzoffsets @@ -499,23 +492,22 @@ SUITE(datetime) { auto dt = utility::datetime::from_string(str, utility::datetime::ISO_8601); VERIFY_ARE_EQUAL(dt.to_interval(), 0); + auto dt_me = utility::datetime::from_string_maximum_error(str, utility::datetime::ISO_8601); + VERIFY_ARE_EQUAL(dt_me, utility::datetime::maximum()); } } - TEST(can_emit_nt_epoch_zero) + TEST(can_emit_nt_epoch_zero_rfc_1123) { - // ISO 8601 - { - auto result = utility::datetime{}.to_string(utility::datetime::RFC_1123); - VERIFY_ARE_EQUAL(_XPLATSTR("Mon, 01 Jan 1601 00:00:00 GMT"), result); - } - // ISO 8601 - { - auto result = utility::datetime{}.to_string(utility::datetime::ISO_8601); - VERIFY_ARE_EQUAL(_XPLATSTR("1601-01-01T00:00:00Z"), result); - } + auto result = utility::datetime {}.to_string(utility::datetime::RFC_1123); + VERIFY_ARE_EQUAL(_XPLATSTR("Mon, 01 Jan 1601 00:00:00 GMT"), result); } + TEST(can_emit_nt_epoch_zero_iso_8601) + { + auto result = utility::datetime {}.to_string(utility::datetime::ISO_8601); + VERIFY_ARE_EQUAL(_XPLATSTR("1601-01-01T00:00:00Z"), result); + } } // SUITE(datetime) } // namespace utils_tests