1 /* Convert a broken-down time stamp to a string. */ 2 3 /* Copyright 1989 The Regents of the University of California. 4 All rights reserved. 5 6 Redistribution and use in source and binary forms, with or without 7 modification, are permitted provided that the following conditions 8 are met: 9 1. Redistributions of source code must retain the above copyright 10 notice, this list of conditions and the following disclaimer. 11 2. Redistributions in binary form must reproduce the above copyright 12 notice, this list of conditions and the following disclaimer in the 13 documentation and/or other materials provided with the distribution. 14 3. Neither the name of the University nor the names of its contributors 15 may be used to endorse or promote products derived from this software 16 without specific prior written permission. 17 18 THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS "AS IS" AND 19 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 22 FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 24 OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 25 HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 26 LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27 OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28 SUCH DAMAGE. */ 29 30 /* 31 ** Based on the UCB version with the copyright notice appearing above. 32 ** 33 ** This is ANSIish only when "multibyte character == plain character". 34 */ 35 36 #include "private.h" 37 38 #include "tzfile.h" 39 #include "fcntl.h" 40 #include "locale.h" 41 42 #if defined(__BIONIC__) 43 44 /* LP32 had a 32-bit time_t, so we need to work around that here. */ 45 #if defined(__LP64__) 46 #define time64_t time_t 47 #define mktime64 mktime 48 #else 49 #include <time64.h> 50 #endif 51 52 #include <ctype.h> 53 54 #endif 55 56 struct lc_time_T { 57 const char * mon[MONSPERYEAR]; 58 const char * month[MONSPERYEAR]; 59 const char * wday[DAYSPERWEEK]; 60 const char * weekday[DAYSPERWEEK]; 61 const char * X_fmt; 62 const char * x_fmt; 63 const char * c_fmt; 64 const char * am; 65 const char * pm; 66 const char * date_fmt; 67 }; 68 69 #define Locale (&C_time_locale) 70 71 static const struct lc_time_T C_time_locale = { 72 { 73 "Jan", "Feb", "Mar", "Apr", "May", "Jun", 74 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" 75 }, { 76 "January", "February", "March", "April", "May", "June", 77 "July", "August", "September", "October", "November", "December" 78 }, { 79 "Sun", "Mon", "Tue", "Wed", 80 "Thu", "Fri", "Sat" 81 }, { 82 "Sunday", "Monday", "Tuesday", "Wednesday", 83 "Thursday", "Friday", "Saturday" 84 }, 85 86 /* X_fmt */ 87 "%H:%M:%S", 88 89 /* 90 ** x_fmt 91 ** C99 requires this format. 92 ** Using just numbers (as here) makes Quakers happier; 93 ** it's also compatible with SVR4. 94 */ 95 "%m/%d/%y", 96 97 /* 98 ** c_fmt 99 ** C99 requires this format. 100 ** Previously this code used "%D %X", but we now conform to C99. 101 ** Note that 102 ** "%a %b %d %H:%M:%S %Y" 103 ** is used by Solaris 2.3. 104 */ 105 "%a %b %e %T %Y", 106 107 /* am */ 108 "AM", 109 110 /* pm */ 111 "PM", 112 113 /* date_fmt */ 114 "%a %b %e %H:%M:%S %Z %Y" 115 }; 116 117 static char * _add(const char *, char *, const char *, int); 118 static char * _conv(int, const char *, char *, const char *); 119 static char * _fmt(const char *, const struct tm *, char *, const char *, 120 int *); 121 static char * _yconv(int, int, bool, bool, char *, const char *, int); 122 123 #if !HAVE_POSIX_DECLS 124 extern char * tzname[]; 125 #endif 126 127 #ifndef YEAR_2000_NAME 128 #define YEAR_2000_NAME "CHECK_STRFTIME_FORMATS_FOR_TWO_DIGIT_YEARS" 129 #endif /* !defined YEAR_2000_NAME */ 130 131 #define IN_NONE 0 132 #define IN_SOME 1 133 #define IN_THIS 2 134 #define IN_ALL 3 135 136 #if HAVE_STRFTIME_L 137 size_t 138 strftime_l(char *s, size_t maxsize, char const *format, struct tm const *t, 139 locale_t locale) 140 { 141 /* Just call strftime, as only the C locale is supported. */ 142 return strftime(s, maxsize, format, t); 143 } 144 #endif 145 146 #define FORCE_LOWER_CASE 0x100 /* Android extension. */ 147 148 size_t 149 strftime(char *s, size_t maxsize, const char *format, const struct tm *t) 150 { 151 char * p; 152 int warn; 153 154 tzset(); 155 warn = IN_NONE; 156 p = _fmt(((format == NULL) ? "%c" : format), t, s, s + maxsize, &warn); 157 #ifndef NO_RUN_TIME_WARNINGS_ABOUT_YEAR_2000_PROBLEMS_THANK_YOU 158 if (warn != IN_NONE && getenv(YEAR_2000_NAME) != NULL) { 159 fprintf(stderr, "\n"); 160 if (format == NULL) 161 fprintf(stderr, "NULL strftime format "); 162 else fprintf(stderr, "strftime format \"%s\" ", 163 format); 164 fprintf(stderr, "yields only two digits of years in "); 165 if (warn == IN_SOME) 166 fprintf(stderr, "some locales"); 167 else if (warn == IN_THIS) 168 fprintf(stderr, "the current locale"); 169 else fprintf(stderr, "all locales"); 170 fprintf(stderr, "\n"); 171 } 172 #endif /* !defined NO_RUN_TIME_WARNINGS_ABOUT_YEAR_2000_PROBLEMS_THANK_YOU */ 173 if (p == s + maxsize) 174 return 0; 175 *p = '\0'; 176 return p - s; 177 } 178 179 static char *getformat(int modifier, char *normal, char *underscore, 180 char *dash, char *zero) { 181 switch (modifier) { 182 case '_': 183 return underscore; 184 case '-': 185 return dash; 186 case '0': 187 return zero; 188 } 189 return normal; 190 } 191 192 static char * 193 _fmt(const char *format, const struct tm *t, char *pt, 194 const char *ptlim, int *warnp) 195 { 196 for ( ; *format; ++format) { 197 if (*format == '%') { 198 int modifier = 0; 199 label: 200 switch (*++format) { 201 case '\0': 202 --format; 203 break; 204 case 'A': 205 pt = _add((t->tm_wday < 0 || 206 t->tm_wday >= DAYSPERWEEK) ? 207 "?" : Locale->weekday[t->tm_wday], 208 pt, ptlim, modifier); 209 continue; 210 case 'a': 211 pt = _add((t->tm_wday < 0 || 212 t->tm_wday >= DAYSPERWEEK) ? 213 "?" : Locale->wday[t->tm_wday], 214 pt, ptlim, modifier); 215 continue; 216 case 'B': 217 pt = _add((t->tm_mon < 0 || 218 t->tm_mon >= MONSPERYEAR) ? 219 "?" : Locale->month[t->tm_mon], 220 pt, ptlim, modifier); 221 continue; 222 case 'b': 223 case 'h': 224 pt = _add((t->tm_mon < 0 || 225 t->tm_mon >= MONSPERYEAR) ? 226 "?" : Locale->mon[t->tm_mon], 227 pt, ptlim, modifier); 228 continue; 229 case 'C': 230 /* 231 ** %C used to do a... 232 ** _fmt("%a %b %e %X %Y", t); 233 ** ...whereas now POSIX 1003.2 calls for 234 ** something completely different. 235 ** (ado, 1993-05-24) 236 */ 237 pt = _yconv(t->tm_year, TM_YEAR_BASE, 238 true, false, pt, ptlim, modifier); 239 continue; 240 case 'c': 241 { 242 int warn2 = IN_SOME; 243 244 pt = _fmt(Locale->c_fmt, t, pt, ptlim, &warn2); 245 if (warn2 == IN_ALL) 246 warn2 = IN_THIS; 247 if (warn2 > *warnp) 248 *warnp = warn2; 249 } 250 continue; 251 case 'D': 252 pt = _fmt("%m/%d/%y", t, pt, ptlim, warnp); 253 continue; 254 case 'd': 255 pt = _conv(t->tm_mday, getformat(modifier, "%02d", "%2d", "%d", "%02d"), pt, ptlim); 256 continue; 257 case 'E': 258 case 'O': 259 /* 260 ** C99 locale modifiers. 261 ** The sequences 262 ** %Ec %EC %Ex %EX %Ey %EY 263 ** %Od %oe %OH %OI %Om %OM 264 ** %OS %Ou %OU %OV %Ow %OW %Oy 265 ** are supposed to provide alternate 266 ** representations. 267 */ 268 goto label; 269 case '_': 270 case '-': 271 case '0': 272 case '^': 273 case '#': 274 modifier = *format; 275 goto label; 276 case 'e': 277 pt = _conv(t->tm_mday, getformat(modifier, "%2d", "%2d", "%d", "%02d"), pt, ptlim); 278 continue; 279 case 'F': 280 pt = _fmt("%Y-%m-%d", t, pt, ptlim, warnp); 281 continue; 282 case 'H': 283 pt = _conv(t->tm_hour, getformat(modifier, "%02d", "%2d", "%d", "%02d"), pt, ptlim); 284 continue; 285 case 'I': 286 pt = _conv((t->tm_hour % 12) ? 287 (t->tm_hour % 12) : 12, 288 getformat(modifier, "%02d", "%2d", "%d", "%02d"), pt, ptlim); 289 continue; 290 case 'j': 291 pt = _conv(t->tm_yday + 1, getformat(modifier, "%03d", "%3d", "%d", "%03d"), pt, ptlim); 292 continue; 293 case 'k': 294 /* 295 ** This used to be... 296 ** _conv(t->tm_hour % 12 ? 297 ** t->tm_hour % 12 : 12, 2, ' '); 298 ** ...and has been changed to the below to 299 ** match SunOS 4.1.1 and Arnold Robbins' 300 ** strftime version 3.0. That is, "%k" and 301 ** "%l" have been swapped. 302 ** (ado, 1993-05-24) 303 */ 304 pt = _conv(t->tm_hour, getformat(modifier, "%2d", "%2d", "%d", "%02d"), pt, ptlim); 305 continue; 306 #ifdef KITCHEN_SINK 307 case 'K': 308 /* 309 ** After all this time, still unclaimed! 310 */ 311 pt = _add("kitchen sink", pt, ptlim); 312 continue; 313 #endif /* defined KITCHEN_SINK */ 314 case 'l': 315 /* 316 ** This used to be... 317 ** _conv(t->tm_hour, 2, ' '); 318 ** ...and has been changed to the below to 319 ** match SunOS 4.1.1 and Arnold Robbin's 320 ** strftime version 3.0. That is, "%k" and 321 ** "%l" have been swapped. 322 ** (ado, 1993-05-24) 323 */ 324 pt = _conv((t->tm_hour % 12) ? 325 (t->tm_hour % 12) : 12, 326 getformat(modifier, "%2d", "%2d", "%d", "%02d"), pt, ptlim); 327 continue; 328 case 'M': 329 pt = _conv(t->tm_min, getformat(modifier, "%02d", "%2d", "%d", "%02d"), pt, ptlim); 330 continue; 331 case 'm': 332 pt = _conv(t->tm_mon + 1, getformat(modifier, "%02d", "%2d", "%d", "%02d"), pt, ptlim); 333 continue; 334 case 'n': 335 pt = _add("\n", pt, ptlim, modifier); 336 continue; 337 case 'P': 338 case 'p': 339 pt = _add((t->tm_hour >= (HOURSPERDAY / 2)) ? 340 Locale->pm : 341 Locale->am, 342 pt, ptlim, (*format == 'P') ? FORCE_LOWER_CASE : modifier); 343 continue; 344 case 'R': 345 pt = _fmt("%H:%M", t, pt, ptlim, warnp); 346 continue; 347 case 'r': 348 pt = _fmt("%I:%M:%S %p", t, pt, ptlim, warnp); 349 continue; 350 case 'S': 351 pt = _conv(t->tm_sec, getformat(modifier, "%02d", "%2d", "%d", "%02d"), pt, ptlim); 352 continue; 353 case 's': 354 { 355 struct tm tm; 356 char buf[INT_STRLEN_MAXIMUM( 357 time64_t) + 1]; 358 time64_t mkt; 359 360 tm = *t; 361 mkt = mktime64(&tm); 362 if (TYPE_SIGNED(time64_t)) 363 snprintf(buf, sizeof(buf), "%"PRIdMAX, 364 (intmax_t) mkt); 365 else snprintf(buf, sizeof(buf), "%"PRIuMAX, 366 (uintmax_t) mkt); 367 pt = _add(buf, pt, ptlim, modifier); 368 } 369 continue; 370 case 'T': 371 pt = _fmt("%H:%M:%S", t, pt, ptlim, warnp); 372 continue; 373 case 't': 374 pt = _add("\t", pt, ptlim, modifier); 375 continue; 376 case 'U': 377 pt = _conv((t->tm_yday + DAYSPERWEEK - 378 t->tm_wday) / DAYSPERWEEK, 379 getformat(modifier, "%02d", "%2d", "%d", "%02d"), pt, ptlim); 380 continue; 381 case 'u': 382 /* 383 ** From Arnold Robbins' strftime version 3.0: 384 ** "ISO 8601: Weekday as a decimal number 385 ** [1 (Monday) - 7]" 386 ** (ado, 1993-05-24) 387 */ 388 pt = _conv((t->tm_wday == 0) ? 389 DAYSPERWEEK : t->tm_wday, 390 "%d", pt, ptlim); 391 continue; 392 case 'V': /* ISO 8601 week number */ 393 case 'G': /* ISO 8601 year (four digits) */ 394 case 'g': /* ISO 8601 year (two digits) */ 395 /* 396 ** From Arnold Robbins' strftime version 3.0: "the week number of the 397 ** year (the first Monday as the first day of week 1) as a decimal number 398 ** (01-53)." 399 ** (ado, 1993-05-24) 400 ** 401 ** From <http://www.ft.uni-erlangen.de/~mskuhn/iso-time.html> by Markus Kuhn: 402 ** "Week 01 of a year is per definition the first week which has the 403 ** Thursday in this year, which is equivalent to the week which contains 404 ** the fourth day of January. In other words, the first week of a new year 405 ** is the week which has the majority of its days in the new year. Week 01 406 ** might also contain days from the previous year and the week before week 407 ** 01 of a year is the last week (52 or 53) of the previous year even if 408 ** it contains days from the new year. A week starts with Monday (day 1) 409 ** and ends with Sunday (day 7). For example, the first week of the year 410 ** 1997 lasts from 1996-12-30 to 1997-01-05..." 411 ** (ado, 1996-01-02) 412 */ 413 { 414 int year; 415 int base; 416 int yday; 417 int wday; 418 int w; 419 420 year = t->tm_year; 421 base = TM_YEAR_BASE; 422 yday = t->tm_yday; 423 wday = t->tm_wday; 424 for ( ; ; ) { 425 int len; 426 int bot; 427 int top; 428 429 len = isleap_sum(year, base) ? 430 DAYSPERLYEAR : 431 DAYSPERNYEAR; 432 /* 433 ** What yday (-3 ... 3) does 434 ** the ISO year begin on? 435 */ 436 bot = ((yday + 11 - wday) % 437 DAYSPERWEEK) - 3; 438 /* 439 ** What yday does the NEXT 440 ** ISO year begin on? 441 */ 442 top = bot - 443 (len % DAYSPERWEEK); 444 if (top < -3) 445 top += DAYSPERWEEK; 446 top += len; 447 if (yday >= top) { 448 ++base; 449 w = 1; 450 break; 451 } 452 if (yday >= bot) { 453 w = 1 + ((yday - bot) / 454 DAYSPERWEEK); 455 break; 456 } 457 --base; 458 yday += isleap_sum(year, base) ? 459 DAYSPERLYEAR : 460 DAYSPERNYEAR; 461 } 462 #ifdef XPG4_1994_04_09 463 if ((w == 52 && 464 t->tm_mon == TM_JANUARY) || 465 (w == 1 && 466 t->tm_mon == TM_DECEMBER)) 467 w = 53; 468 #endif /* defined XPG4_1994_04_09 */ 469 if (*format == 'V') 470 pt = _conv(w, getformat(modifier, "%02d", "%2d", "%d", "%02d"), 471 pt, ptlim); 472 else if (*format == 'g') { 473 *warnp = IN_ALL; 474 pt = _yconv(year, base, 475 false, true, 476 pt, ptlim, modifier); 477 } else pt = _yconv(year, base, 478 true, true, 479 pt, ptlim, modifier); 480 } 481 continue; 482 case 'v': 483 /* 484 ** From Arnold Robbins' strftime version 3.0: 485 ** "date as dd-bbb-YYYY" 486 ** (ado, 1993-05-24) 487 */ 488 pt = _fmt("%e-%b-%Y", t, pt, ptlim, warnp); 489 continue; 490 case 'W': 491 pt = _conv((t->tm_yday + DAYSPERWEEK - 492 (t->tm_wday ? 493 (t->tm_wday - 1) : 494 (DAYSPERWEEK - 1))) / DAYSPERWEEK, 495 getformat(modifier, "%02d", "%2d", "%d", "%02d"), pt, ptlim); 496 continue; 497 case 'w': 498 pt = _conv(t->tm_wday, "%d", pt, ptlim); 499 continue; 500 case 'X': 501 pt = _fmt(Locale->X_fmt, t, pt, ptlim, warnp); 502 continue; 503 case 'x': 504 { 505 int warn2 = IN_SOME; 506 507 pt = _fmt(Locale->x_fmt, t, pt, ptlim, &warn2); 508 if (warn2 == IN_ALL) 509 warn2 = IN_THIS; 510 if (warn2 > *warnp) 511 *warnp = warn2; 512 } 513 continue; 514 case 'y': 515 *warnp = IN_ALL; 516 pt = _yconv(t->tm_year, TM_YEAR_BASE, 517 false, true, 518 pt, ptlim, modifier); 519 continue; 520 case 'Y': 521 pt = _yconv(t->tm_year, TM_YEAR_BASE, 522 true, true, 523 pt, ptlim, modifier); 524 continue; 525 case 'Z': 526 #ifdef TM_ZONE 527 // BEGIN: Android-changed. 528 { 529 const char* zone = t->TM_ZONE; 530 if (!zone || !*zone) { 531 // "The value of tm_isdst shall be positive if Daylight Savings Time is 532 // in effect, 0 if Daylight Savings Time is not in effect, and negative 533 // if the information is not available." 534 if (t->tm_isdst == 0) zone = tzname[0]; 535 else if (t->tm_isdst > 0) zone = tzname[1]; 536 537 // "Replaced by the timezone name or abbreviation, or by no bytes if no 538 // timezone information exists." 539 if (!zone || !*zone) zone = ""; 540 } 541 pt = _add(zone, pt, ptlim, modifier); 542 } 543 // END: Android-changed. 544 #else 545 if (t->tm_isdst >= 0) 546 pt = _add(tzname[t->tm_isdst != 0], 547 pt, ptlim); 548 #endif 549 /* 550 ** C99 says that %Z must be replaced by the 551 ** empty string if the time zone is not 552 ** determinable. 553 */ 554 continue; 555 case 'z': 556 { 557 long diff; 558 char const * sign; 559 560 if (t->tm_isdst < 0) 561 continue; 562 #ifdef TM_GMTOFF 563 diff = t->TM_GMTOFF; 564 #else /* !defined TM_GMTOFF */ 565 /* 566 ** C99 says that the UT offset must 567 ** be computed by looking only at 568 ** tm_isdst. This requirement is 569 ** incorrect, since it means the code 570 ** must rely on magic (in this case 571 ** altzone and timezone), and the 572 ** magic might not have the correct 573 ** offset. Doing things correctly is 574 ** tricky and requires disobeying C99; 575 ** see GNU C strftime for details. 576 ** For now, punt and conform to the 577 ** standard, even though it's incorrect. 578 ** 579 ** C99 says that %z must be replaced by the 580 ** empty string if the time zone is not 581 ** determinable, so output nothing if the 582 ** appropriate variables are not available. 583 */ 584 if (t->tm_isdst == 0) 585 #ifdef USG_COMPAT 586 diff = -timezone; 587 #else /* !defined USG_COMPAT */ 588 continue; 589 #endif /* !defined USG_COMPAT */ 590 else 591 #ifdef ALTZONE 592 diff = -altzone; 593 #else /* !defined ALTZONE */ 594 continue; 595 #endif /* !defined ALTZONE */ 596 #endif /* !defined TM_GMTOFF */ 597 if (diff < 0) { 598 sign = "-"; 599 diff = -diff; 600 } else sign = "+"; 601 pt = _add(sign, pt, ptlim, modifier); 602 diff /= SECSPERMIN; 603 diff = (diff / MINSPERHOUR) * 100 + 604 (diff % MINSPERHOUR); 605 pt = _conv(diff, getformat(modifier, "%04d", "%4d", "%d", "%04d"), pt, ptlim); 606 } 607 continue; 608 case '+': 609 pt = _fmt(Locale->date_fmt, t, pt, ptlim, 610 warnp); 611 continue; 612 case '%': 613 /* 614 ** X311J/88-090 (4.12.3.5): if conversion char is 615 ** undefined, behavior is undefined. Print out the 616 ** character itself as printf(3) also does. 617 */ 618 default: 619 break; 620 } 621 } 622 if (pt == ptlim) 623 break; 624 *pt++ = *format; 625 } 626 return pt; 627 } 628 629 static char * 630 _conv(int n, const char *format, char *pt, const char *ptlim) 631 { 632 char buf[INT_STRLEN_MAXIMUM(int) + 1]; 633 634 snprintf(buf, sizeof(buf), format, n); 635 return _add(buf, pt, ptlim, 0); 636 } 637 638 static char * 639 _add(const char *str, char *pt, const char *const ptlim, int modifier) 640 { 641 int c; 642 643 switch (modifier) { 644 case FORCE_LOWER_CASE: 645 while (pt < ptlim && (*pt = tolower(*str++)) != '\0') { 646 ++pt; 647 } 648 break; 649 650 case '^': 651 while (pt < ptlim && (*pt = toupper(*str++)) != '\0') { 652 ++pt; 653 } 654 break; 655 656 case '#': 657 while (pt < ptlim && (c = *str++) != '\0') { 658 if (isupper(c)) { 659 c = tolower(c); 660 } else if (islower(c)) { 661 c = toupper(c); 662 } 663 *pt = c; 664 ++pt; 665 } 666 667 break; 668 669 default: 670 while (pt < ptlim && (*pt = *str++) != '\0') { 671 ++pt; 672 } 673 } 674 675 return pt; 676 } 677 678 /* 679 ** POSIX and the C Standard are unclear or inconsistent about 680 ** what %C and %y do if the year is negative or exceeds 9999. 681 ** Use the convention that %C concatenated with %y yields the 682 ** same output as %Y, and that %Y contains at least 4 bytes, 683 ** with more only if necessary. 684 */ 685 686 static char * 687 _yconv(int a, int b, bool convert_top, bool convert_yy, 688 char *pt, const char *ptlim, int modifier) 689 { 690 register int lead; 691 register int trail; 692 693 #define DIVISOR 100 694 trail = a % DIVISOR + b % DIVISOR; 695 lead = a / DIVISOR + b / DIVISOR + trail / DIVISOR; 696 trail %= DIVISOR; 697 if (trail < 0 && lead > 0) { 698 trail += DIVISOR; 699 --lead; 700 } else if (lead < 0 && trail > 0) { 701 trail -= DIVISOR; 702 ++lead; 703 } 704 if (convert_top) { 705 if (lead == 0 && trail < 0) 706 pt = _add("-0", pt, ptlim, modifier); 707 else pt = _conv(lead, getformat(modifier, "%02d", "%2d", "%d", "%02d"), pt, ptlim); 708 } 709 if (convert_yy) 710 pt = _conv(((trail < 0) ? -trail : trail), getformat(modifier, "%02d", "%2d", "%d", "%02d"), pt, ptlim); 711 return pt; 712 } 713