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