提交 c8bf3357 编写于 作者: C Charlie

Merge branch 'master' of https://github.com/microsoft/cpprestsdk

......@@ -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>
......@@ -621,6 +628,8 @@ public:
/// </summary>
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 @@ 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 +658,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 +670,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;
......
......@@ -17,6 +17,7 @@
#include "cpprest/astreambuf.h"
#include <iosfwd>
#include <cstdio>
namespace Concurrency
{
......@@ -1434,7 +1435,8 @@ static pplx::task<FloatingPoint> _extract_result(std::shared_ptr<_double_state<F
if (state->exponent_number >= 0)
{
result *= pow(FloatingPoint(10.0), state->exponent_number);
result *= static_cast<FloatingPoint>(
std::pow(static_cast<FloatingPoint>(10.0), static_cast<FloatingPoint>(state->exponent_number)));
#pragma push_macro("max")
#undef max
......@@ -1447,7 +1449,8 @@ static pplx::task<FloatingPoint> _extract_result(std::shared_ptr<_double_state<F
{
bool is_zero = (result == 0);
result /= pow(FloatingPoint(10.0), -state->exponent_number);
result /= static_cast<FloatingPoint>(
std::pow(static_cast<FloatingPoint>(10.0), static_cast<FloatingPoint>(-state->exponent_number)));
if (!is_zero && result > -std::numeric_limits<FloatingPoint>::denorm_min() &&
result < std::numeric_limits<FloatingPoint>::denorm_min())
......
......@@ -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<int64_t>(SecondsInDay) * DaysIn100Years;
static const int64_t SecondsIn400Years = static_cast<int64_t>(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<int>(secondsLeft / SecondsIn400Years);
secondsLeft -= year400 * SecondsIn400Years;
int year400 = static_cast<int>(secondsSince1601 / SecondsIn400Years);
secondsSince1601 -= year400 * SecondsIn400Years;
int year100 = static_cast<int>(secondsLeft / SecondsIn100Years);
secondsLeft -= year100 * SecondsIn100Years;
int year100 = static_cast<int>(secondsSince1601 / SecondsIn100Years);
secondsSince1601 -= year100 * SecondsIn100Years;
int year4 = static_cast<int>(secondsLeft / SecondsIn4Years);
int secondsInt = static_cast<int>(secondsLeft - year4 * SecondsIn4Years);
int year4 = static_cast<int>(secondsSince1601 / SecondsIn4Years);
int secondsInt = static_cast<int>(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 <windows.h>
// #include <stdio.h>
// 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<FILETIME*>(&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<int64_t>(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<int64_t>(m_interval) - NtTo1900OffsetInterval;
const int64_t secondsSince1900 = epochAdjusted / _secondTicks; // convert to seconds
const int fracSec = static_cast<int>(epochAdjusted % _secondTicks);
const int64_t secondsSince1601 = interval / _secondTicks; // convert to seconds
const int fracSec = static_cast<int>(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<int>((secondsSince1900 / SecondsInDay + 1) % 7);
const auto weekday = static_cast<int>((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<class CharT>
......@@ -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<int64_t>(daysSince1900) * SecondsInDay + hour * SecondsInHour + minute * SecondsInMinute + sec;
secondsSince1601 =
static_cast<int64_t>(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<unsigned char>(tzCh), tzHours, tzMinutes);
if (secondsSince1900 < 0)
secondsSince1601 = timezone_adjust(secondsSince1601, static_cast<unsigned char>(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<int64_t>(daysSince1900) * SecondsInDay;
secondsSince1601 = static_cast<int64_t>(daysSince1601) * SecondsInDay;
result.m_interval =
static_cast<interval_type>(secondsSince1900 * _secondTicks + fracSec + NtTo1900OffsetInterval);
result.m_interval = static_cast<interval_type>(secondsSince1601 * _secondTicks + fracSec);
return result;
}
......@@ -1327,8 +1351,8 @@ datetime __cdecl datetime::from_string(const utility::string_t& dateString, date
}
}
secondsSince1900 =
static_cast<int64_t>(daysSince1900) * SecondsInDay + hour * SecondsInHour + minute * SecondsInMinute + sec;
secondsSince1601 =
static_cast<int64_t>(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<interval_type>(secondsSince1900 * _secondTicks + fracSec + NtTo1900OffsetInterval);
result.m_interval = static_cast<interval_type>(secondsSince1601 * _secondTicks + fracSec);
return result;
}
......
......@@ -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,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);