Introduce new API from_string_maximum_error so that legitimate 1601 can be distinguished from parse failure.
This commit is contained in:
Родитель
7046d3a996
Коммит
faa72d6847
|
@ -603,14 +603,21 @@ public:
|
|||
}
|
||||
}
|
||||
|
||||
datetime() : m_interval(0) {}
|
||||
datetime() : m_interval(0) { }
|
||||
|
||||
/// <summary>
|
||||
/// Creates <c>datetime</c> from a string representing time in UTC in RFC 1123 format.
|
||||
/// Creates <c>datetime</c> from a string representing time in UTC in RFC 1123 or ISO 8601 format.
|
||||
/// </summary>
|
||||
/// <returns>Returns a <c>datetime</c> of zero if not successful.</returns>
|
||||
static _ASYNCRTIMP datetime __cdecl from_string(const utility::string_t& timestring, date_format format = RFC_1123);
|
||||
|
||||
/// <summary>
|
||||
/// Creates <c>datetime</c> from a string representing time in UTC in RFC 1123 or ISO 8601 format.
|
||||
/// </summary>
|
||||
/// <returns>Returns <c>datetime::maximum()</c> if not successful.</returns>
|
||||
static _ASYNCRTIMP datetime __cdecl from_string_maximum_error(const utility::string_t& timestring,
|
||||
date_format format = RFC_1123);
|
||||
|
||||
/// <summary>
|
||||
/// Returns a string representation of the <c>datetime</c>.
|
||||
/// </summary>
|
||||
|
@ -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<interval_type>(-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;
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
****/
|
||||
|
||||
#include "stdafx.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string>
|
||||
|
||||
|
@ -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
|
||||
|
|
Загрузка…
Ссылка в новой задаче