update from main archive 961113
[glibc.git] / time / strftime.c
blob2c5280dd16d88beb7c992304e93afe5fad935fa9
1 /* Copyright (C) 1991, 92, 93, 94, 95, 96 Free Software Foundation, Inc.
2 This file is part of the GNU C Library.
4 The GNU C Library is free software; you can redistribute it and/or
5 modify it under the terms of the GNU Library General Public License as
6 published by the Free Software Foundation; either version 2 of the
7 License, or (at your option) any later version.
9 The GNU C Library is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 Library General Public License for more details.
14 You should have received a copy of the GNU Library General Public
15 License along with the GNU C Library; see the file COPYING.LIB. If not,
16 write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17 Boston, MA 02111-1307, USA. */
19 #ifdef HAVE_CONFIG_H
20 # include <config.h>
21 #endif
23 #ifdef _LIBC
24 # define HAVE_LIMITS_H 1
25 # define HAVE_MBLEN 1
26 # define HAVE_MBRLEN 1
27 # define HAVE_STRUCT_ERA_ENTRY 1
28 # define HAVE_TM_GMTOFF 1
29 # define HAVE_TM_ZONE 1
30 # define MULTIBYTE_IS_FORMAT_SAFE 1
31 # define STDC_HEADERS 1
32 # include <ansidecl.h>
33 # include "../locale/localeinfo.h"
34 #endif
36 #include <sys/types.h> /* Some systems define `time_t' here. */
38 #ifdef TIME_WITH_SYS_TIME
39 # include <sys/time.h>
40 # include <time.h>
41 #else
42 # ifdef HAVE_SYS_TIME_H
43 # include <sys/time.h>
44 # else
45 # include <time.h>
46 # endif
47 #endif
49 /* Do multibyte processing if multibytes are supported, unless
50 multibyte sequences are safe in formats. Multibyte sequences are
51 safe if they cannot contain byte sequences that look like format
52 conversion specifications. The GNU C Library uses UTF8 multibyte
53 encoding, which is safe for formats, but strftime.c can be used
54 with other C libraries that use unsafe encodings. */
55 #define DO_MULTIBYTE (HAVE_MBLEN && ! MULTIBYTE_IS_FORMAT_SAFE)
57 #if DO_MULTIBYTE
58 # if HAVE_MBRLEN
59 # include <wchar.h>
60 # else
61 /* Simulate mbrlen with mblen as best we can. */
62 # define mbstate_t int
63 # define mbrlen(s, n, ps) mblen (s, n)
64 # define mbsinit(ps) (*(ps) == 0)
65 # endif
66 static const mbstate_t mbstate_zero;
67 #endif
69 #if HAVE_LIMITS_H
70 # include <limits.h>
71 #endif
73 #if STDC_HEADERS
74 # include <stddef.h>
75 # include <stdlib.h>
76 # include <string.h>
77 #else
78 # define memcpy(d, s, n) bcopy (s, d, n)
79 #endif
81 #ifndef __P
82 #if defined (__GNUC__) || (defined (__STDC__) && __STDC__)
83 #define __P(args) args
84 #else
85 #define __P(args) ()
86 #endif /* GCC. */
87 #endif /* Not __P. */
89 #ifndef PTR
90 #ifdef __STDC__
91 #define PTR void *
92 #else
93 #define PTR char *
94 #endif
95 #endif
97 #ifndef CHAR_BIT
98 #define CHAR_BIT 8
99 #endif
101 #define TYPE_SIGNED(t) ((t) -1 < 0)
103 /* Bound on length of the string representing an integer value of type t.
104 Subtract one for the sign bit if t is signed;
105 302 / 1000 is log10 (2) rounded up;
106 add one for integer division truncation;
107 add one more for a minus sign if t is signed. */
108 #define INT_STRLEN_BOUND(t) \
109 ((sizeof (t) * CHAR_BIT - TYPE_SIGNED (t)) * 302 / 100 + 1 + TYPE_SIGNED (t))
111 #define TM_YEAR_BASE 1900
113 #ifndef __isleap
114 /* Nonzero if YEAR is a leap year (every 4 years,
115 except every 100th isn't, and every 400th is). */
116 #define __isleap(year) \
117 ((year) % 4 == 0 && ((year) % 100 != 0 || (year) % 400 == 0))
118 #endif
121 #ifdef _LIBC
122 # define gmtime_r __gmtime_r
123 # define localtime_r __localtime_r
124 #else
125 # if ! HAVE_LOCALTIME_R
126 # if ! HAVE_TM_GMTOFF
127 /* Approximate gmtime_r as best we can in its absence. */
128 #define gmtime_r my_gmtime_r
129 static struct tm *gmtime_r __P ((const time_t *, struct tm *));
130 static struct tm *
131 gmtime_r (t, tp)
132 const time_t *t;
133 struct tm *tp;
135 struct tm *l = gmtime (t);
136 if (! l)
137 return 0;
138 *tp = *l;
139 return tp;
141 # endif /* ! HAVE_TM_GMTOFF */
143 /* Approximate localtime_r as best we can in its absence. */
144 #define localtime_r my_localtime_r
145 static struct tm *localtime_r __P ((const time_t *, struct tm *));
146 static struct tm *
147 localtime_r (t, tp)
148 const time_t *t;
149 struct tm *tp;
151 struct tm *l = localtime (t);
152 if (! l)
153 return 0;
154 *tp = *l;
155 return tp;
157 # endif /* ! HAVE_LOCALTIME_R */
158 #endif /* ! defined (_LIBC) */
161 #define add(n, f) \
162 do \
164 i += (n); \
165 if (i >= maxsize) \
166 return 0; \
167 else \
168 if (p) \
170 f; \
171 p += (n); \
173 } while (0)
174 #define cpy(n, s) add ((n), memcpy((PTR) p, (PTR) (s), (n)))
176 #if ! HAVE_TM_GMTOFF
177 /* Yield the difference between *A and *B,
178 measured in seconds, ignoring leap seconds. */
179 static int tm_diff __P ((const struct tm *, const struct tm *));
180 static int
181 tm_diff (a, b)
182 const struct tm *a;
183 const struct tm *b;
185 /* Compute intervening leap days correctly even if year is negative.
186 Take care to avoid int overflow in leap day calculations,
187 but it's OK to assume that A and B are close to each other. */
188 int a4 = (a->tm_year >> 2) + (TM_YEAR_BASE >> 2) - ! (a->tm_year & 3);
189 int b4 = (b->tm_year >> 2) + (TM_YEAR_BASE >> 2) - ! (b->tm_year & 3);
190 int a100 = a4 / 25 - (a4 % 25 < 0);
191 int b100 = b4 / 25 - (b4 % 25 < 0);
192 int a400 = a100 >> 2;
193 int b400 = b100 >> 2;
194 int intervening_leap_days = (a4 - b4) - (a100 - b100) + (a400 - b400);
195 int years = a->tm_year - b->tm_year;
196 int days = (365 * years + intervening_leap_days
197 + (a->tm_yday - b->tm_yday));
198 return (60 * (60 * (24 * days + (a->tm_hour - b->tm_hour))
199 + (a->tm_min - b->tm_min))
200 + (a->tm_sec - b->tm_sec));
202 #endif /* ! HAVE_TM_GMTOFF */
206 /* The number of days from the first day of the first ISO week of this
207 year to the year day YDAY with week day WDAY. ISO weeks start on
208 Monday; the first ISO week has the year's first Thursday. YDAY may
209 be as small as YDAY_MINIMUM. */
210 #define ISO_WEEK_START_WDAY 1 /* Monday */
211 #define ISO_WEEK1_WDAY 4 /* Thursday */
212 #define YDAY_MINIMUM (-366)
213 static int iso_week_days __P ((int, int));
214 #ifdef __GNUC__
215 inline
216 #endif
217 static int
218 iso_week_days (yday, wday)
219 int yday;
220 int wday;
222 /* Add enough to the first operand of % to make it nonnegative. */
223 int big_enough_multiple_of_7 = (-YDAY_MINIMUM / 7 + 2) * 7;
224 return (yday
225 - (yday - wday + ISO_WEEK1_WDAY + big_enough_multiple_of_7) % 7
226 + ISO_WEEK1_WDAY - ISO_WEEK_START_WDAY);
230 #ifndef _NL_CURRENT
231 static char const weekday_name[][10] =
233 "Sunday", "Monday", "Tuesday", "Wednesday",
234 "Thursday", "Friday", "Saturday"
236 static char const month_name[][10] =
238 "January", "February", "March", "April", "May", "June",
239 "July", "August", "September", "October", "November", "December"
241 #endif
243 /* Write information from TP into S according to the format
244 string FORMAT, writing no more that MAXSIZE characters
245 (including the terminating '\0') and returning number of
246 characters written. If S is NULL, nothing will be written
247 anywhere, so to determine how many characters would be
248 written, use NULL for S and (size_t) UINT_MAX for MAXSIZE. */
249 size_t
250 strftime (s, maxsize, format, tp)
251 char *s;
252 size_t maxsize;
253 const char *format;
254 register const struct tm *tp;
256 int hour12 = tp->tm_hour;
257 #ifdef _NL_CURRENT
258 const char *const a_wkday = _NL_CURRENT (LC_TIME, ABDAY_1 + tp->tm_wday);
259 const char *const f_wkday = _NL_CURRENT (LC_TIME, DAY_1 + tp->tm_wday);
260 const char *const a_month = _NL_CURRENT (LC_TIME, ABMON_1 + tp->tm_mon);
261 const char *const f_month = _NL_CURRENT (LC_TIME, MON_1 + tp->tm_mon);
262 const char *const ampm = _NL_CURRENT (LC_TIME,
263 hour12 > 11 ? PM_STR : AM_STR);
264 size_t aw_len = strlen (a_wkday);
265 size_t am_len = strlen (a_month);
266 size_t ap_len = strlen (ampm);
267 #else
268 const char *const f_wkday = weekday_name[tp->tm_wday];
269 const char *const f_month = month_name[tp->tm_mon];
270 const char *const a_wkday = f_wkday;
271 const char *const a_month = f_month;
272 const char *const ampm = "AMPM" + 2 * (hour12 > 11);
273 size_t aw_len = 3;
274 size_t am_len = 3;
275 size_t ap_len = 2;
276 #endif
277 size_t wkday_len = strlen (f_wkday);
278 size_t month_len = strlen (f_month);
279 const char *zone;
280 size_t zonelen;
281 register size_t i = 0;
282 register char *p = s;
283 register const char *f;
285 zone = 0;
286 #if HAVE_TM_ZONE
287 zone = (const char *) tp->tm_zone;
288 #endif
289 #if HAVE_TZNAME
290 if (!(zone && *zone) && tp->tm_isdst >= 0)
291 zone = tzname[tp->tm_isdst];
292 #endif
293 if (! zone)
294 zone = ""; /* POSIX.2 requires the empty string here. */
296 zonelen = strlen (zone);
298 if (hour12 > 12)
299 hour12 -= 12;
300 else
301 if (hour12 == 0) hour12 = 12;
303 for (f = format; *f != '\0'; ++f)
305 int pad; /* Padding for number ('-', '_', or 0). */
306 int modifier; /* Field modifier ('E', 'O', or 0). */
307 int digits; /* Max digits for numeric format. */
308 int number_value; /* Numeric value to be printed. */
309 int negative_number; /* 1 if the number is negative. */
310 const char *subfmt;
311 char *bufp;
312 char buf[1 + (sizeof (int) < sizeof (time_t)
313 ? INT_STRLEN_BOUND (time_t)
314 : INT_STRLEN_BOUND (int))];
316 #if DO_MULTIBYTE
318 switch (*f)
320 case '%':
321 break;
323 case '\a': case '\b': case '\t': case '\n':
324 case '\v': case '\f': case '\r':
325 case ' ': case '!': case '"': case '#': case '&': case'\'':
326 case '(': case ')': case '*': case '+': case ',': case '-':
327 case '.': case '/': case '0': case '1': case '2': case '3':
328 case '4': case '5': case '6': case '7': case '8': case '9':
329 case ':': case ';': case '<': case '=': case '>': case '?':
330 case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
331 case 'G': case 'H': case 'I': case 'J': case 'K': case 'L':
332 case 'M': case 'N': case 'O': case 'P': case 'Q': case 'R':
333 case 'S': case 'T': case 'U': case 'V': case 'W': case 'X':
334 case 'Y': case 'Z': case '[': case'\\': case ']': case '^':
335 case '_': case 'a': case 'b': case 'c': case 'd': case 'e':
336 case 'f': case 'g': case 'h': case 'i': case 'j': case 'k':
337 case 'l': case 'm': case 'n': case 'o': case 'p': case 'q':
338 case 'r': case 's': case 't': case 'u': case 'v': case 'w':
339 case 'x': case 'y': case 'z': case '{': case '|': case '}':
340 case '~':
341 /* The C Standard requires these 98 characters (plus '%') to
342 be in the basic execution character set. None of these
343 characters can start a multibyte sequence, so they need
344 not be analyzed further. */
345 add (1, *p = *f);
346 continue;
348 default:
349 /* Copy this multibyte sequence until we reach its end, find
350 an error, or come back to the initial shift state. */
352 mbstate_t mbstate = mbstate_zero;
353 size_t len = 0;
357 size_t bytes = mbrlen (f + len, (size_t) -1, &mbstate);
359 if (bytes == 0)
360 break;
362 if (bytes == (size_t) -2 || bytes == (size_t) -1)
364 len++;
365 break;
368 len += bytes;
370 while (! mbsinit (&mbstate));
372 cpy (len, f);
373 continue;
377 #else /* ! DO_MULTIBYTE */
379 /* Either multibyte encodings are not supported, or they are
380 safe for formats, so any non-'%' byte can be copied through. */
381 if (*f != '%')
383 add (1, *p = *f);
384 continue;
387 #endif /* ! DO_MULTIBYTE */
389 /* Check for flags that can modify a number format. */
390 ++f;
391 switch (*f)
393 case '_':
394 case '-':
395 pad = *f++;
396 break;
398 default:
399 pad = 0;
400 break;
403 /* Check for modifiers. */
404 switch (*f)
406 case 'E':
407 case 'O':
408 modifier = *f++;
409 break;
411 default:
412 modifier = 0;
413 break;
416 /* Now do the specified format. */
417 switch (*f)
419 #define DO_NUMBER(d, v) \
420 digits = d; number_value = v; goto do_number
421 #define DO_NUMBER_SPACEPAD(d, v) \
422 digits = d; number_value = v; goto do_number_spacepad
424 case '%':
425 if (modifier != 0)
426 goto bad_format;
427 add (1, *p = *f);
428 break;
430 case 'a':
431 if (modifier != 0)
432 goto bad_format;
433 cpy (aw_len, a_wkday);
434 break;
436 case 'A':
437 if (modifier != 0)
438 goto bad_format;
439 cpy (wkday_len, f_wkday);
440 break;
442 case 'b':
443 case 'h': /* POSIX.2 extension. */
444 if (modifier != 0)
445 goto bad_format;
446 cpy (am_len, a_month);
447 break;
449 case 'B':
450 if (modifier != 0)
451 goto bad_format;
452 cpy (month_len, f_month);
453 break;
455 case 'c':
456 if (modifier == 'O')
457 goto bad_format;
458 #ifdef _NL_CURRENT
459 if (! (modifier == 'E'
460 && *(subfmt = _NL_CURRENT (LC_TIME, ERA_D_T_FMT)) != '\0'))
461 subfmt = _NL_CURRENT (LC_TIME, D_T_FMT);
462 #else
463 subfmt = "%a %b %e %H:%M:%S %Z %Y";
464 #endif
466 subformat:
468 size_t len = strftime (p, maxsize - i, subfmt, tp);
469 if (len == 0 && *subfmt)
470 return 0;
471 add (len, ;);
473 break;
475 case 'C': /* POSIX.2 extension. */
476 if (modifier == 'O')
477 goto bad_format;
478 #if HAVE_STRUCT_ERA_ENTRY
479 if (modifier == 'E')
481 struct era_entry *era = _nl_get_era_entry (tp);
482 if (era)
484 size_t len = strlen (era->name_fmt);
485 cpy (len, era->name_fmt);
486 break;
489 #endif
491 int year = tp->tm_year + TM_YEAR_BASE;
492 DO_NUMBER (1, year / 100 - (year % 100 < 0));
495 case 'x':
496 if (modifier == 'O')
497 goto bad_format;
498 #ifdef _NL_CURRENT
499 if (! (modifier == 'E'
500 && *(subfmt = _NL_CURRENT (LC_TIME, ERA_D_FMT)) != '\0'))
501 subfmt = _NL_CURRENT (LC_TIME, D_FMT);
502 goto subformat;
503 #endif
504 /* Fall through. */
505 case 'D': /* POSIX.2 extension. */
506 if (modifier != 0)
507 goto bad_format;
508 subfmt = "%m/%d/%y";
509 goto subformat;
511 case 'd':
512 if (modifier == 'E')
513 goto bad_format;
515 DO_NUMBER (2, tp->tm_mday);
517 case 'e': /* POSIX.2 extension. */
518 if (modifier == 'E')
519 goto bad_format;
521 DO_NUMBER_SPACEPAD (2, tp->tm_mday);
523 /* All numeric formats set DIGITS and NUMBER_VALUE and then
524 jump to one of these two labels. */
526 do_number_spacepad:
527 /* Force `_' flag. */
528 pad = '_';
530 do_number:
531 /* Format the number according to the MODIFIER flag. */
533 #ifdef _NL_CURRENT
534 if (modifier == 'O' && 0 <= number_value)
536 /* Get the locale specific alternate representation of
537 the number NUMBER_VALUE. If none exist NULL is returned. */
538 const char *cp = _nl_get_alt_digit (number_value);
540 if (cp != NULL)
542 size_t digitlen = strlen (cp);
543 if (digitlen != 0)
545 cpy (digitlen, cp);
546 break;
550 #endif
552 unsigned int u = number_value;
554 bufp = buf + sizeof (buf);
555 negative_number = number_value < 0;
557 if (negative_number)
558 u = -u;
561 *--bufp = u % 10 + '0';
562 while ((u /= 10) != 0);
565 do_number_sign_and_padding:
566 if (negative_number)
567 *--bufp = '-';
569 if (pad != '-')
571 int padding = digits - (buf + sizeof (buf) - bufp);
573 if (pad == '_')
575 while (0 < padding--)
576 *--bufp = ' ';
578 else
580 bufp += negative_number;
581 while (0 < padding--)
582 *--bufp = '0';
583 if (negative_number)
584 *--bufp = '-';
588 cpy (buf + sizeof (buf) - bufp, bufp);
589 break;
592 case 'H':
593 if (modifier == 'E')
594 goto bad_format;
596 DO_NUMBER (2, tp->tm_hour);
598 case 'I':
599 if (modifier == 'E')
600 goto bad_format;
602 DO_NUMBER (2, hour12);
604 case 'k': /* GNU extension. */
605 if (modifier == 'E')
606 goto bad_format;
608 DO_NUMBER_SPACEPAD (2, tp->tm_hour);
610 case 'l': /* GNU extension. */
611 if (modifier == 'E')
612 goto bad_format;
614 DO_NUMBER_SPACEPAD (2, hour12);
616 case 'j':
617 if (modifier == 'E')
618 goto bad_format;
620 DO_NUMBER (3, 1 + tp->tm_yday);
622 case 'M':
623 if (modifier == 'E')
624 goto bad_format;
626 DO_NUMBER (2, tp->tm_min);
628 case 'm':
629 if (modifier == 'E')
630 goto bad_format;
632 DO_NUMBER (2, tp->tm_mon + 1);
634 case 'n': /* POSIX.2 extension. */
635 add (1, *p = '\n');
636 break;
638 case 'p':
639 cpy (ap_len, ampm);
640 break;
642 case 'R': /* GNU extension. */
643 subfmt = "%H:%M";
644 goto subformat;
646 case 'r': /* POSIX.2 extension. */
647 #ifdef _NL_CURRENT
648 if (*(subfmt = _NL_CURRENT (LC_TIME, T_FMT_AMPM)) == '\0')
649 #endif
650 subfmt = "%I:%M:%S %p";
651 goto subformat;
653 case 'S':
654 if (modifier == 'E')
655 goto bad_format;
657 DO_NUMBER (2, tp->tm_sec);
659 case 's': /* GNU extension. */
661 struct tm ltm;
662 time_t t;
664 ltm = *tp;
665 t = mktime (&ltm);
667 /* Generate string value for T using time_t arithmetic;
668 this works even if sizeof (long) < sizeof (time_t). */
670 bufp = buf + sizeof (buf);
671 negative_number = t < 0;
675 int d = t % 10;
676 t /= 10;
678 if (negative_number)
680 d = -d;
682 /* Adjust if division truncates to minus infinity. */
683 if (0 < -1 % 10 && d < 0)
685 t++;
686 d += 10;
690 *--bufp = d + '0';
692 while (t != 0);
694 digits = 1;
695 goto do_number_sign_and_padding;
698 case 'X':
699 if (modifier == 'O')
700 goto bad_format;
701 #ifdef _NL_CURRENT
702 if (! (modifier == 'E'
703 && *(subfmt = _NL_CURRENT (LC_TIME, ERA_T_FMT)) != '\0'))
704 subfmt = _NL_CURRENT (LC_TIME, T_FMT);
705 goto subformat;
706 #endif
707 /* Fall through. */
708 case 'T': /* POSIX.2 extension. */
709 subfmt = "%H:%M:%S";
710 goto subformat;
712 case 't': /* POSIX.2 extension. */
713 add (1, *p = '\t');
714 break;
716 case 'u': /* POSIX.2 extension. */
717 DO_NUMBER (1, (tp->tm_wday - 1 + 7) % 7 + 1);
719 case 'U':
720 if (modifier == 'E')
721 goto bad_format;
723 DO_NUMBER (2, (tp->tm_yday - tp->tm_wday + 7) / 7);
725 case 'V':
726 case 'g': /* GNU extension. */
727 case 'G': /* GNU extension. */
728 if (modifier == 'E')
729 goto bad_format;
731 int year = tp->tm_year + TM_YEAR_BASE;
732 int days = iso_week_days (tp->tm_yday, tp->tm_wday);
734 if (days < 0)
736 /* This ISO week belongs to the previous year. */
737 year--;
738 days = iso_week_days (tp->tm_yday + (365 + __isleap (year)),
739 tp->tm_wday);
741 else
743 int d = iso_week_days (tp->tm_yday - (365 + __isleap (year)),
744 tp->tm_wday);
745 if (0 <= d)
747 /* This ISO week belongs to the next year. */
748 year++;
749 days = d;
753 switch (*f)
755 case 'g':
756 DO_NUMBER (2, (year % 100 + 100) % 100);
758 case 'G':
759 DO_NUMBER (1, year);
761 default:
762 DO_NUMBER (2, days / 7 + 1);
766 case 'W':
767 if (modifier == 'E')
768 goto bad_format;
770 DO_NUMBER (2, (tp->tm_yday - (tp->tm_wday - 1 + 7) % 7 + 7) / 7);
772 case 'w':
773 if (modifier == 'E')
774 goto bad_format;
776 DO_NUMBER (1, tp->tm_wday);
778 case 'Y':
779 #if HAVE_STRUCT_ERA_ENTRY
780 if (modifier == 'E')
782 struct era_entry *era = _nl_get_era_entry (tp);
783 if (era)
785 subfmt = strchr (era->name_fmt, '\0') + 1;
786 goto subformat;
789 #endif
790 if (modifier == 'O')
791 goto bad_format;
792 else
793 DO_NUMBER (1, tp->tm_year + TM_YEAR_BASE);
795 case 'y':
796 #if HAVE_STRUCT_ERA_ENTRY
797 if (modifier == 'E')
799 struct era_entry *era = _nl_get_era_entry (tp);
800 if (era)
802 int delta = tp->tm_year - era->start_date[0];
803 DO_NUMBER (1, (era->offset
804 + (era->direction == '-' ? -delta : delta)));
807 #endif
808 DO_NUMBER (2, (tp->tm_year % 100 + 100) % 100);
810 case 'Z':
811 cpy (zonelen, zone);
812 break;
814 case 'z': /* GNU extension. */
815 if (tp->tm_isdst < 0)
816 break;
819 int diff;
820 #if HAVE_TM_GMTOFF
821 diff = tp->tm_gmtoff;
822 #else
823 struct tm gtm;
824 struct tm ltm;
825 time_t lt;
827 ltm = *tp;
828 lt = mktime (&ltm);
830 if (lt == (time_t) -1)
832 /* mktime returns -1 for errors, but -1 is also a
833 valid time_t value. Check whether an error really
834 occurred. */
835 struct tm tm;
836 localtime_r (&lt, &tm);
838 if ((ltm.tm_sec ^ tm.tm_sec)
839 | (ltm.tm_min ^ tm.tm_min)
840 | (ltm.tm_hour ^ tm.tm_hour)
841 | (ltm.tm_mday ^ tm.tm_mday)
842 | (ltm.tm_mon ^ tm.tm_mon)
843 | (ltm.tm_year ^ tm.tm_year))
844 break;
847 if (! gmtime_r (&lt, &gtm))
848 break;
850 diff = tm_diff (&ltm, &gtm);
851 #endif
853 if (diff < 0)
855 add (1, *p = '-');
856 diff = -diff;
858 else
859 add (1, *p = '+');
861 diff /= 60;
862 DO_NUMBER (4, (diff / 60) * 100 + diff % 60);
865 case '\0': /* GNU extension: % at end of format. */
866 --f;
867 /* Fall through. */
868 default:
869 /* Unknown format; output the format, including the '%',
870 since this is most likely the right thing to do if a
871 multibyte string has been misparsed. */
872 bad_format:
874 int flen;
875 for (flen = 1; f[1 - flen] != '%'; flen++)
876 continue;
877 cpy (flen, &f[1 - flen]);
879 break;
883 if (p)
884 *p = '\0';
885 return i;