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