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