@@ -30,9 +30,13 @@ using namespace Platform;
30
30
using namespace Windows ::Storage::Streams;
31
31
#endif // #if !defined(__cplusplus_winrt)
32
32
33
- #if !defined(_MS_WINDOWS)
33
+ #if defined(_MS_WINDOWS)
34
+ #include < regex>
35
+ #else
34
36
#include < boost/date_time/posix_time/posix_time.hpp>
35
37
#include < boost/date_time/posix_time/posix_time_io.hpp>
38
+ // GCC 4.8 does not support regex, use boost. TODO: switch to std::regex in GCC 4.9
39
+ #include < boost/regex.hpp>
36
40
using namespace boost ::locale::conv;
37
41
#endif
38
42
@@ -472,8 +476,8 @@ datetime datetime::timeval_to_datetime(struct timeval time)
472
476
{
473
477
const uint64_t epoch_offset = 11644473600LL ; // diff between windows and unix epochs (seconds)
474
478
uint64_t result = epoch_offset + time.tv_sec ;
475
- result *= 10000000LL ; // convert to 10e-7
476
- result += time.tv_usec * 10 ; // add microseconds (in 10e-7)
479
+ result *= _secondTicks ; // convert to 10e-7
480
+ result += time.tv_usec ; // add microseconds (in 10e-7)
477
481
return datetime (result);
478
482
}
479
483
#endif
@@ -552,58 +556,133 @@ utility::string_t datetime::to_string(date_format format) const
552
556
}
553
557
else if ( format == ISO_8601 )
554
558
{
559
+ const size_t buffSize = 64 ;
555
560
#if _WIN32_WINNT < _WIN32_WINNT_VISTA
556
- TCHAR dateStr[18 ] = {0 };
557
- 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 );
558
563
#else
559
- wchar_t dateStr[18 ] = {0 };
560
- 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 );
561
566
#endif // _WIN32_WINNT < _WIN32_WINNT_VISTA
562
567
if (status == 0 )
563
568
{
564
569
throw utility::details::create_system_error (GetLastError ());
565
570
}
566
571
567
572
#if _WIN32_WINNT < _WIN32_WINNT_VISTA
568
- TCHAR timeStr[10 ] = {0 };
569
- 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 );
570
575
#else
571
- wchar_t timeStr[10 ] = {0 };
572
- 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 );
573
578
#endif // _WIN32_WINNT < _WIN32_WINNT_VISTA
574
579
if (status == 0 )
575
580
{
576
581
throw utility::details::create_system_error (GetLastError ());
577
582
}
578
583
579
- outStream << dateStr << " T" << timeStr << " Z" ;
584
+ outStream << dateStr << " T" << timeStr;
585
+ uint64_t frac_sec = largeInt.QuadPart % _secondTicks;
586
+ if (frac_sec > 0 )
587
+ {
588
+ // Append fractional second, which is a 7-digit value with no trailing zeros
589
+ // This way, '1200' becomes '00012'
590
+ char buf[9 ] = { 0 };
591
+ sprintf_s (buf, sizeof (buf), " .%07d" , frac_sec);
592
+ // trim trailing zeros
593
+ for (int i = 7 ; buf[i] == ' 0' ; i--) buf[i] = ' \0 ' ;
594
+ outStream << buf;
595
+ }
596
+ outStream << " Z" ;
580
597
}
581
598
582
599
return outStream.str ();
583
600
#else // LINUX
584
601
uint64_t input = m_interval;
585
- input /= 10000000LL ; // convert to seconds
602
+ uint64_t frac_sec = input % _secondTicks;
603
+ input /= _secondTicks; // convert to seconds
586
604
time_t time = (time_t )input - (time_t )11644473600LL ;// diff between windows and unix epochs (seconds)
587
605
588
606
struct tm datetime;
589
607
gmtime_r (&time, &datetime);
590
608
591
- const int max_dt_length = 29 ;
592
- char output[max_dt_length+1 ]; output[max_dt_length] = ' \0 ' ;
593
- strftime (output, max_dt_length+1 ,
594
- format == RFC_1123 ? " %a, %d %b %Y %H:%M:%S GMT" : " %Y-%m-%dT%H:%M:%SZ" ,
595
- &datetime);
609
+ const int max_dt_length = 64 ;
610
+ char output[max_dt_length+1 ] = {0 };
611
+
612
+ if (format != RFC_1123 && frac_sec > 0 )
613
+ {
614
+ // Append fractional second, which is a 7-digit value with no trailing zeros
615
+ // This way, '1200' becomes '00012'
616
+ char buf[9 ] = { 0 };
617
+ snprintf (buf, sizeof (buf), " .%07ld" , (long int )frac_sec);
618
+ // trim trailing zeros
619
+ for (int i = 7 ; buf[i] == ' 0' ; i--) buf[i] = ' \0 ' ;
620
+ // format the datetime into a separate buffer
621
+ char datetime_str[max_dt_length+1 ] = {0 };
622
+ strftime (datetime_str, sizeof (datetime_str), " %Y-%m-%dT%H:%M:%S" , &datetime);
623
+ // now print this buffer into the output buffer
624
+ snprintf (output, sizeof (output), " %s%sZ" , datetime_str, buf);
625
+ }
626
+ else
627
+ {
628
+ strftime (output, sizeof (output),
629
+ format == RFC_1123 ? " %a, %d %b %Y %H:%M:%S GMT" : " %Y-%m-%dT%H:%M:%SZ" ,
630
+ &datetime);
631
+ }
596
632
597
633
return std::string (output);
598
634
#endif
599
635
}
600
636
637
+ #ifdef _MS_WINDOWS
638
+ bool __cdecl datetime::system_type_to_datetime (void * pvsysTime, uint64_t seconds, datetime * pdt)
639
+ {
640
+ SYSTEMTIME* psysTime = (SYSTEMTIME*)pvsysTime;
641
+ FILETIME fileTime;
642
+
643
+ if (SystemTimeToFileTime (psysTime, &fileTime))
644
+ {
645
+ ULARGE_INTEGER largeInt;
646
+ largeInt.LowPart = fileTime.dwLowDateTime ;
647
+ largeInt.HighPart = fileTime.dwHighDateTime ;
648
+
649
+ // Add hundredths of nanoseconds
650
+ largeInt.QuadPart += seconds;
651
+
652
+ *pdt = datetime (largeInt.QuadPart );
653
+ return true ;
654
+ }
655
+ return false ;
656
+ }
657
+ #endif
658
+
659
+ // Take a string that represents a fractional second and return the number of ticks
660
+ // This is equivalent to doing atof on the string and multiplying by 10000000,
661
+ // but does not lose precision
662
+ uint64_t timeticks_from_second (const utility::string_t & str)
663
+ {
664
+ _ASSERTE (str.size ()>1 );
665
+ _ASSERTE (str[0 ]==U (' .' ));
666
+ uint64_t ufrac_second = 0 ;
667
+ for (int i=1 ; i<=7 ; ++i)
668
+ {
669
+ ufrac_second *= 10 ;
670
+ auto add = i < (int )str.size () ? str[i] - U (' 0' ) : 0 ;
671
+ ufrac_second += add;
672
+ }
673
+ return ufrac_second;
674
+ }
675
+
601
676
// / <summary>
602
677
// / Returns a string representation of the datetime. The string is formatted based on RFC 1123 or ISO 8601
603
678
// / </summary>
604
679
datetime __cdecl datetime::from_string (const utility::string_t & dateString, date_format format)
605
680
{
681
+ // avoid floating point math to preserve precision
682
+ uint64_t ufrac_second = 0 ;
683
+
606
684
#ifdef _MS_WINDOWS
685
+ datetime result;
607
686
if ( format == RFC_1123 )
608
687
{
609
688
SYSTEMTIME sysTime = {0 };
@@ -629,27 +708,35 @@ datetime __cdecl datetime::from_string(const utility::string_t& dateString, date
629
708
630
709
if (loc != monthnames+12 )
631
710
{
632
- FILETIME fileTime;
633
711
sysTime.wMonth = (short ) ((loc - monthnames) + 1 );
634
-
635
- if (SystemTimeToFileTime (&sysTime, &fileTime))
712
+ if (system_type_to_datetime (&sysTime, ufrac_second, &result))
636
713
{
637
- ULARGE_INTEGER largeInt;
638
- largeInt.LowPart = fileTime.dwLowDateTime ;
639
- largeInt.HighPart = fileTime.dwHighDateTime ;
640
-
641
- return datetime (largeInt.QuadPart );
714
+ return result;
642
715
}
643
716
}
644
717
}
645
718
}
646
719
else if ( format == ISO_8601 )
647
720
{
721
+ // Unlike FILETIME, SYSTEMTIME does not have enough precision to hold seconds in 100 nanosecond
722
+ // increments. Therefore, start with seconds and milliseconds set to 0, then add them separately
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))
648
730
{
649
- SYSTEMTIME sysTime = {0 };
650
-
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
+ }
735
+
736
+ {
737
+ SYSTEMTIME sysTime = { 0 };
651
738
const wchar_t * formatString = L" %4d-%2d-%2dT%2d:%2d:%2dZ" ;
652
- auto n = swscanf_s (dateString .c_str (), formatString,
739
+ auto n = swscanf_s (input .c_str (), formatString,
653
740
&sysTime.wYear ,
654
741
&sysTime.wMonth ,
655
742
&sysTime.wDay ,
@@ -659,15 +746,9 @@ datetime __cdecl datetime::from_string(const utility::string_t& dateString, date
659
746
660
747
if (n == 3 || n == 6 )
661
748
{
662
- FILETIME fileTime;
663
-
664
- if (SystemTimeToFileTime (&sysTime, &fileTime))
749
+ if (system_type_to_datetime (&sysTime, ufrac_second, &result))
665
750
{
666
- ULARGE_INTEGER largeInt;
667
- largeInt.LowPart = fileTime.dwLowDateTime ;
668
- largeInt.HighPart = fileTime.dwHighDateTime ;
669
-
670
- return datetime (largeInt.QuadPart );
751
+ return result;
671
752
}
672
753
}
673
754
}
@@ -676,53 +757,43 @@ datetime __cdecl datetime::from_string(const utility::string_t& dateString, date
676
757
DWORD date = 0 ;
677
758
678
759
const wchar_t * formatString = L" %8dT%2d:%2d:%2dZ" ;
679
- auto n = swscanf_s (dateString .c_str (), formatString,
760
+ auto n = swscanf_s (input .c_str (), formatString,
680
761
&date,
681
762
&sysTime.wHour ,
682
763
&sysTime.wMinute ,
683
764
&sysTime.wSecond );
684
765
685
766
if (n == 1 || n == 4 )
686
767
{
687
- FILETIME fileTime;
688
-
689
768
sysTime.wDay = date % 100 ;
690
769
date /= 100 ;
691
770
sysTime.wMonth = date % 100 ;
692
771
date /= 100 ;
693
772
sysTime.wYear = (WORD)date;
694
773
695
- if (SystemTimeToFileTime (&sysTime, &fileTime ))
774
+ if (system_type_to_datetime (&sysTime, ufrac_second, &result ))
696
775
{
697
- ULARGE_INTEGER largeInt;
698
- largeInt.LowPart = fileTime.dwLowDateTime ;
699
- largeInt.HighPart = fileTime.dwHighDateTime ;
700
-
701
- return datetime (largeInt.QuadPart );
776
+ return result;
702
777
}
703
778
}
704
779
}
705
780
{
706
781
SYSTEMTIME sysTime = {0 };
707
782
GetSystemTime (&sysTime); // Fill date portion with today's information
783
+ sysTime.wSecond = 0 ;
784
+ sysTime.wMilliseconds = 0 ;
708
785
709
786
const wchar_t * formatString = L" %2d:%2d:%2dZ" ;
710
- auto n = swscanf_s (dateString .c_str (), formatString,
787
+ auto n = swscanf_s (input .c_str (), formatString,
711
788
&sysTime.wHour ,
712
789
&sysTime.wMinute ,
713
790
&sysTime.wSecond );
714
791
715
792
if (n == 3 )
716
793
{
717
- FILETIME fileTime;
718
-
719
- if (SystemTimeToFileTime (&sysTime, &fileTime))
794
+ if (system_type_to_datetime (&sysTime, ufrac_second, &result))
720
795
{
721
- ULARGE_INTEGER largeInt;
722
- largeInt.LowPart = fileTime.dwLowDateTime ;
723
- largeInt.HighPart = fileTime.dwHighDateTime ;
724
-
725
- return datetime (largeInt.QuadPart );
796
+ return result;
726
797
}
727
798
}
728
799
}
@@ -740,6 +811,17 @@ datetime __cdecl datetime::from_string(const utility::string_t& dateString, date
740
811
}
741
812
else
742
813
{
814
+ // Try to extract the fractional second from the timestamp
815
+ boost::regex r_frac_second (" (.+)(\\ .\\ d+)(Z$)" );
816
+ boost::smatch m;
817
+
818
+ if (boost::regex_search (input,m,r_frac_second))
819
+ {
820
+ auto frac = m[2 ].str (); // this is the fractional second
821
+ ufrac_second = timeticks_from_second (frac);
822
+ input = m[1 ].str () + m[3 ].str ();
823
+ }
824
+
743
825
auto result = strptime (input.data (), " %Y-%m-%dT%H:%M:%SZ" , &output);
744
826
745
827
if ( result == nullptr )
@@ -748,6 +830,12 @@ datetime __cdecl datetime::from_string(const utility::string_t& dateString, date
748
830
}
749
831
if ( result == nullptr )
750
832
{
833
+ // Fill the date portion with the epoch,
834
+ // strptime will do the rest
835
+ memset (&output, 0 , sizeof (struct tm ));
836
+ output.tm_year = 70 ;
837
+ output.tm_mon = 1 ;
838
+ output.tm_mday = 1 ;
751
839
result = strptime (input.data (), " %H:%M:%SZ" , &output);
752
840
}
753
841
if ( result == nullptr )
@@ -768,6 +856,7 @@ datetime __cdecl datetime::from_string(const utility::string_t& dateString, date
768
856
769
857
struct timeval tv = timeval ();
770
858
tv.tv_sec = time;
859
+ tv.tv_usec = (__suseconds_t )ufrac_second;
771
860
return timeval_to_datetime (tv);
772
861
#endif
773
862
}
0 commit comments