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