Something went wrong. Try again.
Reactos
Something went wrong. Try again.
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208//// wcsftime.cpp//// Copyright (c) Microsoft Corporation. All rights reserved.//// The wcsftime family of functions, which format time data into a wide string,// and related functionality.//#include <corecrt_internal_time.h>#include <stdlib.h>#include <locale.h>
//-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+//// Day and Month Name and Time Locale Information Fetching Functions////-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+extern "C" wchar_t* __cdecl _W_Getdays(){ _LocaleUpdate locale_update(nullptr); __crt_lc_time_data const* const time_data = locale_update.GetLocaleT()->locinfo->lc_time_curr;
size_t length = 0; for (size_t n = 0; n < 7; ++n) { length += wcslen(time_data->_W_wday_abbr[n]) + wcslen(time_data->_W_wday[n]) + 2; }
__crt_unique_heap_ptr<wchar_t> buffer(_malloc_crt_t(wchar_t, length + 1)); if (buffer.get() == nullptr) return nullptr;
wchar_t* it = buffer.get(); for (size_t n = 0; n < 7; ++n) { *it++ = L':'; _ERRCHECK(wcscpy_s(it, (length + 1) - (it - buffer.get()), time_data->_W_wday_abbr[n])); it += wcslen(it); *it++ = L':'; _ERRCHECK(wcscpy_s(it, (length + 1) - (it - buffer.get()), time_data->_W_wday[n])); it += wcslen(it); } *it++ = L'\0';
return buffer.detach();}
extern "C" wchar_t* __cdecl _W_Getmonths(){ _LocaleUpdate locale_update(nullptr); __crt_lc_time_data const* const time_data = locale_update.GetLocaleT()->locinfo->lc_time_curr;
size_t length = 0; for (size_t n = 0; n < 12; ++n) { length += wcslen(time_data->_W_month_abbr[n]) + wcslen(time_data->_W_month[n]) + 2; }
__crt_unique_heap_ptr<wchar_t> buffer(_malloc_crt_t(wchar_t, length + 1)); if (buffer.get() == nullptr) return nullptr;
wchar_t* it = buffer.get(); for (size_t n = 0; n < 12; ++n) { *it++ = L':'; _ERRCHECK(wcscpy_s(it, (length + 1) - (it - buffer.get()), time_data->_W_month_abbr[n])); it += wcslen(it); *it++ = L':'; _ERRCHECK(wcscpy_s(it, (length + 1) - (it - buffer.get()), time_data->_W_month[n])); it += wcslen(it); } *it++ = L'\0';
return buffer.detach();}
extern "C" void* __cdecl _W_Gettnames(){ _LocaleUpdate locale_update(nullptr); __crt_lc_time_data const* const src = locale_update.GetLocaleT()->locinfo->lc_time_curr;
#define PROCESS_STRING(STR, CHAR, CPY, LEN) \ while (bytes % sizeof(CHAR) != 0) \ { \ ++bytes; \ } \ if (phase == 1) \ { \ dest->STR = ((CHAR *) dest) + bytes / sizeof(CHAR); \ _ERRCHECK(CPY(dest->STR, (total_bytes - bytes) / sizeof(CHAR), src->STR)); \ } \ bytes += (LEN(src->STR) + 1) * sizeof(CHAR);
#define PROCESS_NARROW_STRING(STR) \ PROCESS_STRING(STR, char, strcpy_s, strlen)
#define PROCESS_WIDE_STRING(STR) \ PROCESS_STRING(STR, wchar_t, wcscpy_s, wcslen)
#define PROCESS_NARROW_ARRAY(ARR) \ for (size_t idx = 0; idx < _countof(src->ARR); ++idx) \ { \ PROCESS_NARROW_STRING(ARR[idx]) \ }
#define PROCESS_WIDE_ARRAY(ARR) \ for (size_t idx = 0; idx < _countof(src->ARR); ++idx) \ { \ PROCESS_WIDE_STRING(ARR[idx]) \ }
size_t total_bytes = 0; size_t bytes = sizeof(__crt_lc_time_data);
__crt_lc_time_data* dest = nullptr; for (int phase = 0; phase < 2; ++phase) { if (phase == 1) { dest = static_cast<__crt_lc_time_data*>(_malloc_crt(bytes));
if (!dest) { return nullptr; }
memset(dest, 0, bytes);
total_bytes = bytes;
bytes = sizeof(__crt_lc_time_data); }
PROCESS_NARROW_ARRAY(wday_abbr) PROCESS_NARROW_ARRAY(wday) PROCESS_NARROW_ARRAY(month_abbr) PROCESS_NARROW_ARRAY(month) PROCESS_NARROW_ARRAY(ampm) PROCESS_NARROW_STRING(ww_sdatefmt) PROCESS_NARROW_STRING(ww_ldatefmt) PROCESS_NARROW_STRING(ww_timefmt)
if (phase == 1) { dest->ww_caltype = src->ww_caltype; dest->refcount = 0; }
PROCESS_WIDE_ARRAY(_W_wday_abbr) PROCESS_WIDE_ARRAY(_W_wday) PROCESS_WIDE_ARRAY(_W_month_abbr) PROCESS_WIDE_ARRAY(_W_month) PROCESS_WIDE_ARRAY(_W_ampm) PROCESS_WIDE_STRING(_W_ww_sdatefmt) PROCESS_WIDE_STRING(_W_ww_ldatefmt) PROCESS_WIDE_STRING(_W_ww_timefmt) PROCESS_WIDE_STRING(_W_ww_locale_name) }
return dest;}
//-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+//// Local Functions Used In Time String Formatting////-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+// Values for __crt_lc_time_data ww_* fields for store_winword:#define WW_SDATEFMT 0#define WW_LDATEFMT 1#define WW_TIMEFMT 2
// Note: annotation does not account for fact < *count bytes may be written if null terminator hit#define _CrtWcstime_Writes_and_advances_ptr_(count) \ _Outptr_result_buffer_(count)
// Copies the supplied time string 'in' into the output buffer 'out' until either// (a) the end of the time string is reached or (b) '*count' becomes zero (and we// run out of buffer space). The '*out' pointer is updated to point to the next// character in the buffer (one-past-the-end of the insertion), and the '*count'// value is updated to reflect the number of characters written.static void __cdecl store_string( _In_reads_or_z_(*count) wchar_t const* in, _CrtWcstime_Writes_and_advances_ptr_(*count) wchar_t** const out, _Inout_ size_t* const count ) throw(){ while (*count != 0 && *in != L'\0') { *(*out)++ = *in++; --*count; }}
// Converts a positive integer ('value') into a string and stores it in the// 'output' buffer. It stops writing when either (a) the full value has been// printed, or (b) '*count' becomes zero (and we run out of buffer space). The// '*out' pointer is updated to point to the next character in the buffer (one-// past-the-end of the insertion), and the '*count' value is updated to reflect// the number of characters written.static void __cdecl store_number_without_lead_zeroes( int value, _CrtWcstime_Writes_and_advances_ptr_(*count) wchar_t** const out, _Inout_ size_t* const count ) throw(){ // Put the digits in the buffer in reverse order: wchar_t* out_it = *out; if (*count > 1) { do { *out_it++ = static_cast<wchar_t>(value % 10 + L'0');
value /= 10; --*count; } while (value > 0 && *count > 1); } else { // Indicate buffer too small. *out -= *count; *count = 0; return; }
wchar_t* left = *out; wchar_t* right = out_it - 1;
// Update the output iterator to point to the next space: *out = out_it;
// Reverse the buffer: while (left < right) { wchar_t const x = *right; *right-- = *left; *left++ = x; }}
// Converts a positive integer ('value') into a string and stores it in the// 'output' buffer. Both '*out' and '*count' are updated to reflect the// write.static void __cdecl store_number( int value, int digits, _CrtWcstime_Writes_and_advances_ptr_(*count) wchar_t** const out, _Inout_ size_t* const count, wchar_t const pad_character ) throw(){ if (pad_character == '\0') { store_number_without_lead_zeroes(value, out, count); return; }
if (static_cast<size_t>(digits) < *count) { int temp = 0; for (digits--; digits + 1 != 0; --digits) { if (value != 0) { (*out)[digits] = static_cast<wchar_t>(L'0' + value % 10); } else { (*out)[digits] = pad_character; }
value /= 10; temp++; }
*out += temp; *count -= temp; } else { *count = 0; }}
//-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+//// Local Functions Used for ISO Week-Based Year Computations////-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+enum{ sunday = 0, monday = 1, tuesday = 2, wednesday = 3, thursday = 4, friday = 5, saturday = 6};
static int compute_week_of_year(int const wstart, int const wday, int const yday) throw(){ int const adjusted_wday{(wday + 7 - wstart) % 7}; return (yday + 7 - adjusted_wday) / 7;}
static int __cdecl compute_iso_week_internal(int year, int wday, int yday) throw(){ int const week_number{compute_week_of_year(monday, wday, yday)}; bool const is_leap_year{__crt_time_is_leap_year(year)};
int const yunleap{yday - is_leap_year}; int const jan1{(371 - yday + wday) % 7}; int const dec32{(jan1 + is_leap_year + 365) % 7};
if ((364 <= yunleap && dec32 == tuesday ) || (363 <= yunleap && dec32 == wednesday) || (362 <= yunleap && dec32 == thursday )) { return -1; // Push into the next year } else if (jan1 == tuesday || jan1 == wednesday || jan1 == thursday) { return week_number + 1; }
return week_number;}
static int __cdecl compute_iso_week(int const year, int const wday, int const yday) throw(){ int const week_number{compute_iso_week_internal(year, wday, yday)};
if (week_number == 0) return compute_iso_week_internal(year - 1, wday + 7 - yday, __crt_time_is_leap_year(year - 1) ? 366 : 365);
if (0 < week_number) return week_number;
return 1;}
static int __cdecl compute_iso_year(int const year, int const wday, int const yday) throw(){ int const week_number{compute_iso_week_internal(year, wday, yday)};
if (week_number == 0) return year - 1;
if (0 < week_number) return year;
return year + 1;}
// store_winword and expand_time are mutually recursive_Success_(return)static bool __cdecl expand_time( _locale_t locale, wchar_t specifier, tm const* tmptr, _CrtWcstime_Writes_and_advances_ptr_(*count) wchar_t** out, _Inout_ size_t* count, __crt_lc_time_data const* lc_time, bool alternate_form ) throw();
// Formats the date and time in the supplied WinWord format and stores the// formatted result in the supplied buffer. For simple localized Gregorian// calendars (calendar type 1), the WinWord format is converted token-by-token// to wcsftime conversion specifiers. expand_time is then called to do the// heavy lifting. For other calendar types, the Windows APIs GetDateFormatEx// and GetTimeFormatEx are instead used to do all the formatting, so this// function does not need to know about era and period strings, year offsets,// etc. Returns true on success; false on failure._Success_(return)static bool __cdecl store_winword( _locale_t const locale, int const field_code, tm const* const tmptr, _CrtWcstime_Writes_and_advances_ptr_(*count) wchar_t** const out, _Inout_ size_t* const count, __crt_lc_time_data const* const lc_time ) throw(){ wchar_t const* format; switch (field_code) { case WW_SDATEFMT: format = lc_time->_W_ww_sdatefmt; break;
case WW_LDATEFMT: format = lc_time->_W_ww_ldatefmt; break;
case WW_TIMEFMT: default: format = lc_time->_W_ww_timefmt; break; }
if (lc_time->ww_caltype != 1) { // We have something other than the basic Gregorian calendar bool const is_time_format = field_code == WW_TIMEFMT;
// We leave the verification of the SYSTEMTIME up to the Windows API // that we call; if one of those functions returns zero to indicate // failure, we fall through and call expand_time() again. SYSTEMTIME system_time; system_time.wYear = static_cast<WORD>(tmptr->tm_year + 1900); system_time.wMonth = static_cast<WORD>(tmptr->tm_mon + 1); system_time.wDay = static_cast<WORD>(tmptr->tm_mday); system_time.wHour = static_cast<WORD>(tmptr->tm_hour); system_time.wMinute = static_cast<WORD>(tmptr->tm_min); system_time.wSecond = static_cast<WORD>(tmptr->tm_sec); system_time.wMilliseconds = 0;
// Find buffer size required: int cch; if (is_time_format) cch = __acrt_GetTimeFormatEx(lc_time->_W_ww_locale_name, 0, &system_time, format, nullptr, 0); else cch = __acrt_GetDateFormatEx(lc_time->_W_ww_locale_name, 0, &system_time, format, nullptr, 0, nullptr);
if (cch != 0) { __crt_scoped_stack_ptr<wchar_t> const buffer(_malloca_crt_t(wchar_t, cch)); if (buffer.get() != nullptr) { // Do actual date/time formatting: if (is_time_format) cch = __acrt_GetTimeFormatEx(lc_time->_W_ww_locale_name, 0, &system_time, format, buffer.get(), cch); else cch = __acrt_GetDateFormatEx(lc_time->_W_ww_locale_name, 0, &system_time, format, buffer.get(), cch, nullptr);
// Copy to output buffer: wchar_t const* buffer_it = buffer.get(); while (--cch > 0 && *count > 0) { *(*out)++ = *buffer_it++; (*count)--; }
return true; } }
// If an error occurs, just fall through to localized Gregorian... }
while (*format && *count != 0) { wchar_t specifier = 0; bool no_lead_zeros = false;
// Count the number of repetitions of this character int repeat = 0; wchar_t const* p = format; for (; *p++ == *format; ++repeat); // Leave p pointing to the beginning of the next token p--;
// Switch on ASCII format character and determine specifier: switch (*format) { case L'M': { switch (repeat) { case 1: no_lead_zeros = true; // fall through case 2: specifier = L'm'; break; case 3: specifier = L'b'; break; case 4: specifier = L'B'; break; } break; }
case L'd': { switch (repeat) { case 1: no_lead_zeros = true; // fall through case 2: specifier = L'd'; break; case 3: specifier = L'a'; break; case 4: specifier = L'A'; break; } break; }
case L'y': { switch (repeat) { case 2: specifier = L'y'; break; case 4: specifier = L'Y'; break; } break; }
case L'h': { switch (repeat) { case 1: no_lead_zeros = true; // fall through case 2: specifier = L'I'; break; } break; }
case L'H': { switch (repeat) { case 1: no_lead_zeros = true; // fall through case 2: specifier = L'H'; break; } break; }
case L'm': { switch (repeat) { case 1: no_lead_zeros = true; // fall through case 2: specifier = L'M'; break; } break; }
case L's': // for compatibility; not strictly WinWord { switch (repeat) { case 1: no_lead_zeros = true; // fall through case 2: specifier = L'S'; break; } break; }
case L'A': case L'a': { if (!_wcsicmp(format, L"am/pm")) { p = format + 5; } else if (!_wcsicmp(format, L"a/p")) { p = format + 3; }
specifier = L'p'; break; }
case L't': // t or tt time marker suffix { wchar_t* ampmstr = tmptr->tm_hour <= 11 ? lc_time->_W_ampm[0] : lc_time->_W_ampm[1];
if (repeat == 1 && *count > 0) { *(*out)++ = *ampmstr++; (*count)--; } else { while (*ampmstr != 0 && *count > 0) { *(*out)++ = *ampmstr++; --*count; } } format = p; continue; }
case L'\'': // literal string { if (repeat % 2 == 0) // even number { format += repeat; } else // odd number { format += repeat; while (*format && *count != 0) { if (*format == L'\'') { format++; break; }
*(*out)++ = *format++; --*count; } }
continue; }
default: // non-control char, print it { break; } }
// expand specifier, or copy literal if specifier not found if (specifier) { _VALIDATE_RETURN_NOEXC(expand_time(locale, specifier, tmptr, out, count, lc_time, no_lead_zeros), EINVAL, false);
format = p; // bump format up to the next token } else { *(*out)++ = *format++; --*count; } }
return true;}
// Expands the conversion specifier using the time struct and stores the result// into the supplied buffer. The expansion is locale-dependent. Returns true// on success; false on failure.static bool __cdecl expand_time( _locale_t const locale, wchar_t const specifier, tm const* const timeptr, wchar_t** const string, size_t* const left, __crt_lc_time_data const* const lc_time, bool const alternate_form ) throw(){ switch (specifier) { case L'a': // abbreviated weekday name { _VALIDATE_RETURN(timeptr->tm_wday >= 0 && timeptr->tm_wday <= 6, EINVAL, false); store_string(lc_time->_W_wday_abbr[timeptr->tm_wday], string, left); return true; }
case L'A': // full weekday name { _VALIDATE_RETURN(timeptr->tm_wday >= 0 && timeptr->tm_wday <= 6, EINVAL, false); store_string(lc_time->_W_wday[timeptr->tm_wday], string, left); return true; }
case L'b': // abbreviated month name { _VALIDATE_RETURN(timeptr->tm_mon >= 0 && timeptr->tm_mon <= 11, EINVAL, false); store_string(lc_time->_W_month_abbr[timeptr->tm_mon], string, left); return true; }
case L'B': // full month name { _VALIDATE_RETURN(timeptr->tm_mon >= 0 && timeptr->tm_mon <= 11, EINVAL, false); store_string(lc_time->_W_month[timeptr->tm_mon], string, left); return true; }
case L'c': // appropriate date and time representation { // In the C locale, %c is equivalent to "%a %b %e %T %Y". This format // is not achievable using the Windows API date and time format APIs // (it's hard to interleave date and time together, and there's no way // to format %e). Therefore, we special case this specifier for the C // locale. if (lc_time == &__lc_time_c && !alternate_form) { _VALIDATE_RETURN_NOEXC(expand_time(locale, L'a', timeptr, string, left, lc_time, false), EINVAL, false); store_string(L" ", string, left); _VALIDATE_RETURN_NOEXC(expand_time(locale, L'b', timeptr, string, left, lc_time, false), EINVAL, false); store_string(L" ", string, left); _VALIDATE_RETURN_NOEXC(expand_time(locale, L'e', timeptr, string, left, lc_time, false), EINVAL, false); store_string(L" ", string, left); _VALIDATE_RETURN_NOEXC(expand_time(locale, L'T', timeptr, string, left, lc_time, false), EINVAL, false); store_string(L" ", string, left); _VALIDATE_RETURN_NOEXC(expand_time(locale, L'Y', timeptr, string, left, lc_time, false), EINVAL, false); } // Otherwise, if we're not in the C locale, use the locale-provided // format strings: else { int const field_code = alternate_form ? WW_LDATEFMT : WW_SDATEFMT;
_VALIDATE_RETURN_NOEXC(store_winword(locale, field_code, timeptr, string, left, lc_time), EINVAL, false); store_string(L" ", string, left); _VALIDATE_RETURN_NOEXC(store_winword(locale, WW_TIMEFMT, timeptr, string, left, lc_time), EINVAL, false); }
return true; }
case L'C': // century in decimal (00-99) { _VALIDATE_RETURN(timeptr->tm_year >= -1900 && timeptr->tm_year <= 8099, EINVAL, false); store_number(__crt_get_century(timeptr->tm_year), 2, string, left, alternate_form ? '\0' : '0'); return true; }
case L'd': // day of the month in decimal (01-31) { _VALIDATE_RETURN(timeptr->tm_mday >= 1 && timeptr->tm_mday <= 31, EINVAL, false); store_number(timeptr->tm_mday, 2, string, left, alternate_form ? '\0' : '0'); return true; }
case L'D': // equivalent to "%m/%d/%y" { _VALIDATE_RETURN_NOEXC(expand_time(locale, L'm', timeptr, string, left, lc_time, alternate_form), EINVAL, false); store_string(L"/", string, left); _VALIDATE_RETURN_NOEXC(expand_time(locale, L'd', timeptr, string, left, lc_time, alternate_form), EINVAL, false); store_string(L"/", string, left); _VALIDATE_RETURN_NOEXC(expand_time(locale, L'y', timeptr, string, left, lc_time, alternate_form), EINVAL, false); return true; }
case L'e': // day of month as a decimal number (1-31); space padded: { _VALIDATE_RETURN(timeptr->tm_mday >= 1 && timeptr->tm_mday <= 31, EINVAL, false); store_number(timeptr->tm_mday, 2, string, left, alternate_form ? '\0' : ' '); return true; }
case L'F': // equivalent to "%Y-%m-%d" (ISO 8601): { _VALIDATE_RETURN_NOEXC(expand_time(locale, L'Y', timeptr, string, left, lc_time, alternate_form), EINVAL, false); store_string(L"-", string, left); _VALIDATE_RETURN_NOEXC(expand_time(locale, L'm', timeptr, string, left, lc_time, alternate_form), EINVAL, false); store_string(L"-", string, left); _VALIDATE_RETURN_NOEXC(expand_time(locale, L'd', timeptr, string, left, lc_time, alternate_form), EINVAL, false); return true; }
case L'g': // last two digits of the week-based year: { _VALIDATE_RETURN(timeptr->tm_year >= -1900 && timeptr->tm_year <= 8099, EINVAL, false); int const iso_year{compute_iso_year(timeptr->tm_year, timeptr->tm_wday, timeptr->tm_yday) + 1900}; store_number(iso_year % 100, 2, string, left, '0'); return true; }
case L'G': // week-based year: { _VALIDATE_RETURN(timeptr->tm_year >= -1900 && timeptr->tm_year <= 8099, EINVAL, false); int const iso_year{compute_iso_year(timeptr->tm_year, timeptr->tm_wday, timeptr->tm_yday) + 1900}; store_number(iso_year, 4, string, left, '0'); return true; }
case L'h': // equivalent to "%b": { return expand_time(locale, L'b', timeptr, string, left, lc_time, alternate_form); }
case L'H': // 24-hour decimal (00-23) { _VALIDATE_RETURN(timeptr->tm_hour >= 0 && timeptr->tm_hour <= 23, EINVAL, false); store_number(timeptr->tm_hour, 2, string, left, alternate_form ? '\0' : '0'); return true; }
case L'I': // 12-hour decimal (01-12) { _VALIDATE_RETURN(timeptr->tm_hour >= 0 && timeptr->tm_hour <= 23, EINVAL, false); unsigned hour = timeptr->tm_hour % 12; if (hour == 0) hour = 12;
store_number(hour, 2, string, left, alternate_form ? '\0' : '0'); return true; }
case L'j': // yday in decimal (001-366) { _VALIDATE_RETURN(timeptr->tm_yday >= 0 && timeptr->tm_yday <= 365, EINVAL, false); store_number(timeptr->tm_yday + 1, 3, string, left, alternate_form ? '\0' : '0'); return true; }
case L'm': // month in decimal (01-12) { _VALIDATE_RETURN(timeptr->tm_mon >= 0 && timeptr->tm_mon <= 11, EINVAL, false); store_number(timeptr->tm_mon + 1, 2, string, left, alternate_form ? '\0' : '0'); return true; }
case L'M': // minute in decimal (00-59) { _VALIDATE_RETURN(timeptr->tm_min >= 0 && timeptr->tm_min <= 59, EINVAL, false); store_number(timeptr->tm_min, 2, string, left, alternate_form ? '\0' : '0'); return true; }
case L'n': // newline character { store_string(L"\n", string, left); return true; }
case L'p': // AM/PM designation { _VALIDATE_RETURN(timeptr->tm_hour >= 0 && timeptr->tm_hour <= 23, EINVAL, false); wchar_t const* const ampm_string = timeptr->tm_hour <= 11 ? lc_time->_W_ampm[0] : lc_time->_W_ampm[1];
store_string(ampm_string, string, left); return true; }
case L'r': // Locale-specific 12-hour clock time { // In the C locale, %r is equivalent to "%I:%M:%S %p". This is the only // locale in which we guarantee that %r is a 12-hour time; in all other // locales we only have one time format which may or may not be a 12-hour // format. if (lc_time == &__lc_time_c) { _VALIDATE_RETURN_NOEXC(expand_time(locale, 'I', timeptr, string, left, lc_time, alternate_form), EINVAL, false); store_string(L":", string, left); _VALIDATE_RETURN_NOEXC(expand_time(locale, 'M', timeptr, string, left, lc_time, alternate_form), EINVAL, false); store_string(L":", string, left); _VALIDATE_RETURN_NOEXC(expand_time(locale, 'S', timeptr, string, left, lc_time, alternate_form), EINVAL, false); store_string(L" ", string, left); _VALIDATE_RETURN_NOEXC(expand_time(locale, 'p', timeptr, string, left, lc_time, alternate_form), EINVAL, false); } else { _VALIDATE_RETURN_NOEXC(expand_time(locale, 'X', timeptr, string, left, lc_time, alternate_form), EINVAL, false); }
return true; }
case L'R': // Equivalent to "%H:%M" { _VALIDATE_RETURN_NOEXC(expand_time(locale, 'H', timeptr, string, left, lc_time, alternate_form), EINVAL, false); store_string(L":", string, left); _VALIDATE_RETURN_NOEXC(expand_time(locale, 'M', timeptr, string, left, lc_time, alternate_form), EINVAL, false); return true; }
case L'S': // seconds in decimal (00-60) allowing for a leap second { _VALIDATE_RETURN(timeptr->tm_sec >= 0 && timeptr->tm_sec <= 60, EINVAL, false); store_number(timeptr->tm_sec, 2, string, left, alternate_form ? '\0' : '0'); return true; }
case L't': // tab character { store_string(L"\t", string, left); return true; }
case L'T': // Equivalent to "%H:%M:%S" (ISO 8601) { _VALIDATE_RETURN_NOEXC(expand_time(locale, 'H', timeptr, string, left, lc_time, alternate_form), EINVAL, false); store_string(L":", string, left); _VALIDATE_RETURN_NOEXC(expand_time(locale, 'M', timeptr, string, left, lc_time, alternate_form), EINVAL, false); store_string(L":", string, left); _VALIDATE_RETURN_NOEXC(expand_time(locale, 'S', timeptr, string, left, lc_time, alternate_form), EINVAL, false); return true; }
case L'u': // week day in decimal (1-7) case L'w': // week day in decimal (0-6) { _VALIDATE_RETURN(timeptr->tm_wday >= 0 && timeptr->tm_wday <= 6, EINVAL, false);
int const weekday_number = timeptr->tm_wday == 0 && specifier == L'u' ? 7 : timeptr->tm_wday;
store_number(weekday_number, 1, string, left, alternate_form ? '\0' : '0'); return true; }
case L'U': // sunday week number (00-53) case L'W': // monday week number (00-53) { _VALIDATE_RETURN(timeptr->tm_wday >= 0 && timeptr->tm_wday <= 6, EINVAL, false); int wdaytemp = timeptr->tm_wday; if (specifier == L'W') { if (timeptr->tm_wday == 0) // Monday-based wdaytemp = 6; else wdaytemp = timeptr->tm_wday - 1; }
_VALIDATE_RETURN(timeptr->tm_yday >= 0 && timeptr->tm_yday <= 365, EINVAL, false); unsigned week_number = 0; if (timeptr->tm_yday >= wdaytemp) { week_number = timeptr->tm_yday / 7; if (timeptr->tm_yday % 7 >= wdaytemp) ++week_number; }
store_number(week_number, 2, string, left, alternate_form ? '\0' : '0'); return true; }
case L'V': // ISO 8601 week number (01-53): { int const iso_week{compute_iso_week(timeptr->tm_year, timeptr->tm_wday, timeptr->tm_yday)}; store_number(iso_week, 2, string, left, alternate_form ? '\0' : '0'); return true; }
case L'x': // date display { int const field_code = alternate_form ? WW_LDATEFMT : WW_SDATEFMT; _VALIDATE_RETURN_NOEXC(store_winword(locale, field_code, timeptr, string, left, lc_time), EINVAL, false); return true; } case L'X': // time display { _VALIDATE_RETURN_NOEXC(store_winword(locale, WW_TIMEFMT, timeptr, string, left, lc_time), EINVAL, false); return true; }
case L'y': // year without century (00-99) { _VALIDATE_RETURN(timeptr->tm_year >= -1900 && timeptr->tm_year <= 8099, EINVAL, false); unsigned const two_digit_year = __crt_get_2digit_year(timeptr->tm_year); store_number(two_digit_year, 2, string, left, alternate_form ? '\0' : '0'); return true; }
case L'Y': // year with century { _VALIDATE_RETURN(timeptr->tm_year >= -1900 && timeptr->tm_year <= 8099, EINVAL, false); unsigned const full_year = timeptr->tm_year + 1900;
store_number(full_year, 4, string, left, alternate_form ? '\0' : '0'); return true; }
case L'z': // time zone in ISO 8601 form ("-0430" = 4 hours 30 minutes) { __tzset();
// Get the current time zone offset from UTC and, if we are currently in // daylight savings time, adjust appropriately: long offset{}; _VALIDATE_RETURN(_get_timezone(&offset) == 0, EINVAL, false);
if (timeptr->tm_isdst) { long dst_bias{}; _VALIDATE_RETURN(_get_dstbias(&dst_bias) == 0, EINVAL, false);
offset += dst_bias; }
long const positive_offset{offset < 0 ? -offset : offset}; long const hours_offset {(positive_offset / 60) / 60}; long const minutes_offset{(positive_offset / 60) % 60};
// This looks wrong, but it is correct: The offset is the difference // between UTC and the local time zone, so it is a positive value if // the local time zone is behind UTC. wchar_t const* const sign_string{offset <= 0 ? L"+" : L"-"};
store_string(sign_string, string, left); store_number(hours_offset, 2, string, left, '0'); store_number(minutes_offset, 2, string, left, '0'); return true; }
case L'Z': // time zone name, if any { __tzset(); store_string(__wide_tzname()[timeptr->tm_isdst ? 1 : 0], string, left); return true; }
case L'%': // percent sign { store_string(L"%", string, left); return true; }
default: // unknown format directive { // We do not raise the invalid parameter handler here. Our caller will // raise the invalid parameter handler when we return failure. return false; } }
// Unreachable. All switch case statements return.}
//-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+//// The _wcsftime family of functions////-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+// These functions format a time as a string using a given locale. They place// characters into the user's output buffer, expanding time format directives as// described in the provided control string. The lc_time_arg and locale are// used for locale data.//// If the total number of characters that need to be written (including the null// terminator) is less than the max_size, then the number of characters written// (not including the null terminator) is returned. Otherwise, zero is returned.extern "C" size_t __cdecl _Wcsftime_l( wchar_t* const string, size_t const max_size, wchar_t const* const format, tm const* const timeptr, void* const lc_time_arg, _locale_t const locale ){ _VALIDATE_RETURN(string != nullptr, EINVAL, 0) _VALIDATE_RETURN(max_size != 0, EINVAL, 0) *string = L'\0';
_VALIDATE_RETURN(format != nullptr, EINVAL, 0)
_LocaleUpdate locale_update(locale);
__crt_lc_time_data const* const lc_time = lc_time_arg == 0 ? locale_update.GetLocaleT()->locinfo->lc_time_curr : static_cast<__crt_lc_time_data*>(lc_time_arg);
// Copy the input string to the output string expanding the format // designations appropriately. Stop copying when one of the following // is true: (1) we hit a null char in the input stream, or (2) there's // no room left in the output stream.
wchar_t* string_it = string; wchar_t const* format_it = format;
bool failed = false; size_t remaining = max_size;
while (remaining > 0) { switch (*format_it) { case L'\0': { // End of format input string goto done; }
case L'%': { // Format directive. Take appropriate action based on format control character. _VALIDATE_RETURN(timeptr != nullptr, EINVAL, 0);
++format_it; // Skip '%'
// Process flags: bool alternate_form = false; if (*format_it == L'#') { alternate_form = true; ++format_it; }
// Skip ISO E and O alternative representation format modifiers. We // do not support alternative formats in any locale. if (*format_it == L'E' || *format_it == L'O') { ++format_it; }
if (!expand_time(locale_update.GetLocaleT(), *format_it, timeptr, &string_it, &remaining, lc_time, alternate_form)) { // if we don't have any space left, do not set the failure flag: // we will simply return ERANGE and do not call the invalid // parameter handler (see below) if (remaining > 0) failed = true;
goto done; }
++format_it; // Skip format char break; }
default: { // store character, bump pointers, decrement the char count: *string_it++ = *format_it++; --remaining; break; } } }
// All done. See if we terminated because we hit a null char or because // we ran out of space: done:
if (!failed && remaining > 0) { // Store a terminating null char and return the number of chars we // stored in the output string: *string_it = L'\0'; return max_size - remaining; } else { // Error: return an empty string: *string = L'\0';
// Now return our error/insufficient buffer indication: if (!failed && remaining <= 0) { // Do not report this as an error to allow the caller to resize: errno = ERANGE; } else { _VALIDATE_RETURN(false, EINVAL, 0); }
return 0; }}
extern "C" size_t __cdecl _Wcsftime( wchar_t* const buffer, size_t const max_size, wchar_t const* const format, tm const* const timeptr, void* const lc_time_arg ){ return _Wcsftime_l(buffer, max_size, format, timeptr, lc_time_arg, nullptr);}
extern "C" size_t __cdecl _wcsftime_l( wchar_t* const buffer, size_t const max_size, wchar_t const* const format, tm const* const timeptr, _locale_t const locale ){ return _Wcsftime_l(buffer, max_size, format, timeptr, nullptr, locale);}
extern "C" size_t __cdecl wcsftime( wchar_t* const buffer, size_t const max_size, wchar_t const* const format, tm const* const timeptr ){ return _Wcsftime_l(buffer, max_size, format, timeptr, nullptr, nullptr);}
/* * Copyright (c) 1992-2013 by P.J. Plauger. ALL RIGHTS RESERVED. * Consult your license regarding permissions and restrictions.V6.40:0009 */