]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - lib/libc/stdtime/strftime.c
Import device-tree files from Linux 6.1
[FreeBSD/FreeBSD.git] / lib / libc / stdtime / strftime.c
1 /*
2  * Copyright (c) 1989 The Regents of the University of California.
3  * All rights reserved.
4  *
5  * Copyright (c) 2011 The FreeBSD Foundation
6  *
7  * Portions of this software were developed by David Chisnall
8  * under sponsorship from the FreeBSD Foundation.
9  *
10  * Redistribution and use in source and binary forms are permitted
11  * provided that the above copyright notice and this paragraph are
12  * duplicated in all such forms and that any documentation,
13  * advertising materials, and other materials related to such
14  * distribution and use acknowledge that the software was developed
15  * by the University of California, Berkeley. The name of the
16  * University may not be used to endorse or promote products derived
17  * from this software without specific prior written permission.
18  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
19  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
20  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
21  */
22
23 #ifndef lint
24 #ifndef NOID
25 static const char       elsieid[] = "@(#)strftime.3     8.3";
26 /*
27  * Based on the UCB version with the ID appearing below.
28  * This is ANSIish only when "multibyte character == plain character".
29  */
30 #endif /* !defined NOID */
31 #endif /* !defined lint */
32
33 #include "namespace.h"
34 #include "private.h"
35
36 #if defined(LIBC_SCCS) && !defined(lint)
37 static const char       sccsid[] = "@(#)strftime.c      5.4 (Berkeley) 3/14/89";
38 #endif /* LIBC_SCCS and not lint */
39 #include <sys/cdefs.h>
40 __FBSDID("$FreeBSD$");
41
42 #include "tzfile.h"
43 #include <fcntl.h>
44 #include <sys/stat.h>
45 #include <stdio.h>
46 #include "un-namespace.h"
47 #include "timelocal.h"
48
49 static char *   _add(const char *, char *, const char *);
50 static char *   _conv(int, const char *, char *, const char *, locale_t);
51 static char *   _fmt(const char *, const struct tm *, char *, const char *,
52                         int *, locale_t);
53 static char *   _yconv(int, int, int, int, char *, const char *, locale_t);
54
55 extern char *   tzname[];
56
57 #ifndef YEAR_2000_NAME
58 #define YEAR_2000_NAME  "CHECK_STRFTIME_FORMATS_FOR_TWO_DIGIT_YEARS"
59 #endif /* !defined YEAR_2000_NAME */
60
61 #define IN_NONE 0
62 #define IN_SOME 1
63 #define IN_THIS 2
64 #define IN_ALL  3
65
66 #define PAD_DEFAULT     0
67 #define PAD_LESS        1
68 #define PAD_SPACE       2
69 #define PAD_ZERO        3
70
71 static const char fmt_padding[][4][5] = {
72         /* DEFAULT,     LESS,   SPACE,  ZERO */
73 #define PAD_FMT_MONTHDAY        0
74 #define PAD_FMT_HMS             0
75 #define PAD_FMT_CENTURY         0
76 #define PAD_FMT_SHORTYEAR       0
77 #define PAD_FMT_MONTH           0
78 #define PAD_FMT_WEEKOFYEAR      0
79 #define PAD_FMT_DAYOFMONTH      0
80         { "%02d",       "%d",   "%2d",  "%02d" },
81 #define PAD_FMT_SDAYOFMONTH     1
82 #define PAD_FMT_SHMS            1
83         { "%2d",        "%d",   "%2d",  "%02d" },
84 #define PAD_FMT_DAYOFYEAR       2
85         { "%03d",       "%d",   "%3d",  "%03d" },
86 #define PAD_FMT_YEAR            3
87         { "%04d",       "%d",   "%4d",  "%04d" }
88 };
89
90 size_t
91 strftime_l(char * __restrict s, size_t maxsize, const char * __restrict format,
92     const struct tm * __restrict t, locale_t loc)
93 {
94         char *  p;
95         int     warn;
96         FIX_LOCALE(loc);
97
98         tzset();
99         warn = IN_NONE;
100         p = _fmt(((format == NULL) ? "%c" : format), t, s, s + maxsize, &warn, loc);
101 #ifndef NO_RUN_TIME_WARNINGS_ABOUT_YEAR_2000_PROBLEMS_THANK_YOU
102         if (warn != IN_NONE && getenv(YEAR_2000_NAME) != NULL) {
103                 (void) fprintf_l(stderr, loc, "\n");
104                 if (format == NULL)
105                         (void) fputs("NULL strftime format ", stderr);
106                 else    (void) fprintf_l(stderr, loc, "strftime format \"%s\" ",
107                                 format);
108                 (void) fputs("yields only two digits of years in ", stderr);
109                 if (warn == IN_SOME)
110                         (void) fputs("some locales", stderr);
111                 else if (warn == IN_THIS)
112                         (void) fputs("the current locale", stderr);
113                 else    (void) fputs("all locales", stderr);
114                 (void) fputs("\n", stderr);
115         }
116 #endif /* !defined NO_RUN_TIME_WARNINGS_ABOUT_YEAR_2000_PROBLEMS_THANK_YOU */
117         if (p == s + maxsize)
118                 return (0);
119         *p = '\0';
120         return p - s;
121 }
122
123 size_t
124 strftime(char * __restrict s, size_t maxsize, const char * __restrict format,
125     const struct tm * __restrict t)
126 {
127         return strftime_l(s, maxsize, format, t, __get_locale());
128 }
129
130 static char *
131 _fmt(const char *format, const struct tm * const t, char *pt,
132     const char * const ptlim, int *warnp, locale_t loc)
133 {
134         int Ealternative, Oalternative, PadIndex;
135         struct lc_time_T *tptr = __get_current_time_locale(loc);
136
137         for ( ; *format; ++format) {
138                 if (*format == '%') {
139                         Ealternative = 0;
140                         Oalternative = 0;
141                         PadIndex         = PAD_DEFAULT;
142 label:
143                         switch (*++format) {
144                         case '\0':
145                                 --format;
146                                 break;
147                         case 'A':
148                                 pt = _add((t->tm_wday < 0 ||
149                                         t->tm_wday >= DAYSPERWEEK) ?
150                                         "?" : tptr->weekday[t->tm_wday],
151                                         pt, ptlim);
152                                 continue;
153                         case 'a':
154                                 pt = _add((t->tm_wday < 0 ||
155                                         t->tm_wday >= DAYSPERWEEK) ?
156                                         "?" : tptr->wday[t->tm_wday],
157                                         pt, ptlim);
158                                 continue;
159                         case 'B':
160                                 pt = _add((t->tm_mon < 0 ||
161                                         t->tm_mon >= MONSPERYEAR) ?
162                                         "?" : (Oalternative ? tptr->alt_month :
163                                         tptr->month)[t->tm_mon],
164                                         pt, ptlim);
165                                 continue;
166                         case 'b':
167                         case 'h':
168                                 pt = _add((t->tm_mon < 0 ||
169                                         t->tm_mon >= MONSPERYEAR) ?
170                                         "?" : tptr->mon[t->tm_mon],
171                                         pt, ptlim);
172                                 continue;
173                         case 'C':
174                                 /*
175                                  * %C used to do a...
176                                  *      _fmt("%a %b %e %X %Y", t);
177                                  * ...whereas now POSIX 1003.2 calls for
178                                  * something completely different.
179                                  * (ado, 1993-05-24)
180                                  */
181                                 pt = _yconv(t->tm_year, TM_YEAR_BASE, 1, 0,
182                                         pt, ptlim, loc);
183                                 continue;
184                         case 'c':
185                                 {
186                                 int warn2 = IN_SOME;
187
188                                 pt = _fmt(tptr->c_fmt, t, pt, ptlim, &warn2, loc);
189                                 if (warn2 == IN_ALL)
190                                         warn2 = IN_THIS;
191                                 if (warn2 > *warnp)
192                                         *warnp = warn2;
193                                 }
194                                 continue;
195                         case 'D':
196                                 pt = _fmt("%m/%d/%y", t, pt, ptlim, warnp, loc);
197                                 continue;
198                         case 'd':
199                                 pt = _conv(t->tm_mday,
200                                         fmt_padding[PAD_FMT_DAYOFMONTH][PadIndex],
201                                         pt, ptlim, loc);
202                                 continue;
203                         case 'E':
204                                 if (Ealternative || Oalternative)
205                                         break;
206                                 Ealternative++;
207                                 goto label;
208                         case 'O':
209                                 /*
210                                  * C99 locale modifiers.
211                                  * The sequences
212                                  *      %Ec %EC %Ex %EX %Ey %EY
213                                  *      %Od %oe %OH %OI %Om %OM
214                                  *      %OS %Ou %OU %OV %Ow %OW %Oy
215                                  * are supposed to provide alternate
216                                  * representations.
217                                  *
218                                  * FreeBSD extension
219                                  *      %OB
220                                  */
221                                 if (Ealternative || Oalternative)
222                                         break;
223                                 Oalternative++;
224                                 goto label;
225                         case 'e':
226                                 pt = _conv(t->tm_mday,
227                                         fmt_padding[PAD_FMT_SDAYOFMONTH][PadIndex],
228                                         pt, ptlim, loc);
229                                 continue;
230                         case 'F':
231                                 pt = _fmt("%Y-%m-%d", t, pt, ptlim, warnp, loc);
232                                 continue;
233                         case 'H':
234                                 pt = _conv(t->tm_hour, fmt_padding[PAD_FMT_HMS][PadIndex],
235                                         pt, ptlim, loc);
236                                 continue;
237                         case 'I':
238                                 pt = _conv((t->tm_hour % 12) ?
239                                         (t->tm_hour % 12) : 12,
240                                         fmt_padding[PAD_FMT_HMS][PadIndex],
241                                         pt, ptlim, loc);
242                                 continue;
243                         case 'j':
244                                 pt = _conv(t->tm_yday + 1,
245                                         fmt_padding[PAD_FMT_DAYOFYEAR][PadIndex],
246                                         pt, ptlim, loc);
247                                 continue;
248                         case 'k':
249                                 /*
250                                  * This used to be...
251                                  *      _conv(t->tm_hour % 12 ?
252                                  *              t->tm_hour % 12 : 12, 2, ' ');
253                                  * ...and has been changed to the below to
254                                  * match SunOS 4.1.1 and Arnold Robbins'
255                                  * strftime version 3.0. That is, "%k" and
256                                  * "%l" have been swapped.
257                                  * (ado, 1993-05-24)
258                                  */
259                                 pt = _conv(t->tm_hour, fmt_padding[PAD_FMT_SHMS][PadIndex],
260                                         pt, ptlim, loc);
261                                 continue;
262 #ifdef KITCHEN_SINK
263                         case 'K':
264                                 /*
265                                 ** After all this time, still unclaimed!
266                                 */
267                                 pt = _add("kitchen sink", pt, ptlim);
268                                 continue;
269 #endif /* defined KITCHEN_SINK */
270                         case 'l':
271                                 /*
272                                  * This used to be...
273                                  *      _conv(t->tm_hour, 2, ' ');
274                                  * ...and has been changed to the below to
275                                  * match SunOS 4.1.1 and Arnold Robbin's
276                                  * strftime version 3.0. That is, "%k" and
277                                  * "%l" have been swapped.
278                                  * (ado, 1993-05-24)
279                                  */
280                                 pt = _conv((t->tm_hour % 12) ?
281                                         (t->tm_hour % 12) : 12,
282                                         fmt_padding[PAD_FMT_SHMS][PadIndex],
283                                         pt, ptlim, loc);
284                                 continue;
285                         case 'M':
286                                 pt = _conv(t->tm_min, fmt_padding[PAD_FMT_HMS][PadIndex],
287                                         pt, ptlim, loc);
288                                 continue;
289                         case 'm':
290                                 pt = _conv(t->tm_mon + 1,
291                                         fmt_padding[PAD_FMT_MONTH][PadIndex],
292                                         pt, ptlim, loc);
293                                 continue;
294                         case 'n':
295                                 pt = _add("\n", pt, ptlim);
296                                 continue;
297                         case 'p':
298                                 pt = _add((t->tm_hour >= (HOURSPERDAY / 2)) ?
299                                         tptr->pm : tptr->am,
300                                         pt, ptlim);
301                                 continue;
302                         case 'R':
303                                 pt = _fmt("%H:%M", t, pt, ptlim, warnp, loc);
304                                 continue;
305                         case 'r':
306                                 pt = _fmt(tptr->ampm_fmt, t, pt, ptlim,
307                                         warnp, loc);
308                                 continue;
309                         case 'S':
310                                 pt = _conv(t->tm_sec, fmt_padding[PAD_FMT_HMS][PadIndex],
311                                         pt, ptlim, loc);
312                                 continue;
313                         case 's':
314                                 {
315                                         struct tm       tm;
316                                         char            buf[INT_STRLEN_MAXIMUM(
317                                                                 time_t) + 1];
318                                         time_t          mkt;
319
320                                         tm = *t;
321                                         mkt = mktime(&tm);
322                                         if (TYPE_SIGNED(time_t))
323                                                 (void) sprintf_l(buf, loc, "%ld",
324                                                         (long) mkt);
325                                         else    (void) sprintf_l(buf, loc, "%lu",
326                                                         (unsigned long) mkt);
327                                         pt = _add(buf, pt, ptlim);
328                                 }
329                                 continue;
330                         case 'T':
331                                 pt = _fmt("%H:%M:%S", t, pt, ptlim, warnp, loc);
332                                 continue;
333                         case 't':
334                                 pt = _add("\t", pt, ptlim);
335                                 continue;
336                         case 'U':
337                                 pt = _conv((t->tm_yday + DAYSPERWEEK -
338                                         t->tm_wday) / DAYSPERWEEK,
339                                         fmt_padding[PAD_FMT_WEEKOFYEAR][PadIndex],
340                                         pt, ptlim, loc);
341                                 continue;
342                         case 'u':
343                                 /*
344                                  * From Arnold Robbins' strftime version 3.0:
345                                  * "ISO 8601: Weekday as a decimal number
346                                  * [1 (Monday) - 7]"
347                                  * (ado, 1993-05-24)
348                                  */
349                                 pt = _conv((t->tm_wday == 0) ?
350                                         DAYSPERWEEK : t->tm_wday,
351                                         "%d", pt, ptlim, loc);
352                                 continue;
353                         case 'V':       /* ISO 8601 week number */
354                         case 'G':       /* ISO 8601 year (four digits) */
355                         case 'g':       /* ISO 8601 year (two digits) */
356 /*
357  * From Arnold Robbins' strftime version 3.0: "the week number of the
358  * year (the first Monday as the first day of week 1) as a decimal number
359  * (01-53)."
360  * (ado, 1993-05-24)
361  *
362  * From "http://www.ft.uni-erlangen.de/~mskuhn/iso-time.html" by Markus Kuhn:
363  * "Week 01 of a year is per definition the first week which has the
364  * Thursday in this year, which is equivalent to the week which contains
365  * the fourth day of January. In other words, the first week of a new year
366  * is the week which has the majority of its days in the new year. Week 01
367  * might also contain days from the previous year and the week before week
368  * 01 of a year is the last week (52 or 53) of the previous year even if
369  * it contains days from the new year. A week starts with Monday (day 1)
370  * and ends with Sunday (day 7). For example, the first week of the year
371  * 1997 lasts from 1996-12-30 to 1997-01-05..."
372  * (ado, 1996-01-02)
373  */
374                                 {
375                                         int     year;
376                                         int     base;
377                                         int     yday;
378                                         int     wday;
379                                         int     w;
380
381                                         year = t->tm_year;
382                                         base = TM_YEAR_BASE;
383                                         yday = t->tm_yday;
384                                         wday = t->tm_wday;
385                                         for ( ; ; ) {
386                                                 int     len;
387                                                 int     bot;
388                                                 int     top;
389
390                                                 len = isleap_sum(year, base) ?
391                                                         DAYSPERLYEAR :
392                                                         DAYSPERNYEAR;
393                                                 /*
394                                                  * What yday (-3 ... 3) does
395                                                  * the ISO year begin on?
396                                                  */
397                                                 bot = ((yday + 11 - wday) %
398                                                         DAYSPERWEEK) - 3;
399                                                 /*
400                                                  * What yday does the NEXT
401                                                  * ISO year begin on?
402                                                  */
403                                                 top = bot -
404                                                         (len % DAYSPERWEEK);
405                                                 if (top < -3)
406                                                         top += DAYSPERWEEK;
407                                                 top += len;
408                                                 if (yday >= top) {
409                                                         ++base;
410                                                         w = 1;
411                                                         break;
412                                                 }
413                                                 if (yday >= bot) {
414                                                         w = 1 + ((yday - bot) /
415                                                                 DAYSPERWEEK);
416                                                         break;
417                                                 }
418                                                 --base;
419                                                 yday += isleap_sum(year, base) ?
420                                                         DAYSPERLYEAR :
421                                                         DAYSPERNYEAR;
422                                         }
423 #ifdef XPG4_1994_04_09
424                                         if ((w == 52 &&
425                                                 t->tm_mon == TM_JANUARY) ||
426                                                 (w == 1 &&
427                                                 t->tm_mon == TM_DECEMBER))
428                                                         w = 53;
429 #endif /* defined XPG4_1994_04_09 */
430                                         if (*format == 'V')
431                                                 pt = _conv(w, fmt_padding[PAD_FMT_WEEKOFYEAR][PadIndex],
432                                                         pt, ptlim, loc);
433                                         else if (*format == 'g') {
434                                                 *warnp = IN_ALL;
435                                                 pt = _yconv(year, base, 0, 1,
436                                                         pt, ptlim, loc);
437                                         } else  pt = _yconv(year, base, 1, 1,
438                                                         pt, ptlim, loc);
439                                 }
440                                 continue;
441                         case 'v':
442                                 /*
443                                  * From Arnold Robbins' strftime version 3.0:
444                                  * "date as dd-bbb-YYYY"
445                                  * (ado, 1993-05-24)
446                                  */
447                                 pt = _fmt("%e-%b-%Y", t, pt, ptlim, warnp, loc);
448                                 continue;
449                         case 'W':
450                                 pt = _conv((t->tm_yday + DAYSPERWEEK -
451                                         (t->tm_wday ?
452                                         (t->tm_wday - 1) :
453                                         (DAYSPERWEEK - 1))) / DAYSPERWEEK,
454                                         fmt_padding[PAD_FMT_WEEKOFYEAR][PadIndex],
455                                         pt, ptlim, loc);
456                                 continue;
457                         case 'w':
458                                 pt = _conv(t->tm_wday, "%d", pt, ptlim, loc);
459                                 continue;
460                         case 'X':
461                                 pt = _fmt(tptr->X_fmt, t, pt, ptlim, warnp, loc);
462                                 continue;
463                         case 'x':
464                                 {
465                                 int     warn2 = IN_SOME;
466
467                                 pt = _fmt(tptr->x_fmt, t, pt, ptlim, &warn2, loc);
468                                 if (warn2 == IN_ALL)
469                                         warn2 = IN_THIS;
470                                 if (warn2 > *warnp)
471                                         *warnp = warn2;
472                                 }
473                                 continue;
474                         case 'y':
475                                 *warnp = IN_ALL;
476                                 pt = _yconv(t->tm_year, TM_YEAR_BASE, 0, 1,
477                                         pt, ptlim, loc);
478                                 continue;
479                         case 'Y':
480                                 pt = _yconv(t->tm_year, TM_YEAR_BASE, 1, 1,
481                                         pt, ptlim, loc);
482                                 continue;
483                         case 'Z':
484 #ifdef TM_ZONE
485                                 if (t->TM_ZONE != NULL)
486                                         pt = _add(t->TM_ZONE, pt, ptlim);
487                                 else
488 #endif /* defined TM_ZONE */
489                                 if (t->tm_isdst >= 0)
490                                         pt = _add(tzname[t->tm_isdst != 0],
491                                                 pt, ptlim);
492                                 /*
493                                  * C99 says that %Z must be replaced by the
494                                  * empty string if the time zone is not
495                                  * determinable.
496                                  */
497                                 continue;
498                         case 'z':
499                                 {
500                                 int             diff;
501                                 char const *    sign;
502
503                                 if (t->tm_isdst < 0)
504                                         continue;
505 #ifdef TM_GMTOFF
506                                 diff = t->TM_GMTOFF;
507 #else /* !defined TM_GMTOFF */
508                                 /*
509                                  * C99 says that the UTC offset must
510                                  * be computed by looking only at
511                                  * tm_isdst. This requirement is
512                                  * incorrect, since it means the code
513                                  * must rely on magic (in this case
514                                  * altzone and timezone), and the
515                                  * magic might not have the correct
516                                  * offset. Doing things correctly is
517                                  * tricky and requires disobeying C99;
518                                  * see GNU C strftime for details.
519                                  * For now, punt and conform to the
520                                  * standard, even though it's incorrect.
521                                  *
522                                  * C99 says that %z must be replaced by the
523                                  * empty string if the time zone is not
524                                  * determinable, so output nothing if the
525                                  * appropriate variables are not available.
526                                  */
527                                 if (t->tm_isdst == 0)
528 #ifdef USG_COMPAT
529                                         diff = -timezone;
530 #else /* !defined USG_COMPAT */
531                                         continue;
532 #endif /* !defined USG_COMPAT */
533                                 else
534 #ifdef ALTZONE
535                                         diff = -altzone;
536 #else /* !defined ALTZONE */
537                                         continue;
538 #endif /* !defined ALTZONE */
539 #endif /* !defined TM_GMTOFF */
540                                 if (diff < 0) {
541                                         sign = "-";
542                                         diff = -diff;
543                                 } else
544                                         sign = "+";
545                                 pt = _add(sign, pt, ptlim);
546                                 diff /= SECSPERMIN;
547                                 diff = (diff / MINSPERHOUR) * 100 +
548                                         (diff % MINSPERHOUR);
549                                 pt = _conv(diff,
550                                         fmt_padding[PAD_FMT_YEAR][PadIndex],
551                                         pt, ptlim, loc);
552                                 }
553                                 continue;
554                         case '+':
555                                 pt = _fmt(tptr->date_fmt, t, pt, ptlim,
556                                         warnp, loc);
557                                 continue;
558                         case '-':
559                                 if (PadIndex != PAD_DEFAULT)
560                                         break;
561                                 PadIndex = PAD_LESS;
562                                 goto label;
563                         case '_':
564                                 if (PadIndex != PAD_DEFAULT)
565                                         break;
566                                 PadIndex = PAD_SPACE;
567                                 goto label;
568                         case '0':
569                                 if (PadIndex != PAD_DEFAULT)
570                                         break;
571                                 PadIndex = PAD_ZERO;
572                                 goto label;
573                         case '%':
574                         /*
575                          * X311J/88-090 (4.12.3.5): if conversion char is
576                          * undefined, behavior is undefined. Print out the
577                          * character itself as printf(3) also does.
578                          */
579                         default:
580                                 break;
581                         }
582                 }
583                 if (pt == ptlim)
584                         break;
585                 *pt++ = *format;
586         }
587         return (pt);
588 }
589
590 static char *
591 _conv(const int n, const char * const format, char * const pt,
592     const char * const ptlim, locale_t  loc)
593 {
594         char    buf[INT_STRLEN_MAXIMUM(int) + 1];
595
596         (void) sprintf_l(buf, loc, format, n);
597         return _add(buf, pt, ptlim);
598 }
599
600 static char *
601 _add(const char *str, char *pt, const char * const ptlim)
602 {
603         while (pt < ptlim && (*pt = *str++) != '\0')
604                 ++pt;
605         return (pt);
606 }
607
608 /*
609  * POSIX and the C Standard are unclear or inconsistent about
610  * what %C and %y do if the year is negative or exceeds 9999.
611  * Use the convention that %C concatenated with %y yields the
612  * same output as %Y, and that %Y contains at least 4 bytes,
613  * with more only if necessary.
614  */
615
616 static char *
617 _yconv(const int a, const int b, const int convert_top, const int convert_yy,
618     char *pt, const char * const ptlim, locale_t  loc)
619 {
620         register int    lead;
621         register int    trail;
622
623 #define DIVISOR 100
624         trail = a % DIVISOR + b % DIVISOR;
625         lead = a / DIVISOR + b / DIVISOR + trail / DIVISOR;
626         trail %= DIVISOR;
627         if (trail < 0 && lead > 0) {
628                 trail += DIVISOR;
629                 --lead;
630         } else if (lead < 0 && trail > 0) {
631                 trail -= DIVISOR;
632                 ++lead;
633         }
634         if (convert_top) {
635                 if (lead == 0 && trail < 0)
636                         pt = _add("-0", pt, ptlim);
637                 else    pt = _conv(lead, "%02d", pt, ptlim, loc);
638         }
639         if (convert_yy)
640                 pt = _conv(((trail < 0) ? -trail : trail), "%02d", pt,
641                      ptlim, loc);
642         return (pt);
643 }