Introduce new API from_string_maximum_error so that legitimate 1601 can be distinguished from parse failure.

This commit is contained in:
Billy Robert O'Neal III 2021-01-25 20:15:04 -08:00
Родитель 7046d3a996
Коммит faa72d6847
3 изменённых файлов: 62 добавлений и 51 удалений

Просмотреть файл

@ -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