@@ -30,7 +30,9 @@ using namespace Platform;
3030using namespace Windows ::Storage::Streams;
3131#endif // #if !defined(__cplusplus_winrt)
3232
33- #if !defined(_MS_WINDOWS)
33+ #if defined(_MS_WINDOWS)
34+ #include < regex>
35+ #else
3436#include < boost/date_time/posix_time/posix_time.hpp>
3537#include < boost/date_time/posix_time/posix_time_io.hpp>
3638// GCC 4.8 does not support regex, use boost. TODO: switch to std::regex in GCC 4.9
@@ -554,24 +556,25 @@ utility::string_t datetime::to_string(date_format format) const
554556 }
555557 else if ( format == ISO_8601 )
556558 {
559+ const size_t buffSize = 64 ;
557560#if _WIN32_WINNT < _WIN32_WINNT_VISTA
558- TCHAR dateStr[18 ] = {0 };
559- status = GetDateFormat (LOCALE_INVARIANT, 0 , &systemTime, " yyyy-MM-dd" , dateStr, sizeof (dateStr) / sizeof ( wchar_t ) );
561+ TCHAR dateStr[buffSize ] = {0 };
562+ status = GetDateFormat (LOCALE_INVARIANT, 0 , &systemTime, " yyyy-MM-dd" , dateStr, buffSize );
560563#else
561- wchar_t dateStr[18 ] = {0 };
562- status = GetDateFormatEx (LOCALE_NAME_INVARIANT, 0 , &systemTime, L" yyyy-MM-dd" , dateStr, sizeof (dateStr) / sizeof ( wchar_t ) , NULL );
564+ wchar_t dateStr[buffSize ] = {0 };
565+ status = GetDateFormatEx (LOCALE_NAME_INVARIANT, 0 , &systemTime, L" yyyy-MM-dd" , dateStr, buffSize , NULL );
563566#endif // _WIN32_WINNT < _WIN32_WINNT_VISTA
564567 if (status == 0 )
565568 {
566569 throw utility::details::create_system_error (GetLastError ());
567570 }
568571
569572#if _WIN32_WINNT < _WIN32_WINNT_VISTA
570- TCHAR timeStr[10 ] = {0 };
571- status = GetTimeFormat (LOCALE_INVARIANT, TIME_NOTIMEMARKER | TIME_FORCE24HOURFORMAT, &systemTime, " HH':'mm':'ss" , timeStr, sizeof (timeStr) / sizeof ( wchar_t ) );
573+ TCHAR timeStr[buffSize ] = {0 };
574+ status = GetTimeFormat (LOCALE_INVARIANT, TIME_NOTIMEMARKER | TIME_FORCE24HOURFORMAT, &systemTime, " HH':'mm':'ss" , timeStr, buffSize );
572575#else
573- wchar_t timeStr[10 ] = {0 };
574- status = GetTimeFormatEx (LOCALE_NAME_INVARIANT, TIME_NOTIMEMARKER | TIME_FORCE24HOURFORMAT, &systemTime, L" HH':'mm':'ss" , timeStr, sizeof (timeStr) / sizeof ( wchar_t ) );
576+ wchar_t timeStr[buffSize ] = {0 };
577+ status = GetTimeFormatEx (LOCALE_NAME_INVARIANT, TIME_NOTIMEMARKER | TIME_FORCE24HOURFORMAT, &systemTime, L" HH':'mm':'ss" , timeStr, buffSize );
575578#endif // _WIN32_WINNT < _WIN32_WINNT_VISTA
576579 if (status == 0 )
577580 {
@@ -632,7 +635,7 @@ utility::string_t datetime::to_string(date_format format) const
632635}
633636
634637#ifdef _MS_WINDOWS
635- bool __cdecl datetime::system_type_to_datetime (void * pvsysTime, double seconds, datetime * pdt)
638+ bool __cdecl datetime::system_type_to_datetime (void * pvsysTime, uint64_t seconds, datetime * pdt)
636639{
637640 SYSTEMTIME* psysTime = (SYSTEMTIME*)pvsysTime;
638641 FILETIME fileTime;
@@ -644,8 +647,7 @@ bool __cdecl datetime::system_type_to_datetime(void* pvsysTime, double seconds,
644647 largeInt.HighPart = fileTime.dwHighDateTime ;
645648
646649 // Add hundredths of nanoseconds
647- uint64_t hn = (uint64_t )(seconds * _secondTicks);
648- largeInt.QuadPart += hn;
650+ largeInt.QuadPart += seconds;
649651
650652 *pdt = datetime (largeInt.QuadPart );
651653 return true ;
@@ -676,6 +678,9 @@ uint64_t timeticks_from_second(const utility::string_t& str)
676678// / </summary>
677679datetime __cdecl datetime::from_string (const utility::string_t & dateString, date_format format)
678680{
681+ // avoid floating point math to preserve precision
682+ uint64_t ufrac_second = 0 ;
683+
679684#ifdef _MS_WINDOWS
680685 datetime result;
681686 if ( format == RFC_1123 )
@@ -704,7 +709,7 @@ datetime __cdecl datetime::from_string(const utility::string_t& dateString, date
704709 if (loc != monthnames+12 )
705710 {
706711 sysTime.wMonth = (short ) ((loc - monthnames) + 1 );
707- if (system_type_to_datetime (&sysTime, 0.0 , &result))
712+ if (system_type_to_datetime (&sysTime, ufrac_second , &result))
708713 {
709714 return result;
710715 }
@@ -715,23 +720,33 @@ datetime __cdecl datetime::from_string(const utility::string_t& dateString, date
715720 {
716721 // Unlike FILETIME, SYSTEMTIME does not have enough precision to hold seconds in 100 nanosecond
717722 // increments. Therefore, start with seconds and milliseconds set to 0, then add them separately
718- // from this double:
719- double seconds;
723+
724+ // Try to extract the fractional second from the timestamp
725+ std::wregex r_frac_second (L" (.+)(\\ .\\ d+)(Z$)" );
726+ std::wsmatch m;
727+
728+ std::wstring input (dateString);
729+ if (std::regex_search (dateString, m, r_frac_second))
730+ {
731+ auto frac = m[2 ].str (); // this is the fractional second
732+ ufrac_second = timeticks_from_second (frac);
733+ input = m[1 ].str () + m[3 ].str ();
734+ }
720735
721736 {
722737 SYSTEMTIME sysTime = { 0 };
723- const wchar_t * formatString = L" %4d-%2d-%2dT%2d:%2d:%lfZ " ;
724- auto n = swscanf_s (dateString .c_str (), formatString,
738+ const wchar_t * formatString = L" %4d-%2d-%2dT%2d:%2d:%2dZ " ;
739+ auto n = swscanf_s (input .c_str (), formatString,
725740 &sysTime.wYear ,
726741 &sysTime.wMonth ,
727742 &sysTime.wDay ,
728743 &sysTime.wHour ,
729744 &sysTime.wMinute ,
730- &seconds );
745+ &sysTime. wSecond );
731746
732747 if (n == 3 || n == 6 )
733748 {
734- if (system_type_to_datetime (&sysTime, seconds , &result))
749+ if (system_type_to_datetime (&sysTime, ufrac_second , &result))
735750 {
736751 return result;
737752 }
@@ -741,12 +756,12 @@ datetime __cdecl datetime::from_string(const utility::string_t& dateString, date
741756 SYSTEMTIME sysTime = {0 };
742757 DWORD date = 0 ;
743758
744- const wchar_t * formatString = L" %8dT%2d:%2d:%lfZ " ;
745- auto n = swscanf_s (dateString .c_str (), formatString,
759+ const wchar_t * formatString = L" %8dT%2d:%2d:%2dZ " ;
760+ auto n = swscanf_s (input .c_str (), formatString,
746761 &date,
747762 &sysTime.wHour ,
748763 &sysTime.wMinute ,
749- &seconds );
764+ &sysTime. wSecond );
750765
751766 if (n == 1 || n == 4 )
752767 {
@@ -756,7 +771,7 @@ datetime __cdecl datetime::from_string(const utility::string_t& dateString, date
756771 date /= 100 ;
757772 sysTime.wYear = (WORD)date;
758773
759- if (system_type_to_datetime (&sysTime, seconds , &result))
774+ if (system_type_to_datetime (&sysTime, ufrac_second , &result))
760775 {
761776 return result;
762777 }
@@ -765,19 +780,18 @@ datetime __cdecl datetime::from_string(const utility::string_t& dateString, date
765780 {
766781 SYSTEMTIME sysTime = {0 };
767782 GetSystemTime (&sysTime); // Fill date portion with today's information
768- // Zero out second -- we will use the double for that
769783 sysTime.wSecond = 0 ;
770784 sysTime.wMilliseconds = 0 ;
771785
772- const wchar_t * formatString = L" %2d:%2d:%lfZ " ;
773- auto n = swscanf_s (dateString .c_str (), formatString,
786+ const wchar_t * formatString = L" %2d:%2d:%2dZ " ;
787+ auto n = swscanf_s (input .c_str (), formatString,
774788 &sysTime.wHour ,
775789 &sysTime.wMinute ,
776- &seconds );
790+ &sysTime. wSecond );
777791
778792 if (n == 3 )
779793 {
780- if (system_type_to_datetime (&sysTime, seconds , &result))
794+ if (system_type_to_datetime (&sysTime, ufrac_second , &result))
781795 {
782796 return result;
783797 }
@@ -791,9 +805,6 @@ datetime __cdecl datetime::from_string(const utility::string_t& dateString, date
791805
792806 struct tm output = tm ();
793807
794- // avoid floating point math to preserve precision
795- uint64_t ufrac_second = 0 ;
796-
797808 if ( format == RFC_1123 )
798809 {
799810 strptime (input.data (), " %a, %d %b %Y %H:%M:%S GMT" , &output);
@@ -810,7 +821,7 @@ datetime __cdecl datetime::from_string(const utility::string_t& dateString, date
810821 ufrac_second = timeticks_from_second (frac);
811822 input = m[1 ].str () + m[3 ].str ();
812823 }
813-
824+
814825 auto result = strptime (input.data (), " %Y-%m-%dT%H:%M:%SZ" , &output);
815826
816827 if ( result == nullptr )
0 commit comments