diff --git a/Release/include/cpprest/asyncrt_utils.h b/Release/include/cpprest/asyncrt_utils.h
index 1b54f1634a..3e4bfdd5c4 100644
--- a/Release/include/cpprest/asyncrt_utils.h
+++ b/Release/include/cpprest/asyncrt_utils.h
@@ -603,14 +603,21 @@ class datetime
}
}
- 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.
///
@@ -621,6 +628,8 @@ class datetime
///
interval_type to_interval() const { return m_interval; }
+ static datetime from_interval(interval_type interval) { return datetime(interval); }
+
datetime operator-(interval_type value) const { return datetime(m_interval - value); }
datetime operator+(interval_type value) const { return datetime(m_interval + value); }
@@ -628,13 +637,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; }
@@ -649,6 +658,8 @@ class datetime
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 +670,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;
diff --git a/Release/include/cpprest/streams.h b/Release/include/cpprest/streams.h
index c2968b9d49..b6c3864028 100644
--- a/Release/include/cpprest/streams.h
+++ b/Release/include/cpprest/streams.h
@@ -17,6 +17,7 @@
#include "cpprest/astreambuf.h"
#include
+#include
namespace Concurrency
{
@@ -1434,7 +1435,8 @@ static pplx::task _extract_result(std::shared_ptr<_double_stateexponent_number >= 0)
{
- result *= pow(FloatingPoint(10.0), state->exponent_number);
+ result *= static_cast(
+ std::pow(static_cast(10.0), static_cast(state->exponent_number)));
#pragma push_macro("max")
#undef max
@@ -1447,7 +1449,8 @@ static pplx::task _extract_result(std::shared_ptr<_double_stateexponent_number);
+ result /= static_cast(
+ std::pow(static_cast(10.0), static_cast(-state->exponent_number)));
if (!is_zero && result > -std::numeric_limits::denorm_min() &&
result < std::numeric_limits::denorm_min())
diff --git a/Release/src/utilities/asyncrt_utils.cpp b/Release/src/utilities/asyncrt_utils.cpp
index 579fedc25f..cf747c666c 100644
--- a/Release/src/utilities/asyncrt_utils.cpp
+++ b/Release/src/utilities/asyncrt_utils.cpp
@@ -177,8 +177,8 @@ scoped_c_thread_locale::~scoped_c_thread_locale()
}
}
#elif (defined(ANDROID) || defined(__ANDROID__))
-scoped_c_thread_locale::scoped_c_thread_locale() {}
-scoped_c_thread_locale::~scoped_c_thread_locale() {}
+scoped_c_thread_locale::scoped_c_thread_locale() { }
+scoped_c_thread_locale::~scoped_c_thread_locale() { }
#else
scoped_c_thread_locale::scoped_c_thread_locale() : m_prevLocale(nullptr)
{
@@ -620,7 +620,11 @@ utf16string __cdecl conversions::to_utf16string(const std::string& value) { retu
static const int64_t NtToUnixOffsetSeconds = 11644473600; // diff between windows and unix epochs (seconds)
-static bool year_is_leap_year(int year) { return (year % 4 == 0 && (year % 100 != 0 || year % 400 == 0)); }
+static bool year_is_leap_year_1601(int yearsSince1601)
+{
+ int decimalYear = yearsSince1601 + 1601;
+ return (decimalYear % 4 == 0 && (decimalYear % 100 != 0 || decimalYear % 400 == 0));
+}
static const int SecondsInMinute = 60;
static const int SecondsInHour = SecondsInMinute * 60;
@@ -635,26 +639,18 @@ static const int SecondsInYear = SecondsInDay * DaysInYear;
static const int SecondsIn4Years = SecondsInDay * DaysIn4Years;
static const int64_t SecondsIn100Years = static_cast(SecondsInDay) * DaysIn100Years;
static const int64_t SecondsIn400Years = static_cast(SecondsInDay) * DaysIn400Years;
-static const int64_t SecondsFrom1900To2001 = INT64_C(3187296000);
-
-static const int64_t NtTo1900OffsetInterval = INT64_C(0x014F373BFDE04000);
-static int count_leap_years(const int yearsSince1900)
+static int count_leap_years_1601(int yearsSince1601)
{
- int tmpYears = yearsSince1900 + 299; // shift into 1601, the first 400 year cycle including 1900
-
- int year400 = tmpYears / 400;
- tmpYears -= year400 * 400;
+ int year400 = yearsSince1601 / 400;
+ yearsSince1601 -= year400 * 400;
int result = year400 * 97;
- int year100 = tmpYears / 100;
- tmpYears -= year100 * 100;
+ int year100 = yearsSince1601 / 100;
+ yearsSince1601 -= year100 * 100;
result += year100 * 24;
- result += tmpYears / 4;
-
- // subtract off leap years from 1601
- result -= 72;
+ result += yearsSince1601 / 4;
return result;
}
@@ -720,20 +716,16 @@ struct compute_year_result
int secondsLeftThisYear;
};
-static const int64_t secondsFrom1601To1900 = INT64_C(9435484800);
-
-static compute_year_result compute_year(int64_t secondsSince1900)
+static compute_year_result compute_year_1601(int64_t secondsSince1601)
{
- int64_t secondsLeft = secondsSince1900 + secondsFrom1601To1900; // shift to start of this 400 year cycle
-
- int year400 = static_cast(secondsLeft / SecondsIn400Years);
- secondsLeft -= year400 * SecondsIn400Years;
+ int year400 = static_cast(secondsSince1601 / SecondsIn400Years);
+ secondsSince1601 -= year400 * SecondsIn400Years;
- int year100 = static_cast(secondsLeft / SecondsIn100Years);
- secondsLeft -= year100 * SecondsIn100Years;
+ int year100 = static_cast(secondsSince1601 / SecondsIn100Years);
+ secondsSince1601 -= year100 * SecondsIn100Years;
- int year4 = static_cast(secondsLeft / SecondsIn4Years);
- int secondsInt = static_cast(secondsLeft - year4 * SecondsIn4Years);
+ int year4 = static_cast(secondsSince1601 / SecondsIn4Years);
+ int secondsInt = static_cast(secondsSince1601 - year4 * SecondsIn4Years);
int year1 = secondsInt / SecondsInYear;
if (year1 == 4)
@@ -743,23 +735,44 @@ static compute_year_result compute_year(int64_t secondsSince1900)
}
secondsInt -= year1 * SecondsInYear;
-
- // shift back to 1900 base from 1601:
- return {year400 * 400 + year100 * 100 + year4 * 4 + year1 - 299, secondsInt};
+ return {year400 * 400 + year100 * 100 + year4 * 4 + year1, secondsInt};
}
+// The constant below was calculated by running the following test program on a Windows machine:
+// #include
+// #include
+
+// int main() {
+// SYSTEMTIME st;
+// st.wYear = 9999;
+// st.wMonth = 12;
+// st.wDayOfWeek = 5;
+// st.wDay = 31;
+// st.wHour = 23;
+// st.wMinute = 59;
+// st.wSecond = 59;
+// st.wMilliseconds = 999;
+
+// unsigned long long ft;
+// if (SystemTimeToFileTime(&st, reinterpret_cast(&ft))) {
+// printf("0x%016llX\n", ft);
+// } else {
+// puts("failed!");
+// }
+// }
+
utility::string_t datetime::to_string(date_format format) const
{
- if (m_interval > INT64_C(2650467743990000000))
+ const int64_t interval = static_cast(m_interval);
+ if (interval > INT64_C(0x24C85A5ED1C018F0))
{
throw std::out_of_range("The requested year exceeds the year 9999.");
}
- const int64_t epochAdjusted = static_cast(m_interval) - NtTo1900OffsetInterval;
- const int64_t secondsSince1900 = epochAdjusted / _secondTicks; // convert to seconds
- const int fracSec = static_cast(epochAdjusted % _secondTicks);
+ const int64_t secondsSince1601 = interval / _secondTicks; // convert to seconds
+ const int fracSec = static_cast(interval % _secondTicks);
- const auto yearData = compute_year(secondsSince1900);
+ const auto yearData = compute_year_1601(secondsSince1601);
const int year = yearData.year;
const int yearDay = yearData.secondsLeftThisYear / SecondsInDay;
int leftover = yearData.secondsLeftThisYear % SecondsInDay;
@@ -768,7 +781,7 @@ utility::string_t datetime::to_string(date_format format) const
const int minute = leftover / SecondsInMinute;
leftover = leftover % SecondsInMinute;
- const auto& monthTable = year_is_leap_year(year) ? cumulative_days_to_month_leap : cumulative_days_to_month;
+ const auto& monthTable = year_is_leap_year_1601(year) ? cumulative_days_to_month_leap : cumulative_days_to_month;
int month = 0;
while (month < 11 && monthTable[month + 1] <= yearDay)
{
@@ -776,7 +789,7 @@ utility::string_t datetime::to_string(date_format format) const
}
const auto monthDay = yearDay - monthTable[month] + 1;
- const auto weekday = static_cast((secondsSince1900 / SecondsInDay + 1) % 7);
+ const auto weekday = static_cast((secondsSince1601 / SecondsInDay + 1) % 7);
char outBuffer[38]; // Thu, 01 Jan 1970 00:00:00 GMT\0
// 1970-01-01T00:00:00.1234567Z\0
@@ -791,7 +804,7 @@ utility::string_t datetime::to_string(date_format format) const
dayNames + 4 * weekday,
monthDay,
monthNames + 4 * month,
- year + 1900,
+ year + 1601,
hour,
minute,
leftover);
@@ -801,7 +814,7 @@ utility::string_t datetime::to_string(date_format format) const
dayNames + 4 * weekday,
monthDay,
monthNames + 4 * month,
- year + 1900,
+ year + 1601,
hour,
minute,
leftover);
@@ -815,7 +828,7 @@ utility::string_t datetime::to_string(date_format format) const
sprintf_s(outCursor,
20,
"%04d-%02d-%02dT%02d:%02d:%02d",
- year + 1900,
+ year + 1601,
month + 1,
monthDay,
hour,
@@ -823,7 +836,7 @@ utility::string_t datetime::to_string(date_format format) const
leftover);
#else // ^^^ _MSC_VER // !_MSC_VER vvv
sprintf(
- outCursor, "%04d-%02d-%02dT%02d:%02d:%02d", year + 1900, month + 1, monthDay, hour, minute, leftover);
+ outCursor, "%04d-%02d-%02dT%02d:%02d:%02d", year + 1601, month + 1, monthDay, hour, minute, leftover);
#endif // _MSC_VER
outCursor += 19;
if (fracSec != 0)
@@ -889,12 +902,12 @@ static const unsigned char max_days_in_month[12] = {
31 // Dec
};
-static bool validate_day_month(int day, int month, int year)
+static bool validate_day_month_1601(int day, int month, int year)
{
int maxDaysThisMonth;
if (month == 1)
{ // Feb needs leap year testing
- maxDaysThisMonth = 28 + year_is_leap_year(year);
+ maxDaysThisMonth = 28 + year_is_leap_year_1601(year);
}
else
{
@@ -904,9 +917,9 @@ static bool validate_day_month(int day, int month, int year)
return day >= 1 && day <= maxDaysThisMonth;
}
-static int get_year_day(int month, int monthDay, int year)
+static int get_year_day_1601(int month, int monthDay, int year)
{
- return cumulative_days_to_month[month] + monthDay + (year_is_leap_year(year) && month > 1) - 1;
+ return cumulative_days_to_month[month] + monthDay + (year_is_leap_year_1601(year) && month > 1) - 1;
}
template
@@ -985,11 +998,21 @@ zone = "UT" / "GMT" ; Universal Time
; hours+min. (HHMM)
*/
-
datetime __cdecl datetime::from_string(const utility::string_t& dateString, date_format format)
{
- datetime result;
- int64_t secondsSince1900;
+ 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 secondsSince1601;
uint64_t fracSec = 0;
auto str = dateString.c_str();
if (format == RFC_1123)
@@ -1056,19 +1079,21 @@ datetime __cdecl datetime::from_string(const utility::string_t& dateString, date
int year = (str[0] - _XPLATSTR('0')) * 1000 + (str[1] - _XPLATSTR('0')) * 100 + (str[2] - _XPLATSTR('0')) * 10 +
(str[3] - _XPLATSTR('0'));
- if (year < 1900)
+ if (year < 1601)
{
return result;
}
+ year -= 1601;
+
// days in month validity check
- if (!validate_day_month(monthDay, month, year))
+ if (!validate_day_month_1601(monthDay, month, year))
{
return result;
}
str += 5; // parsed year
- const int yearDay = get_year_day(month, monthDay, year);
+ const int yearDay = get_year_day_1601(month, monthDay, year);
if (!ascii_isdigit2(str[0]) || !ascii_isdigit(str[1]) || str[2] != _XPLATSTR(':') || !ascii_isdigit5(str[3]) ||
!ascii_isdigit(str[4]))
@@ -1112,12 +1137,11 @@ datetime __cdecl datetime::from_string(const utility::string_t& dateString, date
return result;
}
- year -= 1900;
- int daysSince1900 = year * DaysInYear + count_leap_years(year) + yearDay;
+ int daysSince1601 = year * DaysInYear + count_leap_years_1601(year) + yearDay;
if (parsedWeekday != 7)
{
- const int actualWeekday = (daysSince1900 + 1) % 7;
+ const int actualWeekday = (daysSince1601 + 1) % 7;
if (parsedWeekday != actualWeekday)
{
@@ -1125,8 +1149,8 @@ datetime __cdecl datetime::from_string(const utility::string_t& dateString, date
}
}
- secondsSince1900 =
- static_cast(daysSince1900) * SecondsInDay + hour * SecondsInHour + minute * SecondsInMinute + sec;
+ secondsSince1601 =
+ static_cast(daysSince1601) * SecondsInDay + hour * SecondsInHour + minute * SecondsInMinute + sec;
if (!string_starts_with(str, "GMT") && !string_starts_with(str, "UT"))
{
@@ -1166,8 +1190,8 @@ datetime __cdecl datetime::from_string(const utility::string_t& dateString, date
return result;
}
- secondsSince1900 = timezone_adjust(secondsSince1900, static_cast(tzCh), tzHours, tzMinutes);
- if (secondsSince1900 < 0)
+ secondsSince1601 = timezone_adjust(secondsSince1601, static_cast(tzCh), tzHours, tzMinutes);
+ if (secondsSince1601 < 0)
{
return result;
}
@@ -1183,11 +1207,13 @@ datetime __cdecl datetime::from_string(const utility::string_t& dateString, date
int year = (str[0] - _XPLATSTR('0')) * 1000 + (str[1] - _XPLATSTR('0')) * 100 + (str[2] - _XPLATSTR('0')) * 10 +
(str[3] - _XPLATSTR('0'));
- if (year < 1900)
+ if (year < 1601)
{
return result;
}
+ year -= 1601;
+
str += 4;
if (*str == _XPLATSTR('-'))
{
@@ -1221,24 +1247,22 @@ datetime __cdecl datetime::from_string(const utility::string_t& dateString, date
}
int monthDay = atoi2(str);
- if (!validate_day_month(monthDay, month, year))
+ if (!validate_day_month_1601(monthDay, month, year))
{
return result;
}
- const int yearDay = get_year_day(month, monthDay, year);
+ const int yearDay = get_year_day_1601(month, monthDay, year);
str += 2;
- year -= 1900;
- int daysSince1900 = year * DaysInYear + count_leap_years(year) + yearDay;
+ int daysSince1601 = year * DaysInYear + count_leap_years_1601(year) + yearDay;
if (str[0] != _XPLATSTR('T') && str[0] != _XPLATSTR('t'))
{
// No time
- secondsSince1900 = static_cast(daysSince1900) * SecondsInDay;
+ secondsSince1601 = static_cast(daysSince1601) * SecondsInDay;
- result.m_interval =
- static_cast(secondsSince1900 * _secondTicks + fracSec + NtTo1900OffsetInterval);
+ result.m_interval = static_cast(secondsSince1601 * _secondTicks + fracSec);
return result;
}
@@ -1327,8 +1351,8 @@ datetime __cdecl datetime::from_string(const utility::string_t& dateString, date
}
}
- secondsSince1900 =
- static_cast(daysSince1900) * SecondsInDay + hour * SecondsInHour + minute * SecondsInMinute + sec;
+ secondsSince1601 =
+ static_cast(daysSince1601) * SecondsInDay + hour * SecondsInHour + minute * SecondsInMinute + sec;
if (str[0] == _XPLATSTR('Z') || str[0] == _XPLATSTR('z'))
{
@@ -1343,8 +1367,8 @@ datetime __cdecl datetime::from_string(const utility::string_t& dateString, date
return result;
}
- secondsSince1900 = timezone_adjust(secondsSince1900, offsetDirection, atoi2(str + 1), atoi2(str + 4));
- if (secondsSince1900 < 0)
+ secondsSince1601 = timezone_adjust(secondsSince1601, offsetDirection, atoi2(str + 1), atoi2(str + 4));
+ if (secondsSince1601 < 0)
{
return result;
}
@@ -1359,7 +1383,7 @@ datetime __cdecl datetime::from_string(const utility::string_t& dateString, date
throw std::invalid_argument("unrecognized date format");
}
- result.m_interval = static_cast(secondsSince1900 * _secondTicks + fracSec + NtTo1900OffsetInterval);
+ result.m_interval = static_cast(secondsSince1601 * _secondTicks + fracSec);
return result;
}
diff --git a/Release/tests/functional/utils/datetime.cpp b/Release/tests/functional/utils/datetime.cpp
index a584b1e3c2..22954ca942 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,26 @@ 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(parsing_time_roundtrip_year_1601) { TestDateTimeRoundtrip(_XPLATSTR("1601-01-01T00:00:00Z")); }
- TEST(emitting_time_correct_day) {
+ TEST(parsing_time_roundtrip_year_1602) { TestDateTimeRoundtrip(_XPLATSTR("1602-01-01T00:00:00Z")); }
+
+ TEST(parsing_time_roundtrip_year_1603) { TestDateTimeRoundtrip(_XPLATSTR("1603-01-01T00:00:00Z")); }
+
+ TEST(parsing_time_roundtrip_year_1604) { TestDateTimeRoundtrip(_XPLATSTR("1604-01-01T00:00:00Z")); }
+
+ 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 +295,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 1600 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 +316,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 1600 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 +331,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);
}
}
@@ -461,7 +462,7 @@ SUITE(datetime)
_XPLATSTR("1970-01-01T00:00:"),
_XPLATSTR("1970-01-01T00:00:0"),
// _XPLATSTR("1970-01-01T00:00:00"), // accepted as invalid timezone above
- _XPLATSTR("1899-01-01T00:00:00Z"), // year too small
+ _XPLATSTR("1600-01-01T00:00:00Z"), // year too small
_XPLATSTR("1971-00-01T00:00:00Z"), // month too small
_XPLATSTR("1971-20-01T00:00:00Z"), // month too big
_XPLATSTR("1971-13-01T00:00:00Z"),
@@ -484,8 +485,8 @@ 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("1900-01-01T00:00:00+00:01"), // time zone underflow
+ _XPLATSTR("1600-01-01T00:00:00Z"), // underflow
+ _XPLATSTR("1601-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
_XPLATSTR("1970-01-01T00:00:00-30:00"),
@@ -499,9 +500,55 @@ 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_rfc_1123)
+ {
+ 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);
+ }
+
+ TEST(can_emit_year_9999_rfc_1123)
+ {
+ auto result =
+ utility::datetime::from_interval(INT64_C(0x24C85A5ED1C018F0)).to_string(utility::datetime::RFC_1123);
+ VERIFY_ARE_EQUAL(_XPLATSTR("Fri, 31 Dec 9999 23:59:59 GMT"), result);
+ }
+
+ TEST(can_emit_year_9999_iso_8601)
+ {
+ auto result =
+ utility::datetime::from_interval(INT64_C(0x24C85A5ED1C018F0)).to_string(utility::datetime::ISO_8601);
+ VERIFY_ARE_EQUAL(_XPLATSTR("9999-12-31T23:59:59.999Z"), result);
+ }
+
+ TEST(can_parse_nt_epoch_zero_rfc_1123)
+ {
+ auto dt =
+ utility::datetime::from_string(_XPLATSTR("Mon, 01 Jan 1601 00:00:00 GMT"), utility::datetime::RFC_1123);
+ VERIFY_ARE_EQUAL(0U, dt.to_interval());
+ auto dt_me = utility::datetime::from_string_maximum_error(_XPLATSTR("Mon, 01 Jan 1601 00:00:00 GMT"),
+ utility::datetime::RFC_1123);
+ VERIFY_ARE_EQUAL(0U, dt_me.to_interval());
+ }
+
+ TEST(can_parse_nt_epoch_zero_iso_8601)
+ {
+ auto dt = utility::datetime::from_string(_XPLATSTR("1601-01-01T00:00:00Z"), utility::datetime::ISO_8601);
+ VERIFY_ARE_EQUAL(0U, dt.to_interval());
+ auto dt_me = utility::datetime::from_string_maximum_error(_XPLATSTR("1601-01-01T00:00:00Z"),
+ utility::datetime::ISO_8601);
+ VERIFY_ARE_EQUAL(0U, dt_me.to_interval());
+ }
} // SUITE(datetime)
} // namespace utils_tests
diff --git a/vcpkg b/vcpkg
index 6709d3d7d0..b759049a36 160000
--- a/vcpkg
+++ b/vcpkg
@@ -1 +1 @@
-Subproject commit 6709d3d7d0cba96508ba3606f810ab562ea32556
+Subproject commit b759049a36728d18260963799a56e6b19cb4a2ef