Skip to content

Commit 5534f25

Browse files
committed
Merge branch 'apple' into development
2 parents 115cff9 + cc7a041 commit 5534f25

File tree

4 files changed

+207
-59
lines changed

4 files changed

+207
-59
lines changed

Release/include/cpprest/asyncrt_utils.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -389,7 +389,10 @@ class datetime
389389
static const interval_type _dayTicks = 24*60*60*_secondTicks;
390390

391391

392-
#ifndef _MS_WINDOWS
392+
#ifdef _MS_WINDOWS
393+
// void* to avoid pulling in windows.h
394+
static _ASYNCRTIMP bool __cdecl datetime::system_type_to_datetime(/*SYSTEMTIME*/ void* psysTime, double seconds, datetime * pdt);
395+
#else
393396
static datetime timeval_to_datetime(struct timeval time);
394397
#endif
395398

Release/src/utilities/asyncrt_utils.cpp

Lines changed: 144 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,13 @@ using namespace Platform;
3030
using 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>
38+
// GCC 4.8 does not support regex, use boost. TODO: switch to std::regex in GCC 4.9
39+
#include <boost/regex.hpp>
3640
using namespace boost::locale::conv;
3741
#endif
3842

@@ -472,8 +476,8 @@ datetime datetime::timeval_to_datetime(struct timeval time)
472476
{
473477
const uint64_t epoch_offset = 11644473600LL; // diff between windows and unix epochs (seconds)
474478
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)
477481
return datetime(result);
478482
}
479483
#endif
@@ -552,58 +556,133 @@ utility::string_t datetime::to_string(date_format format) const
552556
}
553557
else if ( format == ISO_8601 )
554558
{
559+
const size_t buffSize = 64;
555560
#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);
558563
#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);
561566
#endif // _WIN32_WINNT < _WIN32_WINNT_VISTA
562567
if (status == 0)
563568
{
564569
throw utility::details::create_system_error(GetLastError());
565570
}
566571

567572
#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);
570575
#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);
573578
#endif // _WIN32_WINNT < _WIN32_WINNT_VISTA
574579
if (status == 0)
575580
{
576581
throw utility::details::create_system_error(GetLastError());
577582
}
578583

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";
580597
}
581598

582599
return outStream.str();
583600
#else //LINUX
584601
uint64_t input = m_interval;
585-
input /= 10000000LL; // convert to seconds
602+
uint64_t frac_sec = input % _secondTicks;
603+
input /= _secondTicks; // convert to seconds
586604
time_t time = (time_t)input - (time_t)11644473600LL;// diff between windows and unix epochs (seconds)
587605

588606
struct tm datetime;
589607
gmtime_r(&time, &datetime);
590608

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+
}
596632

597633
return std::string(output);
598634
#endif
599635
}
600636

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+
601676
/// <summary>
602677
/// Returns a string representation of the datetime. The string is formatted based on RFC 1123 or ISO 8601
603678
/// </summary>
604679
datetime __cdecl datetime::from_string(const utility::string_t& dateString, date_format format)
605680
{
681+
// avoid floating point math to preserve precision
682+
uint64_t ufrac_second = 0;
683+
606684
#ifdef _MS_WINDOWS
685+
datetime result;
607686
if ( format == RFC_1123 )
608687
{
609688
SYSTEMTIME sysTime = {0};
@@ -629,27 +708,35 @@ datetime __cdecl datetime::from_string(const utility::string_t& dateString, date
629708

630709
if (loc != monthnames+12)
631710
{
632-
FILETIME fileTime;
633711
sysTime.wMonth = (short) ((loc - monthnames) + 1);
634-
635-
if (SystemTimeToFileTime(&sysTime, &fileTime))
712+
if (system_type_to_datetime(&sysTime, ufrac_second, &result))
636713
{
637-
ULARGE_INTEGER largeInt;
638-
largeInt.LowPart = fileTime.dwLowDateTime;
639-
largeInt.HighPart = fileTime.dwHighDateTime;
640-
641-
return datetime(largeInt.QuadPart);
714+
return result;
642715
}
643716
}
644717
}
645718
}
646719
else if ( format == ISO_8601 )
647720
{
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))
648730
{
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 };
651738
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,
653740
&sysTime.wYear,
654741
&sysTime.wMonth,
655742
&sysTime.wDay,
@@ -659,15 +746,9 @@ datetime __cdecl datetime::from_string(const utility::string_t& dateString, date
659746

660747
if (n == 3 || n == 6)
661748
{
662-
FILETIME fileTime;
663-
664-
if (SystemTimeToFileTime(&sysTime, &fileTime))
749+
if (system_type_to_datetime(&sysTime, ufrac_second, &result))
665750
{
666-
ULARGE_INTEGER largeInt;
667-
largeInt.LowPart = fileTime.dwLowDateTime;
668-
largeInt.HighPart = fileTime.dwHighDateTime;
669-
670-
return datetime(largeInt.QuadPart);
751+
return result;
671752
}
672753
}
673754
}
@@ -676,53 +757,43 @@ datetime __cdecl datetime::from_string(const utility::string_t& dateString, date
676757
DWORD date = 0;
677758

678759
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,
680761
&date,
681762
&sysTime.wHour,
682763
&sysTime.wMinute,
683764
&sysTime.wSecond);
684765

685766
if (n == 1 || n == 4)
686767
{
687-
FILETIME fileTime;
688-
689768
sysTime.wDay = date % 100;
690769
date /= 100;
691770
sysTime.wMonth = date % 100;
692771
date /= 100;
693772
sysTime.wYear = (WORD)date;
694773

695-
if (SystemTimeToFileTime(&sysTime, &fileTime))
774+
if (system_type_to_datetime(&sysTime, ufrac_second, &result))
696775
{
697-
ULARGE_INTEGER largeInt;
698-
largeInt.LowPart = fileTime.dwLowDateTime;
699-
largeInt.HighPart = fileTime.dwHighDateTime;
700-
701-
return datetime(largeInt.QuadPart);
776+
return result;
702777
}
703778
}
704779
}
705780
{
706781
SYSTEMTIME sysTime = {0};
707782
GetSystemTime(&sysTime); // Fill date portion with today's information
783+
sysTime.wSecond = 0;
784+
sysTime.wMilliseconds = 0;
708785

709786
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,
711788
&sysTime.wHour,
712789
&sysTime.wMinute,
713790
&sysTime.wSecond);
714791

715792
if (n == 3)
716793
{
717-
FILETIME fileTime;
718-
719-
if (SystemTimeToFileTime(&sysTime, &fileTime))
794+
if (system_type_to_datetime(&sysTime, ufrac_second, &result))
720795
{
721-
ULARGE_INTEGER largeInt;
722-
largeInt.LowPart = fileTime.dwLowDateTime;
723-
largeInt.HighPart = fileTime.dwHighDateTime;
724-
725-
return datetime(largeInt.QuadPart);
796+
return result;
726797
}
727798
}
728799
}
@@ -740,6 +811,17 @@ datetime __cdecl datetime::from_string(const utility::string_t& dateString, date
740811
}
741812
else
742813
{
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+
743825
auto result = strptime(input.data(), "%Y-%m-%dT%H:%M:%SZ", &output);
744826

745827
if ( result == nullptr )
@@ -748,6 +830,12 @@ datetime __cdecl datetime::from_string(const utility::string_t& dateString, date
748830
}
749831
if ( result == nullptr )
750832
{
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;
751839
result = strptime(input.data(), "%H:%M:%SZ", &output);
752840
}
753841
if ( result == nullptr )
@@ -768,6 +856,7 @@ datetime __cdecl datetime::from_string(const utility::string_t& dateString, date
768856

769857
struct timeval tv = timeval();
770858
tv.tv_sec = time;
859+
tv.tv_usec = (__suseconds_t)ufrac_second;
771860
return timeval_to_datetime(tv);
772861
#endif
773862
}

Release/tests/Functional/utils/base64.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -245,12 +245,12 @@ TEST(bad_decode, "Ignore:Linux", "glib does not reject any of these inputs")
245245

246246
TEST(large_string)
247247
{
248-
size_t size = 128*1024;
248+
const size_t size = 64*1024;
249249

250-
std::vector<unsigned char> data;
250+
std::vector<unsigned char> data(size);
251251
for (auto i = 0u; i < size; ++i)
252252
{
253-
data.push_back((unsigned char)(rand() & 0xFF));
253+
data[i] = (unsigned char)(rand() & 0xFF);
254254
}
255255

256256
auto string = utility::conversions::to_base64(data);

0 commit comments

Comments
 (0)