blob: 01e4631130039b9d9c0a7ed86325642dbd864808 [file] [log] [blame]
Brad Bishop19323692019-04-05 15:28:33 -04001Description: revert upstream bugfix (see:https://github.com/Microsoft/cpprestsdk/issues/1090)
2Last-Update: 2019-03-28
3
4--- cpprest-2.10.12.orig/Release/include/cpprest/asyncrt_utils.h
5+++ cpprest-2.10.12/Release/include/cpprest/asyncrt_utils.h
6@@ -630,6 +630,15 @@ private:
7 static const interval_type _hourTicks = 60 * 60 * _secondTicks;
8 static const interval_type _dayTicks = 24 * 60 * 60 * _secondTicks;
9
10+#ifdef _WIN32
11+ // void* to avoid pulling in windows.h
12+ static _ASYNCRTIMP bool __cdecl system_type_to_datetime(/*SYSTEMTIME*/ void* psysTime,
13+ uint64_t seconds,
14+ datetime* pdt);
15+#else
16+ static datetime timeval_to_datetime(const timeval& time);
17+#endif
18+
19 // Private constructor. Use static methods to create an instance.
20 datetime(interval_type interval) : m_interval(interval) {}
21
22@@ -690,6 +699,7 @@ public:
23 void set_length(int length) { m_length = length; }
24
25 private:
26+ static const utility::string_t c_allowed_chars;
27 std::mt19937 m_random;
28 int m_length;
29 };
30--- cpprest-2.10.12.orig/Release/src/pch/stdafx.h
31+++ cpprest-2.10.12/Release/src/pch/stdafx.h
32@@ -61,6 +61,7 @@
33 #undef BOOST_NO_CXX11_NULLPTR
34 #endif
35 #include "boost/bind/bind.hpp"
36+#include "boost/date_time/posix_time/posix_time_types.hpp"
37 #include "boost/thread/condition_variable.hpp"
38 #include "boost/thread/mutex.hpp"
39 #include <fcntl.h>
40--- cpprest-2.10.12.orig/Release/src/utilities/asyncrt_utils.cpp
41+++ cpprest-2.10.12/Release/src/utilities/asyncrt_utils.cpp
42@@ -15,9 +15,20 @@
43
44 #include <algorithm>
45 #include <cpprest/asyncrt_utils.h>
46-#include <stdexcept>
47+#include <sstream>
48 #include <string>
49-#include <time.h>
50+
51+#ifndef _WIN32
52+#if defined(__clang__)
53+#pragma clang diagnostic push
54+#pragma clang diagnostic ignored "-Wunused-local-typedef"
55+#endif
56+#include <boost/date_time/posix_time/posix_time.hpp>
57+#include <boost/date_time/posix_time/posix_time_io.hpp>
58+#if defined(__clang__)
59+#pragma clang diagnostic pop
60+#endif
61+#endif
62
63 using namespace web;
64 using namespace utility;
65@@ -618,7 +629,18 @@ std::string __cdecl conversions::to_utf8
66
67 utf16string __cdecl conversions::to_utf16string(const std::string& value) { return utf8_to_utf16(value); }
68
69-static const uint64_t ntToUnixOffsetSeconds = 11644473600U; // diff between windows and unix epochs (seconds)
70+#ifndef WIN32
71+datetime datetime::timeval_to_datetime(const timeval& time)
72+{
73+ const uint64_t epoch_offset = 11644473600LL; // diff between windows and unix epochs (seconds)
74+ uint64_t result = epoch_offset + time.tv_sec;
75+ result *= _secondTicks; // convert to 10e-7
76+ result += time.tv_usec * 10; // convert and add microseconds, 10e-6 to 10e-7
77+ return datetime(result);
78+}
79+#endif
80+
81+static bool is_digit(utility::char_t c) { return c >= _XPLATSTR('0') && c <= _XPLATSTR('9'); }
82
83 datetime __cdecl datetime::utc_now()
84 {
85@@ -634,649 +656,450 @@ datetime __cdecl datetime::utc_now()
86 #else // LINUX
87 struct timeval time;
88 gettimeofday(&time, nullptr);
89- uint64_t result = ntToUnixOffsetSeconds + time.tv_sec;
90- result *= _secondTicks; // convert to 10e-7
91- result += time.tv_usec * 10; // convert and add microseconds, 10e-6 to 10e-7
92- return datetime(result);
93+ return timeval_to_datetime(time);
94 #endif
95 }
96
97-static const char dayNames[] = "Sun\0Mon\0Tue\0Wed\0Thu\0Fri\0Sat";
98-static const char monthNames[] = "Jan\0Feb\0Mar\0Apr\0May\0Jun\0Jul\0Aug\0Sep\0Oct\0Nov\0Dec";
99-
100 utility::string_t datetime::to_string(date_format format) const
101 {
102- const uint64_t input = m_interval / _secondTicks; // convert to seconds
103- const int frac_sec = static_cast<int>(m_interval % _secondTicks);
104- const time_t time = static_cast<time_t>(input - ntToUnixOffsetSeconds);
105- struct tm t;
106-#ifdef _MSC_VER
107- if (gmtime_s(&t, &time) != 0)
108-#else // ^^^ _MSC_VER ^^^ // vvv !_MSC_VER vvv
109- if (gmtime_r(&time, &t) == 0)
110-#endif // _MSC_VER
111- {
112- throw std::invalid_argument("gmtime_r/s failed on the time supplied");
113- }
114-
115- char outBuffer[38]; // Thu, 01 Jan 1970 00:00:00 GMT\0
116- // 1970-01-01T00:00:00.1234567Z\0
117- char* outCursor = outBuffer;
118- switch (format)
119- {
120- case RFC_1123:
121-#ifdef _MSC_VER
122- sprintf_s(outCursor,
123- 26,
124- "%s, %02d %s %04d %02d:%02d:%02d",
125- dayNames + 4 * t.tm_wday,
126- t.tm_mday,
127- monthNames + 4 * t.tm_mon,
128- t.tm_year + 1900,
129- t.tm_hour,
130- t.tm_min,
131- t.tm_sec);
132-#else // ^^^ _MSC_VER // !_MSC_VER vvv
133- sprintf(outCursor,
134- "%s, %02d %s %04d %02d:%02d:%02d",
135- dayNames + 4 * t.tm_wday,
136- t.tm_mday,
137- monthNames + 4 * t.tm_mon,
138- t.tm_year + 1900,
139- t.tm_hour,
140- t.tm_min,
141- t.tm_sec);
142-#endif // _MSC_VER
143- outCursor += 25;
144- memcpy(outCursor, " GMT", 4);
145- outCursor += 4;
146- return utility::string_t(outBuffer, outCursor);
147- case ISO_8601:
148-#ifdef _MSC_VER
149- sprintf_s(outCursor,
150- 20,
151- "%04d-%02d-%02dT%02d:%02d:%02d",
152- t.tm_year + 1900,
153- t.tm_mon + 1,
154- t.tm_mday,
155- t.tm_hour,
156- t.tm_min,
157- t.tm_sec);
158-#else // ^^^ _MSC_VER // !_MSC_VER vvv
159- sprintf(outCursor,
160- "%04d-%02d-%02dT%02d:%02d:%02d",
161- t.tm_year + 1900,
162- t.tm_mon + 1,
163- t.tm_mday,
164- t.tm_hour,
165- t.tm_min,
166- t.tm_sec);
167-#endif // _MSC_VER
168- outCursor += 19;
169- if (frac_sec != 0)
170- {
171- // Append fractional second, which is a 7-digit value with no trailing zeros
172- // This way, '1200' becomes '00012'
173-#ifdef _MSC_VER
174- size_t appended = sprintf_s(outCursor, 9, ".%07d", frac_sec);
175-#else // ^^^ _MSC_VER // !_MSC_VER vvv
176- size_t appended = sprintf(outCursor, ".%07d", frac_sec);
177-#endif // _MSC_VER
178- while (outCursor[appended - 1] == '0')
179- {
180- --appended; // trim trailing zeros
181- }
182-
183- outCursor += appended;
184- }
185-
186- *outCursor = 'Z';
187- ++outCursor;
188- return utility::string_t(outBuffer, outCursor);
189- default: throw std::invalid_argument("Unrecognized date format.");
190- }
191-}
192-
193-template<class CharT>
194-static bool string_starts_with(const CharT* haystack, const char* needle)
195-{
196- while (*needle)
197- {
198- if (*haystack != static_cast<CharT>(*needle))
199- {
200- return false;
201- }
202-
203- ++haystack;
204- ++needle;
205- }
206-
207- return true;
208-}
209-
210-#define ascii_isdigit(c) ((unsigned char)((unsigned char)(c) - '0') <= 9)
211-#define ascii_isdigit6(c) ((unsigned char)((unsigned char)(c) - '0') <= 6)
212-#define ascii_isdigit5(c) ((unsigned char)((unsigned char)(c) - '0') <= 5)
213-#define ascii_isdigit3(c) ((unsigned char)((unsigned char)(c) - '0') <= 3)
214-#define ascii_isdigit2(c) ((unsigned char)((unsigned char)(c) - '0') <= 2)
215-#define ascii_isdigit1(c) ((unsigned char)((unsigned char)(c) - '0') <= 1)
216-
217-static const unsigned char max_days_in_month[12] = {
218- 31, // Jan
219- 00, // Feb, special handling for leap years
220- 31, // Mar
221- 30, // Apr
222- 31, // May
223- 30, // Jun
224- 31, // Jul
225- 31, // Aug
226- 30, // Sep
227- 31, // Oct
228- 30, // Nov
229- 31 // Dec
230-};
231-
232-static bool validate_day_month(int day, int month, int year)
233-{
234- int maxDaysThisMonth;
235- if (month == 1)
236- { // Feb needs leap year testing
237- maxDaysThisMonth = 28 + (year % 4 == 0 && (year % 100 != 0 || year % 400 == 0));
238- }
239- else
240- {
241- maxDaysThisMonth = max_days_in_month[month];
242- }
243-
244- return day >= 1 && day <= maxDaysThisMonth;
245-}
246+#ifdef _WIN32
247+ int status;
248
249-template<class CharT>
250-static int atoi2(const CharT* str)
251-{
252- return (static_cast<unsigned char>(str[0]) - '0') * 10 + (static_cast<unsigned char>(str[1]) - '0');
253-}
254+ ULARGE_INTEGER largeInt;
255+ largeInt.QuadPart = m_interval;
256
257-static const time_t maxTimeT = sizeof(time_t) == 4 ? (time_t)INT_MAX : (time_t)LLONG_MAX;
258+ FILETIME ft;
259+ ft.dwHighDateTime = largeInt.HighPart;
260+ ft.dwLowDateTime = largeInt.LowPart;
261
262-static time_t timezone_adjust(time_t result, unsigned char chSign, int adjustHours, int adjustMinutes)
263-{
264- if (adjustHours > 23)
265+ SYSTEMTIME systemTime;
266+ if (!FileTimeToSystemTime((const FILETIME*)&ft, &systemTime))
267 {
268- return (time_t)-1;
269+ throw utility::details::create_system_error(GetLastError());
270 }
271
272- // adjustMinutes > 59 is impossible due to digit 5 check
273- const int tzAdjust = adjustMinutes * 60 + adjustHours * 60 * 60;
274- if (chSign == '-')
275+ std::wstring result;
276+ if (format == RFC_1123)
277 {
278- if (maxTimeT - result < tzAdjust)
279 {
280- return (time_t)-1;
281+ wchar_t dateStr[18] = {0};
282+#if _WIN32_WINNT < _WIN32_WINNT_VISTA
283+ status = GetDateFormatW(
284+ LOCALE_INVARIANT, 0, &systemTime, L"ddd',' dd MMM yyyy", dateStr, sizeof(dateStr) / sizeof(wchar_t));
285+#else
286+ status = GetDateFormatEx(LOCALE_NAME_INVARIANT,
287+ 0,
288+ &systemTime,
289+ L"ddd',' dd MMM yyyy",
290+ dateStr,
291+ sizeof(dateStr) / sizeof(wchar_t),
292+ NULL);
293+#endif // _WIN32_WINNT < _WIN32_WINNT_VISTA
294+ if (status == 0)
295+ {
296+ throw utility::details::create_system_error(GetLastError());
297+ }
298+
299+ result += dateStr;
300+ result += L' ';
301 }
302
303- result += tzAdjust;
304- }
305- else
306- {
307- if (tzAdjust > result)
308 {
309- return (time_t)-1;
310- }
311-
312- result -= tzAdjust;
313- }
314-
315- return result;
316-}
317-
318-static time_t make_gm_time(struct tm* t)
319-{
320-#ifdef _MSC_VER
321- return _mkgmtime(t);
322-#elif (defined(ANDROID) || defined(__ANDROID__))
323- // HACK: The (nonportable?) POSIX function timegm is not available in
324- // bionic. As a workaround[1][2], we set the C library timezone to
325- // UTC, call mktime, then set the timezone back. However, the C
326- // environment is fundamentally a shared global resource and thread-
327- // unsafe. We can protect our usage here, however any other code might
328- // manipulate the environment at the same time.
329- //
330- // [1] http://linux.die.net/man/3/timegm
331- // [2] http://www.gnu.org/software/libc/manual/html_node/Broken_002ddown-Time.html
332- time_t time;
333- static boost::mutex env_var_lock;
334- {
335- boost::lock_guard<boost::mutex> lock(env_var_lock);
336- std::string prev_env;
337- auto prev_env_cstr = getenv("TZ");
338- if (prev_env_cstr != nullptr)
339- {
340- prev_env = prev_env_cstr;
341- }
342- setenv("TZ", "UTC", 1);
343-
344- time = mktime(t);
345+ wchar_t timeStr[10] = {0};
346+#if _WIN32_WINNT < _WIN32_WINNT_VISTA
347+ status = GetTimeFormatW(LOCALE_INVARIANT,
348+ TIME_NOTIMEMARKER | TIME_FORCE24HOURFORMAT,
349+ &systemTime,
350+ L"HH':'mm':'ss",
351+ timeStr,
352+ sizeof(timeStr) / sizeof(wchar_t));
353+#else
354+ status = GetTimeFormatEx(LOCALE_NAME_INVARIANT,
355+ TIME_NOTIMEMARKER | TIME_FORCE24HOURFORMAT,
356+ &systemTime,
357+ L"HH':'mm':'ss",
358+ timeStr,
359+ sizeof(timeStr) / sizeof(wchar_t));
360+#endif // _WIN32_WINNT < _WIN32_WINNT_VISTA
361+ if (status == 0)
362+ {
363+ throw utility::details::create_system_error(GetLastError());
364+ }
365
366- if (prev_env_cstr)
367- {
368- setenv("TZ", prev_env.c_str(), 1);
369- }
370- else
371- {
372- unsetenv("TZ");
373+ result += timeStr;
374+ result += L" GMT";
375 }
376 }
377- return time;
378-#else // ^^^ ANDROID // Other POSIX platforms vvv
379- return timegm(t);
380-#endif // _MSC_VER
381-}
382-
383-/*
384-https://tools.ietf.org/html/rfc822
385-https://tools.ietf.org/html/rfc1123
386-
387-date-time = [ day "," ] date time ; dd mm yy
388- ; hh:mm:ss zzz
389-
390-day = "Mon" / "Tue" / "Wed" / "Thu"
391- / "Fri" / "Sat" / "Sun"
392-
393-date = 1*2DIGIT month 2DIGIT ; day month year
394- ; e.g. 20 Jun 82
395-RFC1123 changes this to:
396-date = 1*2DIGIT month 2*4DIGIT ; day month year
397- ; e.g. 20 Jun 1982
398-This implementation only accepts 4 digit years.
399-
400-month = "Jan" / "Feb" / "Mar" / "Apr"
401- / "May" / "Jun" / "Jul" / "Aug"
402- / "Sep" / "Oct" / "Nov" / "Dec"
403-
404-time = hour zone ; ANSI and Military
405-
406-hour = 2DIGIT ":" 2DIGIT [":" 2DIGIT]
407- ; 00:00:00 - 23:59:59
408-
409-zone = "UT" / "GMT" ; Universal Time
410- ; North American : UT
411- / "EST" / "EDT" ; Eastern: - 5/ - 4
412- / "CST" / "CDT" ; Central: - 6/ - 5
413- / "MST" / "MDT" ; Mountain: - 7/ - 6
414- / "PST" / "PDT" ; Pacific: - 8/ - 7
415-
416-// military time deleted by RFC 1123
417-
418- / ( ("+" / "-") 4DIGIT ) ; Local differential
419- ; hours+min. (HHMM)
420-*/
421-
422-
423-datetime __cdecl datetime::from_string(const utility::string_t& dateString, date_format format)
424-{
425- datetime result;
426- time_t seconds;
427- uint64_t frac_sec = 0;
428- struct tm t{};
429- auto str = dateString.c_str();
430- if (format == RFC_1123)
431+ else if (format == ISO_8601)
432 {
433- int parsedWeekday = -1;
434- for (int day = 0; day < 7; ++day)
435+ const size_t buffSize = 64;
436 {
437- if (string_starts_with(str, dayNames + day * 4) && str[3] == _XPLATSTR(',') && str[4] == _XPLATSTR(' '))
438+ wchar_t dateStr[buffSize] = {0};
439+#if _WIN32_WINNT < _WIN32_WINNT_VISTA
440+ status = GetDateFormatW(LOCALE_INVARIANT, 0, &systemTime, L"yyyy-MM-dd", dateStr, buffSize);
441+#else
442+ status = GetDateFormatEx(LOCALE_NAME_INVARIANT, 0, &systemTime, L"yyyy-MM-dd", dateStr, buffSize, NULL);
443+#endif // _WIN32_WINNT < _WIN32_WINNT_VISTA
444+ if (status == 0)
445 {
446- parsedWeekday = day;
447- str += 5; // parsed day of week
448- break;
449+ throw utility::details::create_system_error(GetLastError());
450 }
451- }
452
453- if (ascii_isdigit3(str[0]) && ascii_isdigit(str[1]) && str[2] == _XPLATSTR(' '))
454- {
455- t.tm_mday = atoi2(str); // validity checked later
456- str += 3; // parsed day
457- }
458- else if (ascii_isdigit(str[0]) && str[1] == _XPLATSTR(' '))
459- {
460- t.tm_mday = str[0] - _XPLATSTR('0');
461- str += 2; // parsed day
462- }
463- else
464- {
465- return result;
466+ result += dateStr;
467+ result += L'T';
468 }
469
470- t.tm_mon = -1;
471- for (int month = 0; month < 12; ++month)
472 {
473- if (string_starts_with(str, monthNames + month * 4))
474+ wchar_t timeStr[buffSize] = {0};
475+#if _WIN32_WINNT < _WIN32_WINNT_VISTA
476+ status = GetTimeFormatW(LOCALE_INVARIANT,
477+ TIME_NOTIMEMARKER | TIME_FORCE24HOURFORMAT,
478+ &systemTime,
479+ L"HH':'mm':'ss",
480+ timeStr,
481+ buffSize);
482+#else
483+ status = GetTimeFormatEx(LOCALE_NAME_INVARIANT,
484+ TIME_NOTIMEMARKER | TIME_FORCE24HOURFORMAT,
485+ &systemTime,
486+ L"HH':'mm':'ss",
487+ timeStr,
488+ buffSize);
489+#endif // _WIN32_WINNT < _WIN32_WINNT_VISTA
490+ if (status == 0)
491 {
492- t.tm_mon = month;
493- break;
494+ throw utility::details::create_system_error(GetLastError());
495 }
496- }
497
498- if (t.tm_mon == -1 || str[3] != _XPLATSTR(' '))
499- {
500- return result;
501+ result += timeStr;
502 }
503
504- str += 4; // parsed month
505-
506- if (!ascii_isdigit3(str[0]) || !ascii_isdigit(str[1]) || !ascii_isdigit(str[2]) || !ascii_isdigit(str[3]) ||
507- str[4] != ' ')
508+ uint64_t frac_sec = largeInt.QuadPart % _secondTicks;
509+ if (frac_sec > 0)
510 {
511- return result;
512+ // Append fractional second, which is a 7-digit value with no trailing zeros
513+ // This way, '1200' becomes '00012'
514+ wchar_t buf[9] = {0};
515+ size_t appended = swprintf_s(buf, 9, L".%07ld", static_cast<long>(frac_sec));
516+ while (buf[appended - 1] == L'0')
517+ --appended; // trim trailing zeros
518+ result.append(buf, appended);
519 }
520
521- t.tm_year = (str[0] - _XPLATSTR('0')) * 1000 + (str[1] - _XPLATSTR('0')) * 100 +
522- (str[2] - _XPLATSTR('0')) * 10 + (str[3] - _XPLATSTR('0'));
523- if (t.tm_year < 1970 || t.tm_year > 3000)
524- {
525- return result;
526- }
527+ result += L'Z';
528+ }
529
530- // days in month validity check
531- if (!validate_day_month(t.tm_mday, t.tm_mon, t.tm_year))
532- {
533- return result;
534- }
535+ return result;
536+#else // LINUX
537+ uint64_t input = m_interval;
538+ uint64_t frac_sec = input % _secondTicks;
539+ input /= _secondTicks; // convert to seconds
540+ time_t time = (time_t)input - (time_t)11644473600LL; // diff between windows and unix epochs (seconds)
541+
542+ struct tm datetime;
543+ gmtime_r(&time, &datetime);
544+
545+ const int max_dt_length = 64;
546+ char output[max_dt_length + 1] = {0};
547+
548+ if (format != RFC_1123 && frac_sec > 0)
549+ {
550+ // Append fractional second, which is a 7-digit value with no trailing zeros
551+ // This way, '1200' becomes '00012'
552+ const int max_frac_length = 8;
553+ char buf[max_frac_length + 1] = {0};
554+ snprintf(buf, sizeof(buf), ".%07ld", (long int)frac_sec);
555+ // trim trailing zeros
556+ for (int i = max_frac_length - 1; buf[i] == '0'; i--)
557+ buf[i] = '\0';
558+ // format the datetime into a separate buffer
559+ char datetime_str[max_dt_length - max_frac_length - 1 + 1] = {0};
560+ strftime(datetime_str, sizeof(datetime_str), "%Y-%m-%dT%H:%M:%S", &datetime);
561+ // now print this buffer into the output buffer
562+ snprintf(output, sizeof(output), "%s%sZ", datetime_str, buf);
563+ }
564+ else
565+ {
566+ strftime(
567+ output, sizeof(output), format == RFC_1123 ? "%a, %d %b %Y %H:%M:%S GMT" : "%Y-%m-%dT%H:%M:%SZ", &datetime);
568+ }
569
570- t.tm_year -= 1900;
571- str += 5; // parsed year
572+ return std::string(output);
573+#endif
574+}
575
576- if (!ascii_isdigit2(str[0]) || !ascii_isdigit(str[1]) || str[2] != _XPLATSTR(':') || !ascii_isdigit5(str[3]) ||
577- !ascii_isdigit(str[4]))
578- {
579- return result;
580- }
581+#ifdef _WIN32
582+bool __cdecl datetime::system_type_to_datetime(void* pvsysTime, uint64_t seconds, datetime* pdt)
583+{
584+ SYSTEMTIME* psysTime = (SYSTEMTIME*)pvsysTime;
585+ FILETIME fileTime;
586
587- t.tm_hour = atoi2(str);
588- if (t.tm_hour > 23)
589- {
590- return result;
591- }
592+ if (SystemTimeToFileTime(psysTime, &fileTime))
593+ {
594+ ULARGE_INTEGER largeInt;
595+ largeInt.LowPart = fileTime.dwLowDateTime;
596+ largeInt.HighPart = fileTime.dwHighDateTime;
597
598- str += 3; // parsed hour
599- t.tm_min = atoi2(str);
600- str += 2; // parsed mins
601+ // Add hundredths of nanoseconds
602+ largeInt.QuadPart += seconds;
603
604- if (str[0] == ':')
605- {
606- if (!ascii_isdigit6(str[1]) || !ascii_isdigit(str[2]) || str[3] != _XPLATSTR(' '))
607- {
608- return result;
609- }
610+ *pdt = datetime(largeInt.QuadPart);
611+ return true;
612+ }
613+ return false;
614+}
615+#endif
616
617- t.tm_sec = atoi2(str + 1);
618- str += 4; // parsed seconds
619- }
620- else if (str[0] == _XPLATSTR(' '))
621- {
622- t.tm_sec = 0;
623- str += 1; // parsed seconds
624- }
625- else
626- {
627- return result;
628+// Take a string that represents a fractional second and return the number of ticks
629+// This is equivalent to doing atof on the string and multiplying by 10000000,
630+// but does not lose precision
631+template<typename StringIterator>
632+uint64_t timeticks_from_second(StringIterator begin, StringIterator end)
633+{
634+ int size = (int)(end - begin);
635+ _ASSERTE(begin[0] == U('.'));
636+ uint64_t ufrac_second = 0;
637+ for (int i = 1; i <= 7; ++i)
638+ {
639+ ufrac_second *= 10;
640+ int add = i < size ? begin[i] - U('0') : 0;
641+ ufrac_second += add;
642+ }
643+ return ufrac_second;
644+}
645+
646+void extract_fractional_second(const utility::string_t& dateString,
647+ utility::string_t& resultString,
648+ uint64_t& ufrac_second)
649+{
650+ resultString = dateString;
651+ // First, the string must be strictly longer than 2 characters, and the trailing character must be 'Z'
652+ if (resultString.size() > 2 && resultString[resultString.size() - 1] == U('Z'))
653+ {
654+ // Second, find the last non-digit by scanning the string backwards
655+ auto last_non_digit = std::find_if_not(resultString.rbegin() + 1, resultString.rend(), is_digit);
656+ if (last_non_digit < resultString.rend() - 1)
657+ {
658+ // Finally, make sure the last non-digit is a dot:
659+ auto last_dot = last_non_digit.base() - 1;
660+ if (*last_dot == U('.'))
661+ {
662+ // Got it! Now extract the fractional second
663+ auto last_before_Z = std::end(resultString) - 1;
664+ ufrac_second = timeticks_from_second(last_dot, last_before_Z);
665+ // And erase it from the string
666+ resultString.erase(last_dot, last_before_Z);
667+ }
668 }
669+ }
670+}
671
672- if (t.tm_sec > 60)
673- { // 60 to allow leap seconds
674- return result;
675- }
676+datetime __cdecl datetime::from_string(const utility::string_t& dateString, date_format format)
677+{
678+ // avoid floating point math to preserve precision
679+ uint64_t ufrac_second = 0;
680
681- t.tm_isdst = 0;
682- seconds = make_gm_time(&t);
683- if (seconds < 0)
684- {
685- return result;
686- }
687+#ifdef _WIN32
688+ datetime result;
689+ if (format == RFC_1123)
690+ {
691+ SYSTEMTIME sysTime = {0};
692
693- if (parsedWeekday >= 0 && parsedWeekday != t.tm_wday)
694- {
695- return result;
696- }
697+ std::wstring month(3, L'\0');
698+ std::wstring unused(3, L'\0');
699
700- if (!string_starts_with(str, "GMT") && !string_starts_with(str, "UT"))
701- {
702- // some timezone adjustment necessary
703- auto tzCh = _XPLATSTR('-');
704- int tzHours;
705- int tzMinutes = 0;
706- if (string_starts_with(str, "EDT"))
707- {
708- tzHours = 4;
709- }
710- else if (string_starts_with(str, "EST") || string_starts_with(str, "CDT"))
711- {
712- tzHours = 5;
713- }
714- else if (string_starts_with(str, "CST") || string_starts_with(str, "MDT"))
715- {
716- tzHours = 6;
717- }
718- else if (string_starts_with(str, "MST") || string_starts_with(str, "PDT"))
719- {
720- tzHours = 7;
721- }
722- else if (string_starts_with(str, "PST"))
723- {
724- tzHours = 8;
725- }
726- else if ((tzCh == _XPLATSTR('+') || tzCh == _XPLATSTR('-')) && ascii_isdigit2(str[1]) &&
727- ascii_isdigit(str[2]) && ascii_isdigit5(str[3]) && ascii_isdigit(str[4]))
728- {
729- tzCh = str[0];
730- tzHours = atoi2(str + 1);
731- tzMinutes = atoi2(str + 3);
732- }
733- else
734- {
735- return result;
736- }
737+ const wchar_t* formatString = L"%3c, %2d %3c %4d %2d:%2d:%2d %3c";
738+ auto n = swscanf_s(dateString.c_str(),
739+ formatString,
740+ unused.data(),
741+ unused.size(),
742+ &sysTime.wDay,
743+ month.data(),
744+ month.size(),
745+ &sysTime.wYear,
746+ &sysTime.wHour,
747+ &sysTime.wMinute,
748+ &sysTime.wSecond,
749+ unused.data(),
750+ unused.size());
751+
752+ if (n == 8)
753+ {
754+ std::wstring monthnames[12] = {
755+ L"Jan", L"Feb", L"Mar", L"Apr", L"May", L"Jun", L"Jul", L"Aug", L"Sep", L"Oct", L"Nov", L"Dec"};
756+ auto loc =
757+ std::find_if(monthnames, monthnames + 12, [&month](const std::wstring& m) { return m == month; });
758
759- seconds = timezone_adjust(seconds, static_cast<unsigned char>(tzCh), tzHours, tzMinutes);
760- if (seconds < 0)
761+ if (loc != monthnames + 12)
762 {
763- return result;
764+ sysTime.wMonth = (short)((loc - monthnames) + 1);
765+ if (system_type_to_datetime(&sysTime, ufrac_second, &result))
766+ {
767+ return result;
768+ }
769 }
770 }
771 }
772 else if (format == ISO_8601)
773 {
774- // parse year
775- if (!ascii_isdigit3(str[0]) || !ascii_isdigit(str[1]) || !ascii_isdigit(str[2]) || !ascii_isdigit(str[3]))
776- {
777- return result;
778- }
779+ // Unlike FILETIME, SYSTEMTIME does not have enough precision to hold seconds in 100 nanosecond
780+ // increments. Therefore, start with seconds and milliseconds set to 0, then add them separately
781
782- t.tm_year = (str[0] - _XPLATSTR('0')) * 1000 + (str[1] - _XPLATSTR('0')) * 100 +
783- (str[2] - _XPLATSTR('0')) * 10 + (str[3] - _XPLATSTR('0'));
784- if (t.tm_year < 1970 || t.tm_year > 3000)
785- {
786- return result;
787- }
788+ // Try to extract the fractional second from the timestamp
789+ utility::string_t input;
790+ extract_fractional_second(dateString, input, ufrac_second);
791+ {
792+ SYSTEMTIME sysTime = {0};
793+ const wchar_t* formatString = L"%4d-%2d-%2dT%2d:%2d:%2dZ";
794+ auto n = swscanf_s(input.c_str(),
795+ formatString,
796+ &sysTime.wYear,
797+ &sysTime.wMonth,
798+ &sysTime.wDay,
799+ &sysTime.wHour,
800+ &sysTime.wMinute,
801+ &sysTime.wSecond);
802
803- str += 4;
804- if (*str == _XPLATSTR('-'))
805- {
806- ++str;
807+ if (n == 3 || n == 6)
808+ {
809+ if (system_type_to_datetime(&sysTime, ufrac_second, &result))
810+ {
811+ return result;
812+ }
813+ }
814 }
815-
816- // parse month
817- if (!ascii_isdigit1(str[0]) || !ascii_isdigit(str[1]))
818 {
819- return result;
820- }
821+ SYSTEMTIME sysTime = {0};
822+ DWORD date = 0;
823
824- t.tm_mon = atoi2(str);
825- if (t.tm_mon < 1 || t.tm_mon > 12)
826- {
827- return result;
828- }
829-
830- t.tm_mon -= 1;
831- str += 2;
832+ const wchar_t* formatString = L"%8dT%2d:%2d:%2dZ";
833+ auto n = swscanf_s(input.c_str(), formatString, &date, &sysTime.wHour, &sysTime.wMinute, &sysTime.wSecond);
834
835- if (*str == _XPLATSTR('-'))
836- {
837- ++str;
838- }
839+ if (n == 1 || n == 4)
840+ {
841+ sysTime.wDay = date % 100;
842+ date /= 100;
843+ sysTime.wMonth = date % 100;
844+ date /= 100;
845+ sysTime.wYear = (WORD)date;
846
847- // parse day
848- if (!ascii_isdigit3(str[0]) || !ascii_isdigit(str[1]))
849- {
850- return result;
851+ if (system_type_to_datetime(&sysTime, ufrac_second, &result))
852+ {
853+ return result;
854+ }
855+ }
856 }
857-
858- t.tm_mday = atoi2(str);
859- if (!validate_day_month(t.tm_mday, t.tm_mon, t.tm_year))
860 {
861- return result;
862- }
863+ SYSTEMTIME sysTime = {0};
864+ GetSystemTime(&sysTime); // Fill date portion with today's information
865+ sysTime.wSecond = 0;
866+ sysTime.wMilliseconds = 0;
867
868- t.tm_year -= 1900;
869- str += 2;
870+ const wchar_t* formatString = L"%2d:%2d:%2dZ";
871+ auto n = swscanf_s(input.c_str(), formatString, &sysTime.wHour, &sysTime.wMinute, &sysTime.wSecond);
872
873- if (str[0] != _XPLATSTR('T') && str[0] != _XPLATSTR('t'))
874- {
875- // No time
876- seconds = make_gm_time(&t);
877- if (seconds < 0)
878+ if (n == 3)
879 {
880- return result;
881+ if (system_type_to_datetime(&sysTime, ufrac_second, &result))
882+ {
883+ return result;
884+ }
885 }
886-
887- seconds += ntToUnixOffsetSeconds;
888- result.m_interval = static_cast<interval_type>(seconds) * _secondTicks;
889- return result;
890 }
891+ }
892
893- ++str; // skip 'T'
894+ return datetime();
895+#else
896+ std::string input(dateString);
897
898- // parse hour
899- if (!ascii_isdigit2(str[0]) || !ascii_isdigit(str[1]))
900- {
901- return result;
902- }
903+ struct tm output = tm();
904
905- t.tm_hour = atoi2(str);
906- str += 2;
907- if (t.tm_hour > 23)
908- {
909- return result;
910- }
911+ if (format == RFC_1123)
912+ {
913+ strptime(input.data(), "%a, %d %b %Y %H:%M:%S GMT", &output);
914+ }
915+ else
916+ {
917+ // Try to extract the fractional second from the timestamp
918+ utility::string_t input;
919+ extract_fractional_second(dateString, input, ufrac_second);
920
921- if (*str == _XPLATSTR(':'))
922- {
923- ++str;
924- }
925+ auto result = strptime(input.data(), "%Y-%m-%dT%H:%M:%SZ", &output);
926
927- // parse minute
928- if (!ascii_isdigit5(str[0]) || !ascii_isdigit(str[1]))
929+ if (result == nullptr)
930 {
931- return result;
932+ result = strptime(input.data(), "%Y%m%dT%H:%M:%SZ", &output);
933 }
934- t.tm_min = atoi2(str);
935- // t.tm_min > 59 is impossible because we checked that the first digit is <= 5 in the basic format
936- // check above
937-
938- str += 2;
939-
940- if (*str == _XPLATSTR(':'))
941+ if (result == nullptr)
942 {
943- ++str;
944+ // Fill the date portion with the epoch,
945+ // strptime will do the rest
946+ memset(&output, 0, sizeof(struct tm));
947+ output.tm_year = 70;
948+ output.tm_mon = 1;
949+ output.tm_mday = 1;
950+ result = strptime(input.data(), "%H:%M:%SZ", &output);
951 }
952-
953- // parse seconds
954- if (!ascii_isdigit6(str[0]) || !ascii_isdigit(str[1]))
955+ if (result == nullptr)
956 {
957- return result;
958+ result = strptime(input.data(), "%Y-%m-%d", &output);
959 }
960-
961- t.tm_sec = atoi2(str);
962- // We allow 60 to account for leap seconds
963- if (t.tm_sec > 60)
964+ if (result == nullptr)
965 {
966- return result;
967+ result = strptime(input.data(), "%Y%m%d", &output);
968 }
969-
970- str += 2;
971- if (str[0] == _XPLATSTR('.') && ascii_isdigit(str[1]))
972+ if (result == nullptr)
973 {
974- ++str;
975- int digits = 7;
976- for (;;)
977- {
978- frac_sec *= 10;
979- frac_sec += *str - _XPLATSTR('0');
980- --digits;
981- ++str;
982- if (digits == 0)
983- {
984- while (ascii_isdigit(*str))
985- {
986- // consume remaining fractional second digits we can't use
987- ++str;
988- }
989-
990- break;
991- }
992-
993- if (!ascii_isdigit(*str))
994- {
995- // no more digits in the input, do the remaining multiplies we need
996- for (; digits != 0; --digits)
997- {
998- frac_sec *= 10;
999- }
1000-
1001- break;
1002- }
1003- }
1004+ return datetime();
1005 }
1006+ }
1007
1008- seconds = make_gm_time(&t);
1009- if (seconds < 0)
1010- {
1011- return result;
1012- }
1013+#if (defined(ANDROID) || defined(__ANDROID__))
1014+ // HACK: The (nonportable?) POSIX function timegm is not available in
1015+ // bionic. As a workaround[1][2], we set the C library timezone to
1016+ // UTC, call mktime, then set the timezone back. However, the C
1017+ // environment is fundamentally a shared global resource and thread-
1018+ // unsafe. We can protect our usage here, however any other code might
1019+ // manipulate the environment at the same time.
1020+ //
1021+ // [1] http://linux.die.net/man/3/timegm
1022+ // [2] http://www.gnu.org/software/libc/manual/html_node/Broken_002ddown-Time.html
1023+ time_t time;
1024
1025- if (str[0] == _XPLATSTR('Z') || str[0] == _XPLATSTR('z'))
1026+ static boost::mutex env_var_lock;
1027+ {
1028+ boost::lock_guard<boost::mutex> lock(env_var_lock);
1029+ std::string prev_env;
1030+ auto prev_env_cstr = getenv("TZ");
1031+ if (prev_env_cstr != nullptr)
1032 {
1033- // no adjustment needed for zulu time
1034+ prev_env = prev_env_cstr;
1035 }
1036- else if (str[0] == _XPLATSTR('+') || str[0] == _XPLATSTR('-'))
1037- {
1038- const unsigned char offsetDirection = static_cast<unsigned char>(str[0]);
1039- if (!ascii_isdigit2(str[1]) || !ascii_isdigit(str[2]) || str[3] != _XPLATSTR(':') ||
1040- !ascii_isdigit5(str[4]) || !ascii_isdigit(str[5]))
1041- {
1042- return result;
1043- }
1044+ setenv("TZ", "UTC", 1);
1045
1046- seconds = timezone_adjust(seconds, offsetDirection, atoi2(str + 1), atoi2(str + 4));
1047- if (seconds < 0)
1048- {
1049- return result;
1050- }
1051+ time = mktime(&output);
1052+
1053+ if (prev_env_cstr)
1054+ {
1055+ setenv("TZ", prev_env.c_str(), 1);
1056 }
1057 else
1058 {
1059- // the timezone is malformed, but cpprestsdk currently accepts this as no timezone
1060+ unsetenv("TZ");
1061 }
1062 }
1063- else
1064- {
1065- throw std::invalid_argument("unrecognized date format");
1066- }
1067+#else
1068+ time_t time = timegm(&output);
1069+#endif
1070+
1071+ struct timeval tv = timeval();
1072+ tv.tv_sec = time;
1073+ auto result = timeval_to_datetime(tv);
1074
1075- seconds += ntToUnixOffsetSeconds;
1076- result.m_interval = static_cast<interval_type>(seconds) * _secondTicks + frac_sec;
1077+ // fractional seconds are already in correct format so just add them.
1078+ result = result + ufrac_second;
1079 return result;
1080+#endif
1081 }
1082
1083 /// <summary>
1084@@ -1358,32 +1181,38 @@ utility::seconds __cdecl timespan::xml_d
1085 // The final S could be omitted
1086
1087 int64_t numSecs = 0;
1088- auto cursor = timespanString.c_str();
1089- auto c = *cursor++; // skip 'P'
1090- while (c)
1091+
1092+ utility::istringstream_t is(timespanString);
1093+ is.imbue(std::locale::classic());
1094+ auto eof = std::char_traits<utility::char_t>::eof();
1095+
1096+ std::basic_istream<utility::char_t>::int_type c;
1097+ c = is.get(); // P
1098+
1099+ while (c != eof)
1100 {
1101 int val = 0;
1102- c = *cursor++;
1103+ c = is.get();
1104
1105- while (ascii_isdigit(c))
1106+ while (is_digit((utility::char_t)c))
1107 {
1108- val = val * 10 + (c - _XPLATSTR('0'));
1109- c = *cursor++;
1110+ val = val * 10 + (c - L'0');
1111+ c = is.get();
1112
1113- if (c == _XPLATSTR('.'))
1114+ if (c == '.')
1115 {
1116 // decimal point is not handled
1117 do
1118 {
1119- c = *cursor++;
1120- } while (ascii_isdigit(c));
1121+ c = is.get();
1122+ } while (is_digit((utility::char_t)c));
1123 }
1124 }
1125
1126- if (c == _XPLATSTR('D')) numSecs += val * 24 * 3600; // days
1127- if (c == _XPLATSTR('H')) numSecs += val * 3600; // Hours
1128- if (c == _XPLATSTR('M')) numSecs += val * 60; // Minutes
1129- if (c == _XPLATSTR('S') || c == _XPLATSTR('\0'))
1130+ if (c == L'D') numSecs += val * 24 * 3600; // days
1131+ if (c == L'H') numSecs += val * 3600; // Hours
1132+ if (c == L'M') numSecs += val * 60; // Minutes
1133+ if (c == L'S' || c == eof)
1134 {
1135 numSecs += val; // seconds
1136 break;
1137@@ -1393,12 +1222,12 @@ utility::seconds __cdecl timespan::xml_d
1138 return utility::seconds(numSecs);
1139 }
1140
1141-static const utility::char_t c_allowed_chars[] =
1142- _XPLATSTR("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789");
1143+const utility::string_t nonce_generator::c_allowed_chars(
1144+ _XPLATSTR("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"));
1145
1146 utility::string_t nonce_generator::generate()
1147 {
1148- std::uniform_int_distribution<> distr(0, static_cast<int>(sizeof(c_allowed_chars) / sizeof(utility::char_t)) - 1);
1149+ std::uniform_int_distribution<> distr(0, static_cast<int>(c_allowed_chars.length() - 1));
1150 utility::string_t result;
1151 result.reserve(length());
1152 std::generate_n(std::back_inserter(result), length(), [&]() { return c_allowed_chars[distr(m_random)]; });
1153--- cpprest-2.10.12.orig/Release/tests/functional/utils/datetime.cpp
1154+++ cpprest-2.10.12/Release/tests/functional/utils/datetime.cpp
1155@@ -74,6 +74,16 @@ SUITE(datetime)
1156 }
1157 }
1158
1159+ TEST(parsing_time_extended)
1160+ {
1161+ // ISO 8601
1162+ {
1163+ auto dt = utility::datetime::from_string(_XPLATSTR("14:30:01Z"), utility::datetime::ISO_8601);
1164+
1165+ VERIFY_ARE_NOT_EQUAL(0u, dt.to_interval());
1166+ }
1167+ }
1168+
1169 void TestDateTimeRoundtrip(utility::string_t str, utility::string_t strExpected)
1170 {
1171 auto dt = utility::datetime::from_string(str, utility::datetime::ISO_8601);
1172@@ -91,8 +101,6 @@ SUITE(datetime)
1173
1174 TEST(parsing_time_roundtrip_datetime2)
1175 {
1176- // lose the last '000'
1177- TestDateTimeRoundtrip(_XPLATSTR("2013-11-19T14:30:59.1234567000Z"), _XPLATSTR("2013-11-19T14:30:59.1234567Z"));
1178 // lose the last '999' without rounding up
1179 TestDateTimeRoundtrip(_XPLATSTR("2013-11-19T14:30:59.1234567999Z"), _XPLATSTR("2013-11-19T14:30:59.1234567Z"));
1180 }
1181@@ -121,260 +129,16 @@ SUITE(datetime)
1182 TestDateTimeRoundtrip(_XPLATSTR("2013-11-19T14:30:59.5Z"));
1183 }
1184
1185- void TestRfc1123IsTimeT(const utility::char_t* str, time_t t)
1186- {
1187- datetime dt = datetime::from_string(str, utility::datetime::RFC_1123);
1188- uint64_t interval = dt.to_interval();
1189- VERIFY_ARE_EQUAL(0, interval % 10000000);
1190- interval /= 10000000;
1191- interval -= 11644473600; // NT epoch adjustment
1192- VERIFY_ARE_EQUAL(static_cast<uint64_t>(t), interval);
1193- }
1194-
1195- TEST(parsing_time_rfc1123_accepts_each_day)
1196- {
1197- TestRfc1123IsTimeT(_XPLATSTR("01 Jan 1970 00:00:00 GMT"), (time_t) 0);
1198- TestRfc1123IsTimeT(_XPLATSTR("Fri, 02 Jan 1970 00:00:00 GMT"), (time_t) 86400 * 1);
1199- TestRfc1123IsTimeT(_XPLATSTR("Sat, 03 Jan 1970 00:00:00 GMT"), (time_t) 86400 * 2);
1200- TestRfc1123IsTimeT(_XPLATSTR("Sun, 04 Jan 1970 00:00:00 GMT"), (time_t) 86400 * 3);
1201- TestRfc1123IsTimeT(_XPLATSTR("Mon, 05 Jan 1970 00:00:00 GMT"), (time_t) 86400 * 4);
1202- TestRfc1123IsTimeT(_XPLATSTR("Tue, 06 Jan 1970 00:00:00 GMT"), (time_t) 86400 * 5);
1203- TestRfc1123IsTimeT(_XPLATSTR("Wed, 07 Jan 1970 00:00:00 GMT"), (time_t) 86400 * 6);
1204- }
1205-
1206- TEST(parsing_time_rfc1123_boundary_cases)
1207- {
1208- TestRfc1123IsTimeT(_XPLATSTR("01 Jan 1970 00:00:00 GMT"), (time_t) 0);
1209- TestRfc1123IsTimeT(_XPLATSTR("19 Jan 2038 03:14:06 GMT"), (time_t) INT_MAX - 1);
1210-#ifndef _USE_32BIT_TIME_T
1211- TestRfc1123IsTimeT(_XPLATSTR("19 Jan 2038 03:13:07 -0001"), (time_t) INT_MAX);
1212- TestRfc1123IsTimeT(_XPLATSTR("19 Jan 2038 03:14:07 -0000"), (time_t) INT_MAX);
1213-#endif // _USE_32BIT_TIME_T
1214- TestRfc1123IsTimeT(_XPLATSTR("14 Jan 2019 23:16:21 +0000"), (time_t) 1547507781);
1215- TestRfc1123IsTimeT(_XPLATSTR("14 Jan 2019 23:16:21 -0001"), (time_t) 1547507841);
1216- TestRfc1123IsTimeT(_XPLATSTR("14 Jan 2019 23:16:21 +0001"), (time_t) 1547507721);
1217- TestRfc1123IsTimeT(_XPLATSTR("14 Jan 2019 23:16:21 -0100"), (time_t) 1547511381);
1218- TestRfc1123IsTimeT(_XPLATSTR("14 Jan 2019 23:16:21 +0100"), (time_t) 1547504181);
1219- }
1220-
1221- TEST(parsing_time_rfc1123_uses_each_field)
1222- {
1223- TestRfc1123IsTimeT(_XPLATSTR("02 Jan 1970 00:00:00 GMT"), (time_t) 86400);
1224- TestRfc1123IsTimeT(_XPLATSTR("12 Jan 1970 00:00:00 GMT"), (time_t) 950400);
1225- TestRfc1123IsTimeT(_XPLATSTR("01 Feb 1970 00:00:00 GMT"), (time_t) 2678400);
1226- TestRfc1123IsTimeT(_XPLATSTR("01 Jan 2000 00:00:00 GMT"), (time_t) 946684800);
1227-#ifndef _USE_32BIT_TIME_T
1228- TestRfc1123IsTimeT(_XPLATSTR("01 Jan 2100 00:00:00 GMT"), (time_t) 4102444800);
1229-#endif // _USE_32BIT_TIME_T
1230- TestRfc1123IsTimeT(_XPLATSTR("01 Jan 1990 00:00:00 GMT"), (time_t) 631152000);
1231- TestRfc1123IsTimeT(_XPLATSTR("01 Jan 1971 00:00:00 GMT"), (time_t) 31536000);
1232- TestRfc1123IsTimeT(_XPLATSTR("01 Jan 1970 10:00:00 GMT"), (time_t) 36000);
1233- TestRfc1123IsTimeT(_XPLATSTR("01 Jan 1970 01:00:00 GMT"), (time_t) 3600);
1234- TestRfc1123IsTimeT(_XPLATSTR("01 Jan 1970 00:10:00 GMT"), (time_t) 600);
1235- TestRfc1123IsTimeT(_XPLATSTR("01 Jan 1970 00:01:00 GMT"), (time_t) 60);
1236- TestRfc1123IsTimeT(_XPLATSTR("01 Jan 1970 00:00:10 GMT"), (time_t) 10);
1237- TestRfc1123IsTimeT(_XPLATSTR("01 Jan 1970 00:00:01 GMT"), (time_t) 1);
1238- TestRfc1123IsTimeT(_XPLATSTR("01 Jan 1970 10:00:00 GMT"), (time_t) 36000);
1239- TestRfc1123IsTimeT(_XPLATSTR("01 Jan 1970 02:00:00 PST"), (time_t) 36000);
1240- TestRfc1123IsTimeT(_XPLATSTR("01 Jan 1970 03:00:00 PDT"), (time_t) 36000);
1241- TestRfc1123IsTimeT(_XPLATSTR("01 Jan 1970 03:00:00 MST"), (time_t) 36000);
1242- TestRfc1123IsTimeT(_XPLATSTR("01 Jan 1970 04:00:00 MDT"), (time_t) 36000);
1243- TestRfc1123IsTimeT(_XPLATSTR("01 Jan 1970 04:00:00 CST"), (time_t) 36000);
1244- TestRfc1123IsTimeT(_XPLATSTR("01 Jan 1970 05:00:00 CDT"), (time_t) 36000);
1245- TestRfc1123IsTimeT(_XPLATSTR("01 Jan 1970 05:00:00 EST"), (time_t) 36000);
1246- TestRfc1123IsTimeT(_XPLATSTR("01 Jan 1970 06:00:00 EDT"), (time_t) 36000);
1247- TestRfc1123IsTimeT(_XPLATSTR("01 Jan 1970 06:00:00 -0400"), (time_t) 36000);
1248- TestRfc1123IsTimeT(_XPLATSTR("01 Jan 1970 05:59:00 -0401"), (time_t) 36000);
1249- }
1250-
1251- TEST(parsing_time_rfc1123_max_days)
1252- {
1253- TestRfc1123IsTimeT(_XPLATSTR("31 Jan 1970 00:00:00 GMT"), (time_t) 2592000);
1254- TestRfc1123IsTimeT(_XPLATSTR("28 Feb 2019 00:00:00 GMT"), (time_t) 1551312000); // non leap year allows feb 28
1255- TestRfc1123IsTimeT(_XPLATSTR("29 Feb 2020 00:00:00 GMT"), (time_t) 1582934400); // leap year allows feb 29
1256- TestRfc1123IsTimeT(_XPLATSTR("31 Mar 1970 00:00:00 GMT"), (time_t) 7689600);
1257- TestRfc1123IsTimeT(_XPLATSTR("30 Apr 1970 00:00:00 GMT"), (time_t) 10281600);
1258- TestRfc1123IsTimeT(_XPLATSTR("31 May 1970 00:00:00 GMT"), (time_t) 12960000);
1259- TestRfc1123IsTimeT(_XPLATSTR("30 Jun 1970 00:00:00 GMT"), (time_t) 15552000);
1260- TestRfc1123IsTimeT(_XPLATSTR("31 Jul 1970 00:00:00 GMT"), (time_t) 18230400);
1261- TestRfc1123IsTimeT(_XPLATSTR("31 Aug 1970 00:00:00 GMT"), (time_t) 20908800);
1262- TestRfc1123IsTimeT(_XPLATSTR("30 Sep 1970 00:00:00 GMT"), (time_t) 23500800);
1263- TestRfc1123IsTimeT(_XPLATSTR("31 Oct 1970 00:00:00 GMT"), (time_t) 26179200);
1264- TestRfc1123IsTimeT(_XPLATSTR("30 Nov 1970 00:00:00 GMT"), (time_t) 28771200);
1265- TestRfc1123IsTimeT(_XPLATSTR("31 Dec 1970 00:00:00 GMT"), (time_t) 31449600);
1266- }
1267-
1268- TEST(parsing_time_rfc1123_invalid_cases)
1269- {
1270- const utility::string_t bad_strings[] = {
1271- _XPLATSTR("Ahu, 01 Jan 1970 00:00:00 GMT"), // bad letters in each place
1272- _XPLATSTR("TAu, 01 Jan 1970 00:00:00 GMT"),
1273- _XPLATSTR("ThA, 01 Jan 1970 00:00:00 GMT"),
1274- _XPLATSTR("ThuA 01 Jan 1970 00:00:00 GMT"),
1275- _XPLATSTR("Thu,A01 Jan 1970 00:00:00 GMT"),
1276- _XPLATSTR("Thu, A1 Jan 1970 00:00:00 GMT"),
1277- _XPLATSTR("Thu, 0A Jan 1970 00:00:00 GMT"),
1278- _XPLATSTR("Thu, 01AJan 1970 00:00:00 GMT"),
1279- _XPLATSTR("Thu, 01 Aan 1970 00:00:00 GMT"),
1280- _XPLATSTR("Thu, 01 JAn 1970 00:00:00 GMT"),
1281- _XPLATSTR("Thu, 01 JaA 1970 00:00:00 GMT"),
1282- _XPLATSTR("Thu, 01 JanA1970 00:00:00 GMT"),
1283- _XPLATSTR("Thu, 01 Jan A970 00:00:00 GMT"),
1284- _XPLATSTR("Thu, 01 Jan 1A70 00:00:00 GMT"),
1285- _XPLATSTR("Thu, 01 Jan 19A0 00:00:00 GMT"),
1286- _XPLATSTR("Thu, 01 Jan 197A 00:00:00 GMT"),
1287- _XPLATSTR("Thu, 01 Jan 1970A00:00:00 GMT"),
1288- _XPLATSTR("Thu, 01 Jan 1970 A0:00:00 GMT"),
1289- _XPLATSTR("Thu, 01 Jan 1970 0A:00:00 GMT"),
1290- _XPLATSTR("Thu, 01 Jan 1970 00A00:00 GMT"),
1291- _XPLATSTR("Thu, 01 Jan 1970 00:A0:00 GMT"),
1292- _XPLATSTR("Thu, 01 Jan 1970 00:0A:00 GMT"),
1293- _XPLATSTR("Thu, 01 Jan 1970 00:00A00 GMT"),
1294- _XPLATSTR("Thu, 01 Jan 1970 00:00:A0 GMT"),
1295- _XPLATSTR("Thu, 01 Jan 1970 00:00:0A GMT"),
1296- _XPLATSTR("Thu, 01 Jan 1970 00:00:00AGMT"),
1297- _XPLATSTR("Thu, 01 Jan 1970 00:00:00 AMT"),
1298- _XPLATSTR("Thu, 01 Jan 1970 00:00:00 GAT"),
1299- _XPLATSTR("Thu, 01 Jan 1970 00:00:00 GMA"),
1300- _XPLATSTR(""), // truncation
1301- _XPLATSTR("T"),
1302- _XPLATSTR("Th"),
1303- _XPLATSTR("Thu"),
1304- _XPLATSTR("Thu,"),
1305- _XPLATSTR("Thu, "),
1306- _XPLATSTR("Thu, 0"),
1307- _XPLATSTR("Thu, 01"),
1308- _XPLATSTR("Thu, 01 "),
1309- _XPLATSTR("Thu, 01 J"),
1310- _XPLATSTR("Thu, 01 Ja"),
1311- _XPLATSTR("Thu, 01 Jan"),
1312- _XPLATSTR("Thu, 01 Jan "),
1313- _XPLATSTR("Thu, 01 Jan 1"),
1314- _XPLATSTR("Thu, 01 Jan 19"),
1315- _XPLATSTR("Thu, 01 Jan 197"),
1316- _XPLATSTR("Thu, 01 Jan 1970"),
1317- _XPLATSTR("Thu, 01 Jan 1970 "),
1318- _XPLATSTR("Thu, 01 Jan 1970 0"),
1319- _XPLATSTR("Thu, 01 Jan 1970 00"),
1320- _XPLATSTR("Thu, 01 Jan 1970 00:"),
1321- _XPLATSTR("Thu, 01 Jan 1970 00:0"),
1322- _XPLATSTR("Thu, 01 Jan 1970 00:00"),
1323- _XPLATSTR("Thu, 01 Jan 1970 00:00:"),
1324- _XPLATSTR("Thu, 01 Jan 1970 00:00:0"),
1325- _XPLATSTR("Thu, 01 Jan 1970 00:00:00"),
1326- _XPLATSTR("Thu, 01 Jan 1970 00:00:00 "),
1327- _XPLATSTR("Thu, 01 Jan 1970 00:00:00 G"),
1328- _XPLATSTR("Thu, 01 Jan 1970 00:00:00 GM"),
1329- _XPLATSTR("Fri, 01 Jan 1970 00:00:00 GMT"), // wrong day
1330- _XPLATSTR("01 Jan 4970 00:00:00 GMT"), // year too big
1331- _XPLATSTR("01 Jan 3001 00:00:00 GMT"),
1332- _XPLATSTR("01 Xxx 1971 00:00:00 GMT"), // month bad
1333- _XPLATSTR("00 Jan 1971 00:00:00 GMT"), // day too small
1334- _XPLATSTR("32 Jan 1971 00:00:00 GMT"), // day too big
1335- _XPLATSTR("30 Feb 1971 00:00:00 GMT"), // day too big for feb
1336- _XPLATSTR("30 Feb 1971 00:00:00 GMT"), // day too big for feb (non-leap year)
1337- _XPLATSTR("32 Mar 1971 00:00:00 GMT"), // other months
1338- _XPLATSTR("31 Apr 1971 00:00:00 GMT"),
1339- _XPLATSTR("32 May 1971 00:00:00 GMT"),
1340- _XPLATSTR("31 Jun 1971 00:00:00 GMT"),
1341- _XPLATSTR("32 Jul 1971 00:00:00 GMT"),
1342- _XPLATSTR("32 Aug 1971 00:00:00 GMT"),
1343- _XPLATSTR("31 Sep 1971 00:00:00 GMT"),
1344- _XPLATSTR("32 Oct 1971 00:00:00 GMT"),
1345- _XPLATSTR("31 Nov 1971 00:00:00 GMT"),
1346- _XPLATSTR("32 Dec 1971 00:00:00 GMT"),
1347- _XPLATSTR("01 Jan 1971 70:00:00 GMT"), // hour too big
1348- _XPLATSTR("01 Jan 1971 24:00:00 GMT"),
1349- _XPLATSTR("01 Jan 1971 00:60:00 GMT"), // minute too big
1350- _XPLATSTR("01 Jan 1971 00:00:70 GMT"), // second too big
1351- _XPLATSTR("01 Jan 1971 00:00:61 GMT"),
1352- _XPLATSTR("01 Jan 1969 00:00:00 GMT"), // underflow
1353- _XPLATSTR("01 Jan 1969 00:00:00 CEST"), // bad tz
1354- _XPLATSTR("01 Jan 1970 00:00:00 +2400"), // bad tzoffsets
1355- _XPLATSTR("01 Jan 1970 00:00:00 -3000"),
1356- _XPLATSTR("01 Jan 1970 00:00:00 +2160"),
1357- _XPLATSTR("01 Jan 1970 00:00:00 -2400"),
1358- _XPLATSTR("01 Jan 1970 00:00:00 -2160"),
1359- };
1360-
1361- for (const auto& str : bad_strings)
1362- {
1363- auto dt = utility::datetime::from_string(str, utility::datetime::RFC_1123);
1364- VERIFY_ARE_EQUAL(0, dt.to_interval());
1365- }
1366- }
1367-
1368- TEST(parsing_time_iso8601_boundary_cases)
1369- {
1370- // boundary cases:
1371- TestDateTimeRoundtrip(_XPLATSTR("1970-01-01T00:00:00Z")); // epoch
1372- TestDateTimeRoundtrip(_XPLATSTR("2038-01-19T03:14:06+00:00"), _XPLATSTR("2038-01-19T03:14:06Z")); // INT_MAX - 1
1373-#ifndef _USE_32BIT_TIME_T
1374- TestDateTimeRoundtrip(_XPLATSTR("2038-01-19T03:13:07-00:01"),
1375- _XPLATSTR("2038-01-19T03:14:07Z")); // INT_MAX after subtacting 1
1376- TestDateTimeRoundtrip(_XPLATSTR("2038-01-19T03:14:07-00:00"), _XPLATSTR("2038-01-19T03:14:07Z"));
1377-#endif // _USE_32BIT_TIME_T
1378- }
1379-
1380- TEST(parsing_time_iso8601_uses_each_timezone_digit)
1381- {
1382- TestDateTimeRoundtrip(_XPLATSTR("2019-01-14T23:16:21+00:00"), _XPLATSTR("2019-01-14T23:16:21Z"));
1383- TestDateTimeRoundtrip(_XPLATSTR("2019-01-14T23:16:21-00:01"), _XPLATSTR("2019-01-14T23:17:21Z"));
1384- TestDateTimeRoundtrip(_XPLATSTR("2019-01-14T23:16:21+00:01"), _XPLATSTR("2019-01-14T23:15:21Z"));
1385- TestDateTimeRoundtrip(_XPLATSTR("2019-01-14T23:16:21-01:00"), _XPLATSTR("2019-01-15T00:16:21Z"));
1386- TestDateTimeRoundtrip(_XPLATSTR("2019-01-14T23:16:21+01:00"), _XPLATSTR("2019-01-14T22:16:21Z"));
1387- }
1388-
1389- TEST(parsing_time_iso8601_uses_each_digit)
1390- {
1391- TestDateTimeRoundtrip(_XPLATSTR("1970-01-01T00:00:01Z"));
1392- TestDateTimeRoundtrip(_XPLATSTR("1970-01-01T00:01:00Z"));
1393- TestDateTimeRoundtrip(_XPLATSTR("1970-01-01T01:00:00Z"));
1394- TestDateTimeRoundtrip(_XPLATSTR("1970-01-02T00:00:00Z"));
1395- TestDateTimeRoundtrip(_XPLATSTR("1970-02-01T00:00:00Z"));
1396- TestDateTimeRoundtrip(_XPLATSTR("1971-01-01T00:00:00Z"));
1397-
1398- TestDateTimeRoundtrip(_XPLATSTR("1999-01-01T00:00:00Z"));
1399- TestDateTimeRoundtrip(_XPLATSTR("1970-12-01T00:00:00Z"));
1400- TestDateTimeRoundtrip(_XPLATSTR("1970-09-01T00:00:00Z"));
1401- TestDateTimeRoundtrip(_XPLATSTR("1970-01-30T00:00:00Z"));
1402- TestDateTimeRoundtrip(_XPLATSTR("1970-01-31T00:00:00Z"));
1403- TestDateTimeRoundtrip(_XPLATSTR("1970-01-01T23:00:00Z"));
1404- TestDateTimeRoundtrip(_XPLATSTR("1970-01-01T19:00:00Z"));
1405- TestDateTimeRoundtrip(_XPLATSTR("1970-01-01T00:59:00Z"));
1406- TestDateTimeRoundtrip(_XPLATSTR("1970-01-01T00:00:59Z"));
1407- TestDateTimeRoundtrip(_XPLATSTR("1970-01-01T00:00:60Z"), _XPLATSTR("1970-01-01T00:01:00Z")); // leap seconds
1408- }
1409-
1410- TEST(parsing_time_iso8601_accepts_month_max_days)
1411- {
1412- TestDateTimeRoundtrip(_XPLATSTR("1970-01-31T00:00:00Z")); // jan
1413- TestDateTimeRoundtrip(_XPLATSTR("2019-02-28T00:00:00Z")); // non leap year allows feb 28
1414- TestDateTimeRoundtrip(_XPLATSTR("2020-02-29T00:00:00Z")); // leap year allows feb 29
1415- TestDateTimeRoundtrip(_XPLATSTR("1970-03-31T00:00:00Z")); // mar
1416- TestDateTimeRoundtrip(_XPLATSTR("1970-04-30T00:00:00Z")); // apr
1417- TestDateTimeRoundtrip(_XPLATSTR("1970-05-31T00:00:00Z")); // may
1418- TestDateTimeRoundtrip(_XPLATSTR("1970-06-30T00:00:00Z")); // jun
1419- TestDateTimeRoundtrip(_XPLATSTR("1970-07-31T00:00:00Z")); // jul
1420- TestDateTimeRoundtrip(_XPLATSTR("1970-08-31T00:00:00Z")); // aug
1421- TestDateTimeRoundtrip(_XPLATSTR("1970-09-30T00:00:00Z")); // sep
1422- TestDateTimeRoundtrip(_XPLATSTR("1970-10-31T00:00:00Z")); // oct
1423- TestDateTimeRoundtrip(_XPLATSTR("1970-11-30T00:00:00Z")); // nov
1424- TestDateTimeRoundtrip(_XPLATSTR("1970-12-31T00:00:00Z")); // dec
1425- }
1426-
1427- TEST(parsing_time_iso8601_accepts_lowercase_t_z)
1428- {
1429- TestDateTimeRoundtrip(_XPLATSTR("1970-01-01t00:00:00Z"), _XPLATSTR("1970-01-01T00:00:00Z"));
1430- TestDateTimeRoundtrip(_XPLATSTR("1970-01-01T00:00:00z"), _XPLATSTR("1970-01-01T00:00:00Z"));
1431- }
1432-
1433- TEST(parsing_time_roundtrip_datetime_accepts_invalid_no_trailing_timezone)
1434+ TEST(parsing_time_roundtrip_datetime_invalid1,
1435+ "Ignore:Linux",
1436+ "Codeplex issue #115",
1437+ "Ignore:Apple",
1438+ "Codeplex issue #115")
1439 {
1440 // No digits after the dot, or non-digits. This is not a valid input, but we should not choke on it,
1441 // Simply ignore the bad fraction
1442 const utility::string_t bad_strings[] = {_XPLATSTR("2013-11-19T14:30:59.Z"),
1443- _XPLATSTR("2013-11-19T14:30:59.a12Z")};
1444+ _XPLATSTR("2013-11-19T14:30:59.1a2Z")};
1445 utility::string_t str_corrected = _XPLATSTR("2013-11-19T14:30:59Z");
1446
1447 for (const auto& str : bad_strings)
1448@@ -387,87 +151,12 @@ SUITE(datetime)
1449
1450 TEST(parsing_time_roundtrip_datetime_invalid2)
1451 {
1452- // Various unsupported cases. In all cases, we have produce an empty date time
1453+ // Variouls unsupported cases. In all cases, we have produce an empty date time
1454 const utility::string_t bad_strings[] = {
1455- _XPLATSTR(""), // empty
1456- _XPLATSTR(".Z"), // too short
1457- _XPLATSTR(".Zx"), // no trailing Z
1458- _XPLATSTR("3.14Z") // not a valid date
1459- _XPLATSTR("a971-01-01T00:00:00Z"), // any non digits or valid separators
1460- _XPLATSTR("1a71-01-01T00:00:00Z"),
1461- _XPLATSTR("19a1-01-01T00:00:00Z"),
1462- _XPLATSTR("197a-01-01T00:00:00Z"),
1463- _XPLATSTR("1971a01-01T00:00:00Z"),
1464- _XPLATSTR("1971-a1-01T00:00:00Z"),
1465- _XPLATSTR("1971-0a-01T00:00:00Z"),
1466- _XPLATSTR("1971-01a01T00:00:00Z"),
1467- _XPLATSTR("1971-01-a1T00:00:00Z"),
1468- _XPLATSTR("1971-01-0aT00:00:00Z"),
1469- // _XPLATSTR("1971-01-01a00:00:00Z"), parsed as complete date
1470- _XPLATSTR("1971-01-01Ta0:00:00Z"),
1471- _XPLATSTR("1971-01-01T0a:00:00Z"),
1472- _XPLATSTR("1971-01-01T00a00:00Z"),
1473- _XPLATSTR("1971-01-01T00:a0:00Z"),
1474- _XPLATSTR("1971-01-01T00:0a:00Z"),
1475- _XPLATSTR("1971-01-01T00:00a00Z"),
1476- _XPLATSTR("1971-01-01T00:00:a0Z"),
1477- _XPLATSTR("1971-01-01T00:00:0aZ"),
1478- // "1971-01-01T00:00:00a", accepted as per invalid_no_trailing_timezone above
1479- _XPLATSTR("1"), // truncation
1480- _XPLATSTR("19"),
1481- _XPLATSTR("197"),
1482- _XPLATSTR("1970"),
1483- _XPLATSTR("1970-"),
1484- _XPLATSTR("1970-0"),
1485- _XPLATSTR("1970-01"),
1486- _XPLATSTR("1970-01-"),
1487- _XPLATSTR("1970-01-0"),
1488- // _XPLATSTR("1970-01-01"), complete date
1489- _XPLATSTR("1970-01-01T"),
1490- _XPLATSTR("1970-01-01T0"),
1491- _XPLATSTR("1970-01-01T00"),
1492- _XPLATSTR("1970-01-01T00:"),
1493- _XPLATSTR("1970-01-01T00:0"),
1494- _XPLATSTR("1970-01-01T00:00"),
1495- _XPLATSTR("1970-01-01T00:00:"),
1496- _XPLATSTR("1970-01-01T00:00:0"),
1497- // _XPLATSTR("1970-01-01T00:00:00"), // accepted as invalid timezone above
1498- _XPLATSTR("4970-01-01T00:00:00Z"), // year too big
1499- _XPLATSTR("3001-01-01T00:00:00Z"),
1500- _XPLATSTR("1971-00-01T00:00:00Z"), // month too small
1501- _XPLATSTR("1971-20-01T00:00:00Z"), // month too big
1502- _XPLATSTR("1971-13-01T00:00:00Z"),
1503- _XPLATSTR("1971-01-00T00:00:00Z"), // day too small
1504- _XPLATSTR("1971-01-32T00:00:00Z"), // day too big
1505- _XPLATSTR("1971-02-30T00:00:00Z"), // day too big for feb
1506- _XPLATSTR("1971-02-30T00:00:00Z"), // day too big for feb (non-leap year)
1507- _XPLATSTR("1971-03-32T00:00:00Z"), // other months
1508- _XPLATSTR("1971-04-31T00:00:00Z"),
1509- _XPLATSTR("1971-05-32T00:00:00Z"),
1510- _XPLATSTR("1971-06-31T00:00:00Z"),
1511- _XPLATSTR("1971-07-32T00:00:00Z"),
1512- _XPLATSTR("1971-08-32T00:00:00Z"),
1513- _XPLATSTR("1971-09-31T00:00:00Z"),
1514- _XPLATSTR("1971-10-32T00:00:00Z"),
1515- _XPLATSTR("1971-11-31T00:00:00Z"),
1516- _XPLATSTR("1971-12-32T00:00:00Z"),
1517- _XPLATSTR("1971-01-01T70:00:00Z"), // hour too big
1518- _XPLATSTR("1971-01-01T24:00:00Z"),
1519- _XPLATSTR("1971-01-01T00:60:00Z"), // minute too big
1520- _XPLATSTR("1971-01-01T00:00:70Z"), // second too big
1521- _XPLATSTR("1971-01-01T00:00:61Z"),
1522- _XPLATSTR("1969-01-01T00:00:00Z"), // underflow
1523-#ifdef _USE_32BIT_TIME_T
1524- _XPLATSTR("3000-01-01T00:00:01Z"), // overflow
1525-#endif
1526- _XPLATSTR("3001-01-01T00:00:00Z"),
1527- _XPLATSTR("1970-01-01T00:00:00+00:01"), // time zone underflow
1528- // _XPLATSTR("1970-01-01T00:00:00.Z"), // accepted as invalid timezone above
1529- _XPLATSTR("1970-01-01T00:00:00+24:00"), // bad tzoffsets
1530- _XPLATSTR("1970-01-01T00:00:00-30:00"),
1531- _XPLATSTR("1970-01-01T00:00:00+21:60"),
1532- _XPLATSTR("1970-01-01T00:00:00-24:00"),
1533- _XPLATSTR("1970-01-01T00:00:00-21:60"),
1534+ _XPLATSTR(""), // empty
1535+ _XPLATSTR(".Z"), // too short
1536+ _XPLATSTR(".Zx"), // no trailing Z
1537+ _XPLATSTR("3.14Z") // not a valid date
1538 };
1539
1540 for (const auto& str : bad_strings)
1541@@ -477,6 +166,16 @@ SUITE(datetime)
1542 }
1543 }
1544
1545+ TEST(parsing_time_roundtrip_time)
1546+ {
1547+ // time only without date
1548+ utility::string_t str = _XPLATSTR("14:30:59.1234567Z");
1549+ auto dt = utility::datetime::from_string(str, utility::datetime::ISO_8601);
1550+ utility::string_t str2 = dt.to_string(utility::datetime::ISO_8601);
1551+ // Must look for a substring now, since the date part is filled with today's date
1552+ VERIFY_IS_TRUE(str2.find(str) != std::string::npos);
1553+ }
1554+
1555 } // SUITE(datetime)
1556
1557 } // namespace utils_tests