Skip to content

Commit faa72d6

Browse files
committed
Introduce new API from_string_maximum_error so that legitimate 1601 can be distinguished from parse failure.
1 parent 7046d3a commit faa72d6

File tree

3 files changed

+62
-51
lines changed

3 files changed

+62
-51
lines changed

Release/include/cpprest/asyncrt_utils.h

+16-7
Original file line numberDiff line numberDiff line change
@@ -603,14 +603,21 @@ class datetime
603603
}
604604
}
605605

606-
datetime() : m_interval(0) {}
606+
datetime() : m_interval(0) { }
607607

608608
/// <summary>
609-
/// Creates <c>datetime</c> from a string representing time in UTC in RFC 1123 format.
609+
/// Creates <c>datetime</c> from a string representing time in UTC in RFC 1123 or ISO 8601 format.
610610
/// </summary>
611611
/// <returns>Returns a <c>datetime</c> of zero if not successful.</returns>
612612
static _ASYNCRTIMP datetime __cdecl from_string(const utility::string_t& timestring, date_format format = RFC_1123);
613613

614+
/// <summary>
615+
/// Creates <c>datetime</c> from a string representing time in UTC in RFC 1123 or ISO 8601 format.
616+
/// </summary>
617+
/// <returns>Returns <c>datetime::maximum()</c> if not successful.</returns>
618+
static _ASYNCRTIMP datetime __cdecl from_string_maximum_error(const utility::string_t& timestring,
619+
date_format format = RFC_1123);
620+
614621
/// <summary>
615622
/// Returns a string representation of the <c>datetime</c>.
616623
/// </summary>
@@ -628,13 +635,13 @@ class datetime
628635
bool operator==(datetime dt) const { return m_interval == dt.m_interval; }
629636

630637
bool operator!=(const datetime& dt) const { return !(*this == dt); }
631-
638+
632639
bool operator>(const datetime& dt) const { return this->m_interval > dt.m_interval; }
633-
640+
634641
bool operator<(const datetime& dt) const { return this->m_interval < dt.m_interval; }
635-
642+
636643
bool operator>=(const datetime& dt) const { return this->m_interval >= dt.m_interval; }
637-
644+
638645
bool operator<=(const datetime& dt) const { return this->m_interval <= dt.m_interval; }
639646

640647
static interval_type from_milliseconds(unsigned int milliseconds) { return milliseconds * _msTicks; }
@@ -649,6 +656,8 @@ class datetime
649656

650657
bool is_initialized() const { return m_interval != 0; }
651658

659+
static datetime maximum() { return datetime(static_cast<interval_type>(-1)); }
660+
652661
private:
653662
friend int operator-(datetime t1, datetime t2);
654663

@@ -659,7 +668,7 @@ class datetime
659668
static const interval_type _dayTicks = 24 * 60 * 60 * _secondTicks;
660669

661670
// Private constructor. Use static methods to create an instance.
662-
datetime(interval_type interval) : m_interval(interval) {}
671+
datetime(interval_type interval) : m_interval(interval) { }
663672

664673
// Storing as hundreds of nanoseconds 10e-7, i.e. 1 here equals 100ns.
665674
interval_type m_interval;

Release/src/utilities/asyncrt_utils.cpp

+12-2
Original file line numberDiff line numberDiff line change
@@ -995,10 +995,20 @@ zone = "UT" / "GMT" ; Universal Time
995995
; hours+min. (HHMM)
996996
*/
997997

998-
999998
datetime __cdecl datetime::from_string(const utility::string_t& dateString, date_format format)
1000999
{
1001-
datetime result;
1000+
auto result = from_string_maximum_error(dateString, format);
1001+
if (result == datetime::maximum())
1002+
{
1003+
return datetime();
1004+
}
1005+
1006+
return result;
1007+
}
1008+
1009+
datetime __cdecl datetime::from_string_maximum_error(const utility::string_t& dateString, date_format format)
1010+
{
1011+
datetime result = datetime::maximum();
10021012
int64_t secondsSince1900;
10031013
uint64_t fracSec = 0;
10041014
auto str = dateString.c_str();

Release/tests/functional/utils/datetime.cpp

+34-42
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
****/
1111

1212
#include "stdafx.h"
13+
1314
#include <stdint.h>
1415
#include <string>
1516

@@ -81,6 +82,10 @@ SUITE(datetime)
8182
auto dt = utility::datetime::from_string(str, utility::datetime::ISO_8601);
8283
utility::string_t str2 = dt.to_string(utility::datetime::ISO_8601);
8384
VERIFY_ARE_EQUAL(str2, strExpected);
85+
86+
auto dt_me = utility::datetime::from_string_maximum_error(str, utility::datetime::ISO_8601);
87+
utility::string_t str3 = dt_me.to_string(utility::datetime::ISO_8601);
88+
VERIFY_ARE_EQUAL(str3, strExpected);
8489
}
8590

8691
void TestDateTimeRoundtrip(utility::string_t str) { TestDateTimeRoundtrip(str, str); }
@@ -123,32 +128,18 @@ SUITE(datetime)
123128
TestDateTimeRoundtrip(_XPLATSTR("2013-11-19T14:30:59.5Z"));
124129
}
125130

126-
TEST(parsing_time_roundtrip_year_1900)
127-
{
128-
TestDateTimeRoundtrip(_XPLATSTR("1900-01-01T00:00:00Z"));
129-
}
131+
TEST(parsing_time_roundtrip_year_1900) { TestDateTimeRoundtrip(_XPLATSTR("1900-01-01T00:00:00Z")); }
130132

131-
TEST(parsing_time_roundtrip_year_9999)
132-
{
133-
TestDateTimeRoundtrip(_XPLATSTR("9999-12-31T23:59:59Z"));
134-
}
133+
TEST(parsing_time_roundtrip_year_9999) { TestDateTimeRoundtrip(_XPLATSTR("9999-12-31T23:59:59Z")); }
135134

136-
TEST(parsing_time_roundtrip_year_2016)
137-
{
138-
TestDateTimeRoundtrip(_XPLATSTR("2016-12-31T20:59:59Z"));
139-
}
135+
TEST(parsing_time_roundtrip_year_2016) { TestDateTimeRoundtrip(_XPLATSTR("2016-12-31T20:59:59Z")); }
140136

141-
TEST(parsing_time_roundtrip_year_2020)
142-
{
143-
TestDateTimeRoundtrip(_XPLATSTR("2020-12-31T20:59:59Z"));
144-
}
137+
TEST(parsing_time_roundtrip_year_2020) { TestDateTimeRoundtrip(_XPLATSTR("2020-12-31T20:59:59Z")); }
145138

146-
TEST(parsing_time_roundtrip_year_2021)
147-
{
148-
TestDateTimeRoundtrip(_XPLATSTR("2021-01-01T20:59:59Z"));
149-
}
139+
TEST(parsing_time_roundtrip_year_2021) { TestDateTimeRoundtrip(_XPLATSTR("2021-01-01T20:59:59Z")); }
150140

151-
TEST(emitting_time_correct_day) {
141+
TEST(emitting_time_correct_day)
142+
{
152143
const auto test = utility::datetime() + UINT64_C(132004507640000000); // 2019-04-22T23:52:44 is a Monday
153144
const auto actual = test.to_string(utility::datetime::RFC_1123);
154145
const utility::string_t expected(_XPLATSTR("Mon"));
@@ -296,13 +287,13 @@ SUITE(datetime)
296287
_XPLATSTR("Thu, 01 Jan 1970 00:00:00 G"),
297288
_XPLATSTR("Thu, 01 Jan 1970 00:00:00 GM"),
298289
_XPLATSTR("Fri, 01 Jan 1970 00:00:00 GMT"), // wrong day
299-
_XPLATSTR("01 Jan 1899 00:00:00 GMT"), // year too small
300-
_XPLATSTR("01 Xxx 1971 00:00:00 GMT"), // month bad
301-
_XPLATSTR("00 Jan 1971 00:00:00 GMT"), // day too small
302-
_XPLATSTR("32 Jan 1971 00:00:00 GMT"), // day too big
303-
_XPLATSTR("30 Feb 1971 00:00:00 GMT"), // day too big for feb
304-
_XPLATSTR("30 Feb 1971 00:00:00 GMT"), // day too big for feb (non-leap year)
305-
_XPLATSTR("32 Mar 1971 00:00:00 GMT"), // other months
290+
_XPLATSTR("01 Jan 1899 00:00:00 GMT"), // year too small
291+
_XPLATSTR("01 Xxx 1971 00:00:00 GMT"), // month bad
292+
_XPLATSTR("00 Jan 1971 00:00:00 GMT"), // day too small
293+
_XPLATSTR("32 Jan 1971 00:00:00 GMT"), // day too big
294+
_XPLATSTR("30 Feb 1971 00:00:00 GMT"), // day too big for feb
295+
_XPLATSTR("30 Feb 1971 00:00:00 GMT"), // day too big for feb (non-leap year)
296+
_XPLATSTR("32 Mar 1971 00:00:00 GMT"), // other months
306297
_XPLATSTR("31 Apr 1971 00:00:00 GMT"),
307298
_XPLATSTR("32 May 1971 00:00:00 GMT"),
308299
_XPLATSTR("31 Jun 1971 00:00:00 GMT"),
@@ -317,8 +308,8 @@ SUITE(datetime)
317308
_XPLATSTR("01 Jan 1971 00:60:00 GMT"), // minute too big
318309
_XPLATSTR("01 Jan 1971 00:00:70 GMT"), // second too big
319310
_XPLATSTR("01 Jan 1971 00:00:61 GMT"),
320-
_XPLATSTR("01 Jan 1899 00:00:00 GMT"), // underflow
321-
_XPLATSTR("01 Jan 1969 00:00:00 CEST"), // bad tz
311+
_XPLATSTR("01 Jan 1899 00:00:00 GMT"), // underflow
312+
_XPLATSTR("01 Jan 1969 00:00:00 CEST"), // bad tz
322313
_XPLATSTR("14 Jan 2019 23:16:21 G0100"), // bad tzoffsets
323314
_XPLATSTR("01 Jan 1970 00:00:00 +2400"),
324315
_XPLATSTR("01 Jan 1970 00:00:00 -3000"),
@@ -332,6 +323,8 @@ SUITE(datetime)
332323
{
333324
auto dt = utility::datetime::from_string(str, utility::datetime::RFC_1123);
334325
VERIFY_ARE_EQUAL(0, dt.to_interval());
326+
auto dt_me = utility::datetime::from_string_maximum_error(str, utility::datetime::RFC_1123);
327+
VERIFY_ARE_EQUAL(utility::datetime::maximum(), dt_me);
335328
}
336329
}
337330

@@ -484,7 +477,7 @@ SUITE(datetime)
484477
_XPLATSTR("1971-01-01T00:60:00Z"), // minute too big
485478
_XPLATSTR("1971-01-01T00:00:70Z"), // second too big
486479
_XPLATSTR("1971-01-01T00:00:61Z"),
487-
_XPLATSTR("1899-01-01T00:00:00Z"), // underflow
480+
_XPLATSTR("1899-01-01T00:00:00Z"), // underflow
488481
_XPLATSTR("1900-01-01T00:00:00+00:01"), // time zone underflow
489482
// _XPLATSTR("1970-01-01T00:00:00.Z"), // accepted as invalid timezone above
490483
_XPLATSTR("1970-01-01T00:00:00+24:00"), // bad tzoffsets
@@ -499,23 +492,22 @@ SUITE(datetime)
499492
{
500493
auto dt = utility::datetime::from_string(str, utility::datetime::ISO_8601);
501494
VERIFY_ARE_EQUAL(dt.to_interval(), 0);
495+
auto dt_me = utility::datetime::from_string_maximum_error(str, utility::datetime::ISO_8601);
496+
VERIFY_ARE_EQUAL(dt_me, utility::datetime::maximum());
502497
}
503498
}
504499

505-
TEST(can_emit_nt_epoch_zero)
500+
TEST(can_emit_nt_epoch_zero_rfc_1123)
506501
{
507-
// ISO 8601
508-
{
509-
auto result = utility::datetime{}.to_string(utility::datetime::RFC_1123);
510-
VERIFY_ARE_EQUAL(_XPLATSTR("Mon, 01 Jan 1601 00:00:00 GMT"), result);
511-
}
512-
// ISO 8601
513-
{
514-
auto result = utility::datetime{}.to_string(utility::datetime::ISO_8601);
515-
VERIFY_ARE_EQUAL(_XPLATSTR("1601-01-01T00:00:00Z"), result);
516-
}
502+
auto result = utility::datetime {}.to_string(utility::datetime::RFC_1123);
503+
VERIFY_ARE_EQUAL(_XPLATSTR("Mon, 01 Jan 1601 00:00:00 GMT"), result);
517504
}
518505

506+
TEST(can_emit_nt_epoch_zero_iso_8601)
507+
{
508+
auto result = utility::datetime {}.to_string(utility::datetime::ISO_8601);
509+
VERIFY_ARE_EQUAL(_XPLATSTR("1601-01-01T00:00:00Z"), result);
510+
}
519511
} // SUITE(datetime)
520512

521513
} // namespace utils_tests

0 commit comments

Comments
 (0)