@@ -33,6 +33,8 @@ using namespace Windows::Storage::Streams;
33
33
#if !defined(_MS_WINDOWS)
34
34
#include < boost/date_time/posix_time/posix_time.hpp>
35
35
#include < boost/date_time/posix_time/posix_time_io.hpp>
36
+ // GCC 4.8 does not support regex, use boost. TODO: switch to std::regex in GCC 4.9
37
+ #include < boost/regex.hpp>
36
38
using namespace boost ::locale::conv;
37
39
#endif
38
40
@@ -472,8 +474,8 @@ datetime datetime::timeval_to_datetime(struct timeval time)
472
474
{
473
475
const uint64_t epoch_offset = 11644473600LL ; // diff between windows and unix epochs (seconds)
474
476
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)
477
+ result *= _secondTicks ; // convert to 10e-7
478
+ result += time.tv_usec ; // add microseconds (in 10e-7)
477
479
return datetime (result);
478
480
}
479
481
#endif
@@ -594,17 +596,36 @@ utility::string_t datetime::to_string(date_format format) const
594
596
return outStream.str ();
595
597
#else // LINUX
596
598
uint64_t input = m_interval;
597
- input /= 10000000LL ; // convert to seconds
599
+ uint64_t frac_sec = input % _secondTicks;
600
+ input /= _secondTicks; // convert to seconds
598
601
time_t time = (time_t )input - (time_t )11644473600LL ;// diff between windows and unix epochs (seconds)
599
602
600
603
struct tm datetime;
601
604
gmtime_r (&time, &datetime);
602
605
603
- const int max_dt_length = 29 ;
604
- char output[max_dt_length+1 ]; output[max_dt_length] = ' \0 ' ;
605
- strftime (output, max_dt_length+1 ,
606
- format == RFC_1123 ? " %a, %d %b %Y %H:%M:%S GMT" : " %Y-%m-%dT%H:%M:%SZ" ,
607
- &datetime);
606
+ const int max_dt_length = 64 ;
607
+ char output[max_dt_length+1 ] = {0 };
608
+
609
+ if (format != RFC_1123 && frac_sec > 0 )
610
+ {
611
+ // Append fractional second, which is a 7-digit value with no trailing zeros
612
+ // This way, '1200' becomes '00012'
613
+ char buf[9 ] = { 0 };
614
+ snprintf (buf, sizeof (buf), " .%07ld" , (long int )frac_sec);
615
+ // trim trailing zeros
616
+ for (int i = 7 ; buf[i] == ' 0' ; i--) buf[i] = ' \0 ' ;
617
+ // format the datetime into a separate buffer
618
+ char datetime_str[max_dt_length+1 ] = {0 };
619
+ strftime (datetime_str, sizeof (datetime_str), " %Y-%m-%dT%H:%M:%S" , &datetime);
620
+ // now print this buffer into the output buffer
621
+ snprintf (output, sizeof (output), " %s%sZ" , datetime_str, buf);
622
+ }
623
+ else
624
+ {
625
+ strftime (output, sizeof (output),
626
+ format == RFC_1123 ? " %a, %d %b %Y %H:%M:%S GMT" : " %Y-%m-%dT%H:%M:%SZ" ,
627
+ &datetime);
628
+ }
608
629
609
630
return std::string (output);
610
631
#endif
@@ -633,6 +654,23 @@ bool __cdecl datetime::system_type_to_datetime(void* pvsysTime, double seconds,
633
654
}
634
655
#endif
635
656
657
+ // Take a string that represents a fractional second and return the number of ticks
658
+ // This is equivalent to doing atof on the string and multiplying by 10000000,
659
+ // but does not lose precision
660
+ uint64_t timeticks_from_second (const utility::string_t & str)
661
+ {
662
+ _ASSERTE (str.size ()>1 );
663
+ _ASSERTE (str[0 ]==U (' .' ));
664
+ uint64_t ufrac_second = 0 ;
665
+ for (int i=1 ; i<=7 ; ++i)
666
+ {
667
+ ufrac_second *= 10 ;
668
+ auto add = i < (int )str.size () ? str[i] - U (' 0' ) : 0 ;
669
+ ufrac_second += add;
670
+ }
671
+ return ufrac_second;
672
+ }
673
+
636
674
// / <summary>
637
675
// / Returns a string representation of the datetime. The string is formatted based on RFC 1123 or ISO 8601
638
676
// / </summary>
@@ -753,12 +791,26 @@ datetime __cdecl datetime::from_string(const utility::string_t& dateString, date
753
791
754
792
struct tm output = tm ();
755
793
794
+ // avoid floating point math to preserve precision
795
+ uint64_t ufrac_second = 0 ;
796
+
756
797
if ( format == RFC_1123 )
757
798
{
758
799
strptime (input.data (), " %a, %d %b %Y %H:%M:%S GMT" , &output);
759
800
}
760
801
else
761
802
{
803
+ // Try to extract the fractional second from the timestamp
804
+ boost::regex r_frac_second (" (.+)(\\ .\\ d+)(Z$)" );
805
+ boost::smatch m;
806
+
807
+ if (boost::regex_search (input,m,r_frac_second))
808
+ {
809
+ auto frac = m[2 ].str (); // this is the fractional second
810
+ ufrac_second = timeticks_from_second (frac);
811
+ input = m[1 ].str () + m[3 ].str ();
812
+ }
813
+
762
814
auto result = strptime (input.data (), " %Y-%m-%dT%H:%M:%SZ" , &output);
763
815
764
816
if ( result == nullptr )
@@ -767,6 +819,12 @@ datetime __cdecl datetime::from_string(const utility::string_t& dateString, date
767
819
}
768
820
if ( result == nullptr )
769
821
{
822
+ // Fill the date portion with the epoch,
823
+ // strptime will do the rest
824
+ memset (&output, 0 , sizeof (struct tm ));
825
+ output.tm_year = 70 ;
826
+ output.tm_mon = 1 ;
827
+ output.tm_mday = 1 ;
770
828
result = strptime (input.data (), " %H:%M:%SZ" , &output);
771
829
}
772
830
if ( result == nullptr )
@@ -787,6 +845,7 @@ datetime __cdecl datetime::from_string(const utility::string_t& dateString, date
787
845
788
846
struct timeval tv = timeval ();
789
847
tv.tv_sec = time;
848
+ tv.tv_usec = (__suseconds_t )ufrac_second;
790
849
return timeval_to_datetime (tv);
791
850
#endif
792
851
}
0 commit comments