Reactos
at master 1207 lines 40 kB view raw
1// 2// wcsftime.cpp 3// 4// Copyright (c) Microsoft Corporation. All rights reserved. 5// 6// The wcsftime family of functions, which format time data into a wide string, 7// and related functionality. 8// 9#include <corecrt_internal_time.h> 10#include <stdlib.h> 11#include <locale.h> 12 13 14//-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 15// 16// Day and Month Name and Time Locale Information Fetching Functions 17// 18//-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 19extern "C" wchar_t* __cdecl _W_Getdays() 20{ 21 _LocaleUpdate locale_update(nullptr); 22 __crt_lc_time_data const* const time_data = locale_update.GetLocaleT()->locinfo->lc_time_curr; 23 24 size_t length = 0; 25 for (size_t n = 0; n < 7; ++n) 26 { 27 length += wcslen(time_data->_W_wday_abbr[n]) + wcslen(time_data->_W_wday[n]) + 2; 28 } 29 30 __crt_unique_heap_ptr<wchar_t> buffer(_malloc_crt_t(wchar_t, length + 1)); 31 if (buffer.get() == nullptr) 32 return nullptr; 33 34 wchar_t* it = buffer.get(); 35 for (size_t n = 0; n < 7; ++n) 36 { 37 *it++ = L':'; 38 _ERRCHECK(wcscpy_s(it, (length + 1) - (it - buffer.get()), time_data->_W_wday_abbr[n])); 39 it += wcslen(it); 40 *it++ = L':'; 41 _ERRCHECK(wcscpy_s(it, (length + 1) - (it - buffer.get()), time_data->_W_wday[n])); 42 it += wcslen(it); 43 } 44 *it++ = L'\0'; 45 46 return buffer.detach(); 47} 48 49 50 51extern "C" wchar_t* __cdecl _W_Getmonths() 52{ 53 _LocaleUpdate locale_update(nullptr); 54 __crt_lc_time_data const* const time_data = locale_update.GetLocaleT()->locinfo->lc_time_curr; 55 56 size_t length = 0; 57 for (size_t n = 0; n < 12; ++n) 58 { 59 length += wcslen(time_data->_W_month_abbr[n]) + wcslen(time_data->_W_month[n]) + 2; 60 } 61 62 __crt_unique_heap_ptr<wchar_t> buffer(_malloc_crt_t(wchar_t, length + 1)); 63 if (buffer.get() == nullptr) 64 return nullptr; 65 66 wchar_t* it = buffer.get(); 67 for (size_t n = 0; n < 12; ++n) 68 { 69 *it++ = L':'; 70 _ERRCHECK(wcscpy_s(it, (length + 1) - (it - buffer.get()), time_data->_W_month_abbr[n])); 71 it += wcslen(it); 72 *it++ = L':'; 73 _ERRCHECK(wcscpy_s(it, (length + 1) - (it - buffer.get()), time_data->_W_month[n])); 74 it += wcslen(it); 75 } 76 *it++ = L'\0'; 77 78 return buffer.detach(); 79} 80 81 82 83extern "C" void* __cdecl _W_Gettnames() 84{ 85 _LocaleUpdate locale_update(nullptr); 86 __crt_lc_time_data const* const src = locale_update.GetLocaleT()->locinfo->lc_time_curr; 87 88 89 90 #define PROCESS_STRING(STR, CHAR, CPY, LEN) \ 91 while (bytes % sizeof(CHAR) != 0) \ 92 { \ 93 ++bytes; \ 94 } \ 95 if (phase == 1) \ 96 { \ 97 dest->STR = ((CHAR *) dest) + bytes / sizeof(CHAR); \ 98 _ERRCHECK(CPY(dest->STR, (total_bytes - bytes) / sizeof(CHAR), src->STR)); \ 99 } \ 100 bytes += (LEN(src->STR) + 1) * sizeof(CHAR); 101 102 #define PROCESS_NARROW_STRING(STR) \ 103 PROCESS_STRING(STR, char, strcpy_s, strlen) 104 105 #define PROCESS_WIDE_STRING(STR) \ 106 PROCESS_STRING(STR, wchar_t, wcscpy_s, wcslen) 107 108 #define PROCESS_NARROW_ARRAY(ARR) \ 109 for (size_t idx = 0; idx < _countof(src->ARR); ++idx) \ 110 { \ 111 PROCESS_NARROW_STRING(ARR[idx]) \ 112 } 113 114 #define PROCESS_WIDE_ARRAY(ARR) \ 115 for (size_t idx = 0; idx < _countof(src->ARR); ++idx) \ 116 { \ 117 PROCESS_WIDE_STRING(ARR[idx]) \ 118 } 119 120 121 size_t total_bytes = 0; 122 size_t bytes = sizeof(__crt_lc_time_data); 123 124 __crt_lc_time_data* dest = nullptr; 125 for (int phase = 0; phase < 2; ++phase) 126 { 127 if (phase == 1) 128 { 129 dest = static_cast<__crt_lc_time_data*>(_malloc_crt(bytes)); 130 131 if (!dest) { 132 return nullptr; 133 } 134 135 memset(dest, 0, bytes); 136 137 total_bytes = bytes; 138 139 bytes = sizeof(__crt_lc_time_data); 140 } 141 142 PROCESS_NARROW_ARRAY(wday_abbr) 143 PROCESS_NARROW_ARRAY(wday) 144 PROCESS_NARROW_ARRAY(month_abbr) 145 PROCESS_NARROW_ARRAY(month) 146 PROCESS_NARROW_ARRAY(ampm) 147 PROCESS_NARROW_STRING(ww_sdatefmt) 148 PROCESS_NARROW_STRING(ww_ldatefmt) 149 PROCESS_NARROW_STRING(ww_timefmt) 150 151 if (phase == 1) 152 { 153 dest->ww_caltype = src->ww_caltype; 154 dest->refcount = 0; 155 } 156 157 PROCESS_WIDE_ARRAY(_W_wday_abbr) 158 PROCESS_WIDE_ARRAY(_W_wday) 159 PROCESS_WIDE_ARRAY(_W_month_abbr) 160 PROCESS_WIDE_ARRAY(_W_month) 161 PROCESS_WIDE_ARRAY(_W_ampm) 162 PROCESS_WIDE_STRING(_W_ww_sdatefmt) 163 PROCESS_WIDE_STRING(_W_ww_ldatefmt) 164 PROCESS_WIDE_STRING(_W_ww_timefmt) 165 PROCESS_WIDE_STRING(_W_ww_locale_name) 166 } 167 168 return dest; 169} 170 171 172 173//-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 174// 175// Local Functions Used In Time String Formatting 176// 177//-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 178// Values for __crt_lc_time_data ww_* fields for store_winword: 179#define WW_SDATEFMT 0 180#define WW_LDATEFMT 1 181#define WW_TIMEFMT 2 182 183// Note: annotation does not account for fact < *count bytes may be written if null terminator hit 184#define _CrtWcstime_Writes_and_advances_ptr_(count) \ 185 _Outptr_result_buffer_(count) 186 187 188// Copies the supplied time string 'in' into the output buffer 'out' until either 189// (a) the end of the time string is reached or (b) '*count' becomes zero (and we 190// run out of buffer space). The '*out' pointer is updated to point to the next 191// character in the buffer (one-past-the-end of the insertion), and the '*count' 192// value is updated to reflect the number of characters written. 193static void __cdecl store_string( 194 _In_reads_or_z_(*count) wchar_t const* in, 195 _CrtWcstime_Writes_and_advances_ptr_(*count) wchar_t** const out, 196 _Inout_ size_t* const count 197 ) throw() 198{ 199 while (*count != 0 && *in != L'\0') 200 { 201 *(*out)++ = *in++; 202 --*count; 203 } 204} 205 206 207 208// Converts a positive integer ('value') into a string and stores it in the 209// 'output' buffer. It stops writing when either (a) the full value has been 210// printed, or (b) '*count' becomes zero (and we run out of buffer space). The 211// '*out' pointer is updated to point to the next character in the buffer (one- 212// past-the-end of the insertion), and the '*count' value is updated to reflect 213// the number of characters written. 214static void __cdecl store_number_without_lead_zeroes( 215 int value, 216 _CrtWcstime_Writes_and_advances_ptr_(*count) wchar_t** const out, 217 _Inout_ size_t* const count 218 ) throw() 219{ 220 // Put the digits in the buffer in reverse order: 221 wchar_t* out_it = *out; 222 if (*count > 1) 223 { 224 do 225 { 226 *out_it++ = static_cast<wchar_t>(value % 10 + L'0'); 227 228 value /= 10; 229 --*count; 230 } 231 while (value > 0 && *count > 1); 232 } 233 else 234 { 235 // Indicate buffer too small. 236 *out -= *count; 237 *count = 0; 238 return; 239 } 240 241 wchar_t* left = *out; 242 wchar_t* right = out_it - 1; 243 244 // Update the output iterator to point to the next space: 245 *out = out_it; 246 247 // Reverse the buffer: 248 while (left < right) 249 { 250 wchar_t const x = *right; 251 *right-- = *left; 252 *left++ = x; 253 } 254} 255 256 257 258// Converts a positive integer ('value') into a string and stores it in the 259// 'output' buffer. Both '*out' and '*count' are updated to reflect the 260// write. 261static void __cdecl store_number( 262 int value, 263 int digits, 264 _CrtWcstime_Writes_and_advances_ptr_(*count) wchar_t** const out, 265 _Inout_ size_t* const count, 266 wchar_t const pad_character 267 ) throw() 268{ 269 if (pad_character == '\0') 270 { 271 store_number_without_lead_zeroes(value, out, count); 272 return; 273 } 274 275 if (static_cast<size_t>(digits) < *count) 276 { 277 int temp = 0; 278 for (digits--; digits + 1 != 0; --digits) 279 { 280 if (value != 0) 281 { 282 (*out)[digits] = static_cast<wchar_t>(L'0' + value % 10); 283 } 284 else 285 { 286 (*out)[digits] = pad_character; 287 } 288 289 value /= 10; 290 temp++; 291 } 292 293 *out += temp; 294 *count -= temp; 295 } 296 else 297 { 298 *count = 0; 299 } 300} 301 302 303 304//-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 305// 306// Local Functions Used for ISO Week-Based Year Computations 307// 308//-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 309enum 310{ 311 sunday = 0, 312 monday = 1, 313 tuesday = 2, 314 wednesday = 3, 315 thursday = 4, 316 friday = 5, 317 saturday = 6 318}; 319 320static int compute_week_of_year(int const wstart, int const wday, int const yday) throw() 321{ 322 int const adjusted_wday{(wday + 7 - wstart) % 7}; 323 return (yday + 7 - adjusted_wday) / 7; 324} 325 326static int __cdecl compute_iso_week_internal(int year, int wday, int yday) throw() 327{ 328 int const week_number{compute_week_of_year(monday, wday, yday)}; 329 bool const is_leap_year{__crt_time_is_leap_year(year)}; 330 331 int const yunleap{yday - is_leap_year}; 332 int const jan1{(371 - yday + wday) % 7}; 333 int const dec32{(jan1 + is_leap_year + 365) % 7}; 334 335 if ((364 <= yunleap && dec32 == tuesday ) || 336 (363 <= yunleap && dec32 == wednesday) || 337 (362 <= yunleap && dec32 == thursday )) 338 { 339 return -1; // Push into the next year 340 } 341 else if (jan1 == tuesday || jan1 == wednesday || jan1 == thursday) 342 { 343 return week_number + 1; 344 } 345 346 return week_number; 347} 348 349static int __cdecl compute_iso_week(int const year, int const wday, int const yday) throw() 350{ 351 int const week_number{compute_iso_week_internal(year, wday, yday)}; 352 353 if (week_number == 0) 354 return compute_iso_week_internal(year - 1, wday + 7 - yday, __crt_time_is_leap_year(year - 1) ? 366 : 365); 355 356 if (0 < week_number) 357 return week_number; 358 359 return 1; 360} 361 362static int __cdecl compute_iso_year(int const year, int const wday, int const yday) throw() 363{ 364 int const week_number{compute_iso_week_internal(year, wday, yday)}; 365 366 if (week_number == 0) 367 return year - 1; 368 369 if (0 < week_number) 370 return year; 371 372 return year + 1; 373} 374 375 376 377// store_winword and expand_time are mutually recursive 378_Success_(return) 379static bool __cdecl expand_time( 380 _locale_t locale, 381 wchar_t specifier, 382 tm const* tmptr, 383 _CrtWcstime_Writes_and_advances_ptr_(*count) wchar_t** out, 384 _Inout_ size_t* count, 385 __crt_lc_time_data const* lc_time, 386 bool alternate_form 387 ) throw(); 388 389 390 391// Formats the date and time in the supplied WinWord format and stores the 392// formatted result in the supplied buffer. For simple localized Gregorian 393// calendars (calendar type 1), the WinWord format is converted token-by-token 394// to wcsftime conversion specifiers. expand_time is then called to do the 395// heavy lifting. For other calendar types, the Windows APIs GetDateFormatEx 396// and GetTimeFormatEx are instead used to do all the formatting, so this 397// function does not need to know about era and period strings, year offsets, 398// etc. Returns true on success; false on failure. 399_Success_(return) 400static bool __cdecl store_winword( 401 _locale_t const locale, 402 int const field_code, 403 tm const* const tmptr, 404 _CrtWcstime_Writes_and_advances_ptr_(*count) wchar_t** const out, 405 _Inout_ size_t* const count, 406 __crt_lc_time_data const* const lc_time 407 ) throw() 408{ 409 wchar_t const* format; 410 switch (field_code) 411 { 412 case WW_SDATEFMT: 413 format = lc_time->_W_ww_sdatefmt; 414 break; 415 416 case WW_LDATEFMT: 417 format = lc_time->_W_ww_ldatefmt; 418 break; 419 420 case WW_TIMEFMT: 421 default: 422 format = lc_time->_W_ww_timefmt; 423 break; 424 } 425 426 if (lc_time->ww_caltype != 1) 427 { 428 // We have something other than the basic Gregorian calendar 429 bool const is_time_format = field_code == WW_TIMEFMT; 430 431 // We leave the verification of the SYSTEMTIME up to the Windows API 432 // that we call; if one of those functions returns zero to indicate 433 // failure, we fall through and call expand_time() again. 434 SYSTEMTIME system_time; 435 system_time.wYear = static_cast<WORD>(tmptr->tm_year + 1900); 436 system_time.wMonth = static_cast<WORD>(tmptr->tm_mon + 1); 437 system_time.wDay = static_cast<WORD>(tmptr->tm_mday); 438 system_time.wHour = static_cast<WORD>(tmptr->tm_hour); 439 system_time.wMinute = static_cast<WORD>(tmptr->tm_min); 440 system_time.wSecond = static_cast<WORD>(tmptr->tm_sec); 441 system_time.wMilliseconds = 0; 442 443 // Find buffer size required: 444 int cch; 445 if (is_time_format) 446 cch = __acrt_GetTimeFormatEx(lc_time->_W_ww_locale_name, 0, &system_time, format, nullptr, 0); 447 else 448 cch = __acrt_GetDateFormatEx(lc_time->_W_ww_locale_name, 0, &system_time, format, nullptr, 0, nullptr); 449 450 if (cch != 0) 451 { 452 __crt_scoped_stack_ptr<wchar_t> const buffer(_malloca_crt_t(wchar_t, cch)); 453 if (buffer.get() != nullptr) 454 { 455 // Do actual date/time formatting: 456 if (is_time_format) 457 cch = __acrt_GetTimeFormatEx(lc_time->_W_ww_locale_name, 0, &system_time, format, buffer.get(), cch); 458 else 459 cch = __acrt_GetDateFormatEx(lc_time->_W_ww_locale_name, 0, &system_time, format, buffer.get(), cch, nullptr); 460 461 // Copy to output buffer: 462 wchar_t const* buffer_it = buffer.get(); 463 while (--cch > 0 && *count > 0) 464 { 465 *(*out)++ = *buffer_it++; 466 (*count)--; 467 } 468 469 return true; 470 } 471 } 472 473 // If an error occurs, just fall through to localized Gregorian... 474 } 475 476 while (*format && *count != 0) 477 { 478 wchar_t specifier = 0; 479 bool no_lead_zeros = false; 480 481 // Count the number of repetitions of this character 482 int repeat = 0; 483 wchar_t const* p = format; 484 for (; *p++ == *format; ++repeat); 485 // Leave p pointing to the beginning of the next token 486 p--; 487 488 // Switch on ASCII format character and determine specifier: 489 switch (*format) 490 { 491 case L'M': 492 { 493 switch (repeat) 494 { 495 case 1: no_lead_zeros = true; // fall through 496 case 2: specifier = L'm'; break; 497 case 3: specifier = L'b'; break; 498 case 4: specifier = L'B'; break; 499 } 500 break; 501 } 502 503 case L'd': 504 { 505 switch (repeat) 506 { 507 case 1: no_lead_zeros = true; // fall through 508 case 2: specifier = L'd'; break; 509 case 3: specifier = L'a'; break; 510 case 4: specifier = L'A'; break; 511 } 512 break; 513 } 514 515 case L'y': 516 { 517 switch (repeat) 518 { 519 case 2: specifier = L'y'; break; 520 case 4: specifier = L'Y'; break; 521 } 522 break; 523 } 524 525 case L'h': 526 { 527 switch (repeat) 528 { 529 case 1: no_lead_zeros = true; // fall through 530 case 2: specifier = L'I'; break; 531 } 532 break; 533 } 534 535 case L'H': 536 { 537 switch (repeat) 538 { 539 case 1: no_lead_zeros = true; // fall through 540 case 2: specifier = L'H'; break; 541 } 542 break; 543 } 544 545 case L'm': 546 { 547 switch (repeat) 548 { 549 case 1: no_lead_zeros = true; // fall through 550 case 2: specifier = L'M'; break; 551 } 552 break; 553 } 554 555 case L's': // for compatibility; not strictly WinWord 556 { 557 switch (repeat) 558 { 559 case 1: no_lead_zeros = true; // fall through 560 case 2: specifier = L'S'; break; 561 } 562 break; 563 } 564 565 case L'A': 566 case L'a': 567 { 568 if (!_wcsicmp(format, L"am/pm")) 569 { 570 p = format + 5; 571 } 572 else if (!_wcsicmp(format, L"a/p")) 573 { 574 p = format + 3; 575 } 576 577 specifier = L'p'; 578 break; 579 } 580 581 case L't': // t or tt time marker suffix 582 { 583 wchar_t* ampmstr = tmptr->tm_hour <= 11 584 ? lc_time->_W_ampm[0] 585 : lc_time->_W_ampm[1]; 586 587 if (repeat == 1 && *count > 0) 588 { 589 *(*out)++ = *ampmstr++; 590 (*count)--; 591 } 592 else 593 { 594 while (*ampmstr != 0 && *count > 0) 595 { 596 *(*out)++ = *ampmstr++; 597 --*count; 598 } 599 } 600 format = p; 601 continue; 602 } 603 604 case L'\'': // literal string 605 { 606 if (repeat % 2 == 0) // even number 607 { 608 format += repeat; 609 } 610 else // odd number 611 { 612 format += repeat; 613 while (*format && *count != 0) 614 { 615 if (*format == L'\'') 616 { 617 format++; 618 break; 619 } 620 621 *(*out)++ = *format++; 622 --*count; 623 } 624 } 625 626 continue; 627 } 628 629 default: // non-control char, print it 630 { 631 break; 632 } 633 } 634 635 // expand specifier, or copy literal if specifier not found 636 if (specifier) 637 { 638 _VALIDATE_RETURN_NOEXC(expand_time(locale, specifier, tmptr, out, count, lc_time, no_lead_zeros), EINVAL, false); 639 640 format = p; // bump format up to the next token 641 } 642 else 643 { 644 *(*out)++ = *format++; 645 --*count; 646 } 647 } 648 649 return true; 650} 651 652 653 654// Expands the conversion specifier using the time struct and stores the result 655// into the supplied buffer. The expansion is locale-dependent. Returns true 656// on success; false on failure. 657static bool __cdecl expand_time( 658 _locale_t const locale, 659 wchar_t const specifier, 660 tm const* const timeptr, 661 wchar_t** const string, 662 size_t* const left, 663 __crt_lc_time_data const* const lc_time, 664 bool const alternate_form 665 ) throw() 666{ 667 switch (specifier) 668 { 669 case L'a': // abbreviated weekday name 670 { 671 _VALIDATE_RETURN(timeptr->tm_wday >= 0 && timeptr->tm_wday <= 6, EINVAL, false); 672 store_string(lc_time->_W_wday_abbr[timeptr->tm_wday], string, left); 673 return true; 674 } 675 676 case L'A': // full weekday name 677 { 678 _VALIDATE_RETURN(timeptr->tm_wday >= 0 && timeptr->tm_wday <= 6, EINVAL, false); 679 store_string(lc_time->_W_wday[timeptr->tm_wday], string, left); 680 return true; 681 } 682 683 case L'b': // abbreviated month name 684 { 685 _VALIDATE_RETURN(timeptr->tm_mon >= 0 && timeptr->tm_mon <= 11, EINVAL, false); 686 store_string(lc_time->_W_month_abbr[timeptr->tm_mon], string, left); 687 return true; 688 } 689 690 case L'B': // full month name 691 { 692 _VALIDATE_RETURN(timeptr->tm_mon >= 0 && timeptr->tm_mon <= 11, EINVAL, false); 693 store_string(lc_time->_W_month[timeptr->tm_mon], string, left); 694 return true; 695 } 696 697 case L'c': // appropriate date and time representation 698 { 699 // In the C locale, %c is equivalent to "%a %b %e %T %Y". This format 700 // is not achievable using the Windows API date and time format APIs 701 // (it's hard to interleave date and time together, and there's no way 702 // to format %e). Therefore, we special case this specifier for the C 703 // locale. 704 if (lc_time == &__lc_time_c && !alternate_form) 705 { 706 _VALIDATE_RETURN_NOEXC(expand_time(locale, L'a', timeptr, string, left, lc_time, false), EINVAL, false); 707 store_string(L" ", string, left); 708 _VALIDATE_RETURN_NOEXC(expand_time(locale, L'b', timeptr, string, left, lc_time, false), EINVAL, false); 709 store_string(L" ", string, left); 710 _VALIDATE_RETURN_NOEXC(expand_time(locale, L'e', timeptr, string, left, lc_time, false), EINVAL, false); 711 store_string(L" ", string, left); 712 _VALIDATE_RETURN_NOEXC(expand_time(locale, L'T', timeptr, string, left, lc_time, false), EINVAL, false); 713 store_string(L" ", string, left); 714 _VALIDATE_RETURN_NOEXC(expand_time(locale, L'Y', timeptr, string, left, lc_time, false), EINVAL, false); 715 } 716 // Otherwise, if we're not in the C locale, use the locale-provided 717 // format strings: 718 else 719 { 720 int const field_code = alternate_form ? WW_LDATEFMT : WW_SDATEFMT; 721 722 _VALIDATE_RETURN_NOEXC(store_winword(locale, field_code, timeptr, string, left, lc_time), EINVAL, false); 723 store_string(L" ", string, left); 724 _VALIDATE_RETURN_NOEXC(store_winword(locale, WW_TIMEFMT, timeptr, string, left, lc_time), EINVAL, false); 725 } 726 727 return true; 728 } 729 730 case L'C': // century in decimal (00-99) 731 { 732 _VALIDATE_RETURN(timeptr->tm_year >= -1900 && timeptr->tm_year <= 8099, EINVAL, false); 733 store_number(__crt_get_century(timeptr->tm_year), 2, string, left, alternate_form ? '\0' : '0'); 734 return true; 735 } 736 737 case L'd': // day of the month in decimal (01-31) 738 { 739 _VALIDATE_RETURN(timeptr->tm_mday >= 1 && timeptr->tm_mday <= 31, EINVAL, false); 740 store_number(timeptr->tm_mday, 2, string, left, alternate_form ? '\0' : '0'); 741 return true; 742 } 743 744 case L'D': // equivalent to "%m/%d/%y" 745 { 746 _VALIDATE_RETURN_NOEXC(expand_time(locale, L'm', timeptr, string, left, lc_time, alternate_form), EINVAL, false); 747 store_string(L"/", string, left); 748 _VALIDATE_RETURN_NOEXC(expand_time(locale, L'd', timeptr, string, left, lc_time, alternate_form), EINVAL, false); 749 store_string(L"/", string, left); 750 _VALIDATE_RETURN_NOEXC(expand_time(locale, L'y', timeptr, string, left, lc_time, alternate_form), EINVAL, false); 751 return true; 752 } 753 754 case L'e': // day of month as a decimal number (1-31); space padded: 755 { 756 _VALIDATE_RETURN(timeptr->tm_mday >= 1 && timeptr->tm_mday <= 31, EINVAL, false); 757 store_number(timeptr->tm_mday, 2, string, left, alternate_form ? '\0' : ' '); 758 return true; 759 } 760 761 case L'F': // equivalent to "%Y-%m-%d" (ISO 8601): 762 { 763 _VALIDATE_RETURN_NOEXC(expand_time(locale, L'Y', timeptr, string, left, lc_time, alternate_form), EINVAL, false); 764 store_string(L"-", string, left); 765 _VALIDATE_RETURN_NOEXC(expand_time(locale, L'm', timeptr, string, left, lc_time, alternate_form), EINVAL, false); 766 store_string(L"-", string, left); 767 _VALIDATE_RETURN_NOEXC(expand_time(locale, L'd', timeptr, string, left, lc_time, alternate_form), EINVAL, false); 768 return true; 769 } 770 771 case L'g': // last two digits of the week-based year: 772 { 773 _VALIDATE_RETURN(timeptr->tm_year >= -1900 && timeptr->tm_year <= 8099, EINVAL, false); 774 int const iso_year{compute_iso_year(timeptr->tm_year, timeptr->tm_wday, timeptr->tm_yday) + 1900}; 775 store_number(iso_year % 100, 2, string, left, '0'); 776 return true; 777 } 778 779 case L'G': // week-based year: 780 { 781 _VALIDATE_RETURN(timeptr->tm_year >= -1900 && timeptr->tm_year <= 8099, EINVAL, false); 782 int const iso_year{compute_iso_year(timeptr->tm_year, timeptr->tm_wday, timeptr->tm_yday) + 1900}; 783 store_number(iso_year, 4, string, left, '0'); 784 return true; 785 } 786 787 case L'h': // equivalent to "%b": 788 { 789 return expand_time(locale, L'b', timeptr, string, left, lc_time, alternate_form); 790 } 791 792 case L'H': // 24-hour decimal (00-23) 793 { 794 _VALIDATE_RETURN(timeptr->tm_hour >= 0 && timeptr->tm_hour <= 23, EINVAL, false); 795 store_number(timeptr->tm_hour, 2, string, left, alternate_form ? '\0' : '0'); 796 return true; 797 } 798 799 case L'I': // 12-hour decimal (01-12) 800 { 801 _VALIDATE_RETURN(timeptr->tm_hour >= 0 && timeptr->tm_hour <= 23, EINVAL, false); 802 unsigned hour = timeptr->tm_hour % 12; 803 if (hour == 0) 804 hour = 12; 805 806 store_number(hour, 2, string, left, alternate_form ? '\0' : '0'); 807 return true; 808 } 809 810 case L'j': // yday in decimal (001-366) 811 { 812 _VALIDATE_RETURN(timeptr->tm_yday >= 0 && timeptr->tm_yday <= 365, EINVAL, false); 813 store_number(timeptr->tm_yday + 1, 3, string, left, alternate_form ? '\0' : '0'); 814 return true; 815 } 816 817 case L'm': // month in decimal (01-12) 818 { 819 _VALIDATE_RETURN(timeptr->tm_mon >= 0 && timeptr->tm_mon <= 11, EINVAL, false); 820 store_number(timeptr->tm_mon + 1, 2, string, left, alternate_form ? '\0' : '0'); 821 return true; 822 } 823 824 case L'M': // minute in decimal (00-59) 825 { 826 _VALIDATE_RETURN(timeptr->tm_min >= 0 && timeptr->tm_min <= 59, EINVAL, false); 827 store_number(timeptr->tm_min, 2, string, left, alternate_form ? '\0' : '0'); 828 return true; 829 } 830 831 case L'n': // newline character 832 { 833 store_string(L"\n", string, left); 834 return true; 835 } 836 837 case L'p': // AM/PM designation 838 { 839 _VALIDATE_RETURN(timeptr->tm_hour >= 0 && timeptr->tm_hour <= 23, EINVAL, false); 840 wchar_t const* const ampm_string = timeptr->tm_hour <= 11 841 ? lc_time->_W_ampm[0] 842 : lc_time->_W_ampm[1]; 843 844 store_string(ampm_string, string, left); 845 return true; 846 } 847 848 case L'r': // Locale-specific 12-hour clock time 849 { 850 // In the C locale, %r is equivalent to "%I:%M:%S %p". This is the only 851 // locale in which we guarantee that %r is a 12-hour time; in all other 852 // locales we only have one time format which may or may not be a 12-hour 853 // format. 854 if (lc_time == &__lc_time_c) 855 { 856 _VALIDATE_RETURN_NOEXC(expand_time(locale, 'I', timeptr, string, left, lc_time, alternate_form), EINVAL, false); 857 store_string(L":", string, left); 858 _VALIDATE_RETURN_NOEXC(expand_time(locale, 'M', timeptr, string, left, lc_time, alternate_form), EINVAL, false); 859 store_string(L":", string, left); 860 _VALIDATE_RETURN_NOEXC(expand_time(locale, 'S', timeptr, string, left, lc_time, alternate_form), EINVAL, false); 861 store_string(L" ", string, left); 862 _VALIDATE_RETURN_NOEXC(expand_time(locale, 'p', timeptr, string, left, lc_time, alternate_form), EINVAL, false); 863 } 864 else 865 { 866 _VALIDATE_RETURN_NOEXC(expand_time(locale, 'X', timeptr, string, left, lc_time, alternate_form), EINVAL, false); 867 } 868 869 return true; 870 } 871 872 case L'R': // Equivalent to "%H:%M" 873 { 874 _VALIDATE_RETURN_NOEXC(expand_time(locale, 'H', timeptr, string, left, lc_time, alternate_form), EINVAL, false); 875 store_string(L":", string, left); 876 _VALIDATE_RETURN_NOEXC(expand_time(locale, 'M', timeptr, string, left, lc_time, alternate_form), EINVAL, false); 877 return true; 878 } 879 880 case L'S': // seconds in decimal (00-60) allowing for a leap second 881 { 882 _VALIDATE_RETURN(timeptr->tm_sec >= 0 && timeptr->tm_sec <= 60, EINVAL, false); 883 store_number(timeptr->tm_sec, 2, string, left, alternate_form ? '\0' : '0'); 884 return true; 885 } 886 887 case L't': // tab character 888 { 889 store_string(L"\t", string, left); 890 return true; 891 } 892 893 case L'T': // Equivalent to "%H:%M:%S" (ISO 8601) 894 { 895 _VALIDATE_RETURN_NOEXC(expand_time(locale, 'H', timeptr, string, left, lc_time, alternate_form), EINVAL, false); 896 store_string(L":", string, left); 897 _VALIDATE_RETURN_NOEXC(expand_time(locale, 'M', timeptr, string, left, lc_time, alternate_form), EINVAL, false); 898 store_string(L":", string, left); 899 _VALIDATE_RETURN_NOEXC(expand_time(locale, 'S', timeptr, string, left, lc_time, alternate_form), EINVAL, false); 900 return true; 901 } 902 903 case L'u': // week day in decimal (1-7) 904 case L'w': // week day in decimal (0-6) 905 { 906 _VALIDATE_RETURN(timeptr->tm_wday >= 0 && timeptr->tm_wday <= 6, EINVAL, false); 907 908 int const weekday_number = timeptr->tm_wday == 0 && specifier == L'u' 909 ? 7 910 : timeptr->tm_wday; 911 912 store_number(weekday_number, 1, string, left, alternate_form ? '\0' : '0'); 913 return true; 914 } 915 916 case L'U': // sunday week number (00-53) 917 case L'W': // monday week number (00-53) 918 { 919 _VALIDATE_RETURN(timeptr->tm_wday >= 0 && timeptr->tm_wday <= 6, EINVAL, false); 920 int wdaytemp = timeptr->tm_wday; 921 if (specifier == L'W') 922 { 923 if (timeptr->tm_wday == 0) // Monday-based 924 wdaytemp = 6; 925 else 926 wdaytemp = timeptr->tm_wday - 1; 927 } 928 929 _VALIDATE_RETURN(timeptr->tm_yday >= 0 && timeptr->tm_yday <= 365, EINVAL, false); 930 unsigned week_number = 0; 931 if (timeptr->tm_yday >= wdaytemp) 932 { 933 week_number = timeptr->tm_yday / 7; 934 if (timeptr->tm_yday % 7 >= wdaytemp) 935 ++week_number; 936 } 937 938 store_number(week_number, 2, string, left, alternate_form ? '\0' : '0'); 939 return true; 940 } 941 942 case L'V': // ISO 8601 week number (01-53): 943 { 944 int const iso_week{compute_iso_week(timeptr->tm_year, timeptr->tm_wday, timeptr->tm_yday)}; 945 store_number(iso_week, 2, string, left, alternate_form ? '\0' : '0'); 946 return true; 947 } 948 949 case L'x': // date display 950 { 951 int const field_code = alternate_form ? WW_LDATEFMT : WW_SDATEFMT; 952 _VALIDATE_RETURN_NOEXC(store_winword(locale, field_code, timeptr, string, left, lc_time), EINVAL, false); 953 return true; 954 } 955 case L'X': // time display 956 { 957 _VALIDATE_RETURN_NOEXC(store_winword(locale, WW_TIMEFMT, timeptr, string, left, lc_time), EINVAL, false); 958 return true; 959 } 960 961 case L'y': // year without century (00-99) 962 { 963 _VALIDATE_RETURN(timeptr->tm_year >= -1900 && timeptr->tm_year <= 8099, EINVAL, false); 964 unsigned const two_digit_year = __crt_get_2digit_year(timeptr->tm_year); 965 store_number(two_digit_year, 2, string, left, alternate_form ? '\0' : '0'); 966 return true; 967 } 968 969 case L'Y': // year with century 970 { 971 _VALIDATE_RETURN(timeptr->tm_year >= -1900 && timeptr->tm_year <= 8099, EINVAL, false); 972 unsigned const full_year = timeptr->tm_year + 1900; 973 974 store_number(full_year, 4, string, left, alternate_form ? '\0' : '0'); 975 return true; 976 } 977 978 case L'z': // time zone in ISO 8601 form ("-0430" = 4 hours 30 minutes) 979 { 980 __tzset(); 981 982 // Get the current time zone offset from UTC and, if we are currently in 983 // daylight savings time, adjust appropriately: 984 long offset{}; 985 _VALIDATE_RETURN(_get_timezone(&offset) == 0, EINVAL, false); 986 987 if (timeptr->tm_isdst) 988 { 989 long dst_bias{}; 990 _VALIDATE_RETURN(_get_dstbias(&dst_bias) == 0, EINVAL, false); 991 992 offset += dst_bias; 993 } 994 995 long const positive_offset{offset < 0 ? -offset : offset}; 996 long const hours_offset {(positive_offset / 60) / 60}; 997 long const minutes_offset{(positive_offset / 60) % 60}; 998 999 // This looks wrong, but it is correct: The offset is the difference 1000 // between UTC and the local time zone, so it is a positive value if 1001 // the local time zone is behind UTC. 1002 wchar_t const* const sign_string{offset <= 0 ? L"+" : L"-"}; 1003 1004 store_string(sign_string, string, left); 1005 store_number(hours_offset, 2, string, left, '0'); 1006 store_number(minutes_offset, 2, string, left, '0'); 1007 return true; 1008 } 1009 1010 case L'Z': // time zone name, if any 1011 { 1012 __tzset(); 1013 store_string(__wide_tzname()[timeptr->tm_isdst ? 1 : 0], string, left); 1014 return true; 1015 } 1016 1017 case L'%': // percent sign 1018 { 1019 store_string(L"%", string, left); 1020 return true; 1021 } 1022 1023 default: // unknown format directive 1024 { 1025 // We do not raise the invalid parameter handler here. Our caller will 1026 // raise the invalid parameter handler when we return failure. 1027 return false; 1028 } 1029 } 1030 1031 // Unreachable. All switch case statements return. 1032} 1033 1034 1035 1036//-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 1037// 1038// The _wcsftime family of functions 1039// 1040//-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 1041// These functions format a time as a string using a given locale. They place 1042// characters into the user's output buffer, expanding time format directives as 1043// described in the provided control string. The lc_time_arg and locale are 1044// used for locale data. 1045// 1046// If the total number of characters that need to be written (including the null 1047// terminator) is less than the max_size, then the number of characters written 1048// (not including the null terminator) is returned. Otherwise, zero is returned. 1049extern "C" size_t __cdecl _Wcsftime_l( 1050 wchar_t* const string, 1051 size_t const max_size, 1052 wchar_t const* const format, 1053 tm const* const timeptr, 1054 void* const lc_time_arg, 1055 _locale_t const locale 1056 ) 1057{ 1058 _VALIDATE_RETURN(string != nullptr, EINVAL, 0) 1059 _VALIDATE_RETURN(max_size != 0, EINVAL, 0) 1060 *string = L'\0'; 1061 1062 _VALIDATE_RETURN(format != nullptr, EINVAL, 0) 1063 1064 _LocaleUpdate locale_update(locale); 1065 1066 __crt_lc_time_data const* const lc_time = lc_time_arg == 0 1067 ? locale_update.GetLocaleT()->locinfo->lc_time_curr 1068 : static_cast<__crt_lc_time_data*>(lc_time_arg); 1069 1070 // Copy the input string to the output string expanding the format 1071 // designations appropriately. Stop copying when one of the following 1072 // is true: (1) we hit a null char in the input stream, or (2) there's 1073 // no room left in the output stream. 1074 1075 wchar_t* string_it = string; 1076 wchar_t const* format_it = format; 1077 1078 bool failed = false; 1079 size_t remaining = max_size; 1080 1081 while (remaining > 0) 1082 { 1083 switch (*format_it) 1084 { 1085 case L'\0': 1086 { 1087 // End of format input string 1088 goto done; 1089 } 1090 1091 case L'%': 1092 { 1093 // Format directive. Take appropriate action based on format control character. 1094 _VALIDATE_RETURN(timeptr != nullptr, EINVAL, 0); 1095 1096 ++format_it; // Skip '%' 1097 1098 // Process flags: 1099 bool alternate_form = false; 1100 if (*format_it == L'#') 1101 { 1102 alternate_form = true; 1103 ++format_it; 1104 } 1105 1106 // Skip ISO E and O alternative representation format modifiers. We 1107 // do not support alternative formats in any locale. 1108 if (*format_it == L'E' || *format_it == L'O') 1109 { 1110 ++format_it; 1111 } 1112 1113 if (!expand_time(locale_update.GetLocaleT(), *format_it, timeptr, &string_it, &remaining, lc_time, alternate_form)) 1114 { 1115 // if we don't have any space left, do not set the failure flag: 1116 // we will simply return ERANGE and do not call the invalid 1117 // parameter handler (see below) 1118 if (remaining > 0) 1119 failed = true; 1120 1121 goto done; 1122 } 1123 1124 ++format_it; // Skip format char 1125 break; 1126 } 1127 1128 default: 1129 { 1130 // store character, bump pointers, decrement the char count: 1131 *string_it++ = *format_it++; 1132 --remaining; 1133 break; 1134 } 1135 } 1136 } 1137 1138 1139 // All done. See if we terminated because we hit a null char or because 1140 // we ran out of space: 1141 done: 1142 1143 if (!failed && remaining > 0) 1144 { 1145 // Store a terminating null char and return the number of chars we 1146 // stored in the output string: 1147 *string_it = L'\0'; 1148 return max_size - remaining; 1149 } 1150 else 1151 { 1152 // Error: return an empty string: 1153 *string = L'\0'; 1154 1155 // Now return our error/insufficient buffer indication: 1156 if (!failed && remaining <= 0) 1157 { 1158 // Do not report this as an error to allow the caller to resize: 1159 errno = ERANGE; 1160 } 1161 else 1162 { 1163 _VALIDATE_RETURN(false, EINVAL, 0); 1164 } 1165 1166 return 0; 1167 } 1168} 1169 1170extern "C" size_t __cdecl _Wcsftime( 1171 wchar_t* const buffer, 1172 size_t const max_size, 1173 wchar_t const* const format, 1174 tm const* const timeptr, 1175 void* const lc_time_arg 1176 ) 1177{ 1178 return _Wcsftime_l(buffer, max_size, format, timeptr, lc_time_arg, nullptr); 1179} 1180 1181extern "C" size_t __cdecl _wcsftime_l( 1182 wchar_t* const buffer, 1183 size_t const max_size, 1184 wchar_t const* const format, 1185 tm const* const timeptr, 1186 _locale_t const locale 1187 ) 1188{ 1189 return _Wcsftime_l(buffer, max_size, format, timeptr, nullptr, locale); 1190} 1191 1192extern "C" size_t __cdecl wcsftime( 1193 wchar_t* const buffer, 1194 size_t const max_size, 1195 wchar_t const* const format, 1196 tm const* const timeptr 1197 ) 1198{ 1199 return _Wcsftime_l(buffer, max_size, format, timeptr, nullptr, nullptr); 1200} 1201 1202 1203 1204/* 1205 * Copyright (c) 1992-2013 by P.J. Plauger. ALL RIGHTS RESERVED. 1206 * Consult your license regarding permissions and restrictions. 1207V6.40:0009 */