Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Introduce new API from_string_maximum_error so that legitimate 1601 c…
…an be distinguished from parse failure.
  • Loading branch information
BillyONeal committed Jan 26, 2021
commit faa72d6847e329c97c540a34018790fceb68baf8
23 changes: 16 additions & 7 deletions Release/include/cpprest/asyncrt_utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -603,14 +603,21 @@ class datetime
}
}

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>
Expand All @@ -628,13 +635,13 @@ class datetime
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; }
Expand All @@ -649,6 +656,8 @@ class datetime

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);

Expand All @@ -659,7 +668,7 @@ class datetime
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;
Expand Down
14 changes: 12 additions & 2 deletions Release/src/utilities/asyncrt_utils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down
76 changes: 34 additions & 42 deletions Release/tests/functional/utils/datetime.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
****/

#include "stdafx.h"

#include <stdint.h>
#include <string>

Expand Down Expand Up @@ -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); }
Expand Down Expand Up @@ -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"));
Expand Down Expand Up @@ -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"),
Expand All @@ -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"),
Expand All @@ -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);
}
}

Expand Down Expand Up @@ -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
Expand All @@ -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
Expand Down