new version
[emacs.git] / src / strftime.c
blobabd039f5b24983d9e9393f5a8bf4e05fb927de2a
1 /* Copyright (C) 1991, 92, 93, 94, 95, 96, 97 Free Software Foundation, Inc.
3 NOTE: The canonical source of this file is maintained with the GNU C Library.
4 Bugs can be reported to bug-glibc@prep.ai.mit.edu.
6 This program is free software; you can redistribute it and/or modify it
7 under the terms of the GNU General Public License as published by the
8 Free Software Foundation; either version 2, or (at your option) any
9 later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
19 USA. */
21 #ifdef HAVE_CONFIG_H
22 # include <config.h>
23 #endif
25 #ifdef _LIBC
26 # define HAVE_LIMITS_H 1
27 # define HAVE_MBLEN 1
28 # define HAVE_MBRLEN 1
29 # define HAVE_STRUCT_ERA_ENTRY 1
30 # define HAVE_TM_GMTOFF 1
31 # define HAVE_TM_ZONE 1
32 # define HAVE_TZNAME 1
33 # define HAVE_TZSET 1
34 # define MULTIBYTE_IS_FORMAT_SAFE 1
35 # define STDC_HEADERS 1
36 # include <ansidecl.h>
37 # include "../locale/localeinfo.h"
38 #endif
40 #include <ctype.h>
41 #include <sys/types.h> /* Some systems define `time_t' here. */
43 #ifdef TIME_WITH_SYS_TIME
44 # include <sys/time.h>
45 # include <time.h>
46 #else
47 # ifdef HAVE_SYS_TIME_H
48 # include <sys/time.h>
49 # else
50 # include <time.h>
51 # endif
52 #endif
53 #if HAVE_TZNAME
54 extern char *tzname[];
55 #endif
57 /* Do multibyte processing if multibytes are supported, unless
58 multibyte sequences are safe in formats. Multibyte sequences are
59 safe if they cannot contain byte sequences that look like format
60 conversion specifications. The GNU C Library uses UTF8 multibyte
61 encoding, which is safe for formats, but strftime.c can be used
62 with other C libraries that use unsafe encodings. */
63 #define DO_MULTIBYTE (HAVE_MBLEN && ! MULTIBYTE_IS_FORMAT_SAFE)
65 #if DO_MULTIBYTE
66 # if HAVE_MBRLEN
67 # include <wchar.h>
68 # else
69 /* Simulate mbrlen with mblen as best we can. */
70 # define mbstate_t int
71 # define mbrlen(s, n, ps) mblen (s, n)
72 # define mbsinit(ps) (*(ps) == 0)
73 # endif
74 static const mbstate_t mbstate_zero;
75 #endif
77 #if HAVE_LIMITS_H
78 # include <limits.h>
79 #endif
81 #if STDC_HEADERS
82 # include <stddef.h>
83 # include <stdlib.h>
84 # include <string.h>
85 #else
86 # define memcpy(d, s, n) bcopy ((s), (d), (n))
87 #endif
89 #ifndef __P
90 # if defined (__GNUC__) || (defined (__STDC__) && __STDC__)
91 # define __P(args) args
92 # else
93 # define __P(args) ()
94 # endif /* GCC. */
95 #endif /* Not __P. */
97 #ifndef PTR
98 # ifdef __STDC__
99 # define PTR void *
100 # else
101 # define PTR char *
102 # endif
103 #endif
105 #ifndef CHAR_BIT
106 # define CHAR_BIT 8
107 #endif
109 #ifndef NULL
110 # define NULL 0
111 #endif
113 #define TYPE_SIGNED(t) ((t) -1 < 0)
115 /* Bound on length of the string representing an integer value of type t.
116 Subtract one for the sign bit if t is signed;
117 302 / 1000 is log10 (2) rounded up;
118 add one for integer division truncation;
119 add one more for a minus sign if t is signed. */
120 #define INT_STRLEN_BOUND(t) \
121 ((sizeof (t) * CHAR_BIT - TYPE_SIGNED (t)) * 302 / 100 + 1 + TYPE_SIGNED (t))
123 #define TM_YEAR_BASE 1900
125 #ifndef __isleap
126 /* Nonzero if YEAR is a leap year (every 4 years,
127 except every 100th isn't, and every 400th is). */
128 # define __isleap(year) \
129 ((year) % 4 == 0 && ((year) % 100 != 0 || (year) % 400 == 0))
130 #endif
133 #ifdef _LIBC
134 # define gmtime_r __gmtime_r
135 # define localtime_r __localtime_r
136 extern int __tz_compute __P ((time_t timer, const struct tm *tm));
137 # define tzname __tzname
138 # define tzset __tzset
139 #else
140 # if ! HAVE_LOCALTIME_R
141 # if ! HAVE_TM_GMTOFF
142 /* Approximate gmtime_r as best we can in its absence. */
143 # define gmtime_r my_gmtime_r
144 static struct tm *gmtime_r __P ((const time_t *, struct tm *));
145 static struct tm *
146 gmtime_r (t, tp)
147 const time_t *t;
148 struct tm *tp;
150 struct tm *l = gmtime (t);
151 if (! l)
152 return 0;
153 *tp = *l;
154 return tp;
156 # endif /* ! HAVE_TM_GMTOFF */
158 /* Approximate localtime_r as best we can in its absence. */
159 # define localtime_r my_localtime_r
160 static struct tm *localtime_r __P ((const time_t *, struct tm *));
161 static struct tm *
162 localtime_r (t, tp)
163 const time_t *t;
164 struct tm *tp;
166 struct tm *l = localtime (t);
167 if (! l)
168 return 0;
169 *tp = *l;
170 return tp;
172 # endif /* ! HAVE_LOCALTIME_R */
173 #endif /* ! defined (_LIBC) */
176 #if !defined (memset) && !defined (HAVE_MEMSET) && !defined (_LIBC)
177 /* Some systems lack the `memset' function and we don't want to
178 introduce additional dependencies. */
179 static const char spaces[16] = " ";
180 static const char zeroes[16] = "0000000000000000";
182 # define memset_space(P, Len) \
183 do { \
184 int _len = (Len); \
186 do \
188 int _this = _len > 16 ? 16 : _len; \
189 memcpy ((P), spaces, _this); \
190 (P) += _this; \
191 _len -= _this; \
193 while (_len > 0); \
194 } while (0)
196 # define memset_zero(P, Len) \
197 do { \
198 int _len = (Len); \
200 do \
202 int _this = _len > 16 ? 16 : _len; \
203 memcpy ((P), zeroes, _this); \
204 (P) += _this; \
205 _len -= _this; \
207 while (_len > 0); \
208 } while (0)
209 #else
210 # define memset_space(P, Len) (memset ((P), ' ', (Len)), (P) += (Len))
211 # define memset_zero(P, Len) (memset ((P), '0', (Len)), (P) += (Len))
212 #endif
214 #define add(n, f) \
215 do \
217 int _n = (n); \
218 int _delta = width - _n; \
219 int _incr = _n + (_delta > 0 ? _delta : 0); \
220 if (i + _incr >= maxsize) \
221 return 0; \
222 if (p) \
224 if (_delta > 0) \
226 if (pad == '0') \
227 memset_zero (p, _delta); \
228 else \
229 memset_space (p, _delta); \
231 f; \
232 p += _n; \
234 i += _incr; \
235 } while (0)
237 #define cpy(n, s) \
238 add ((n), \
239 if (to_lowcase) \
240 memcpy_lowcase (p, (s), _n); \
241 else if (to_uppcase) \
242 memcpy_uppcase (p, (s), _n); \
243 else \
244 memcpy ((PTR) p, (PTR) (s), _n))
248 #ifdef _LIBC
249 # define TOUPPER(Ch) toupper (Ch)
250 # define TOLOWER(Ch) tolower (Ch)
251 #else
252 # define TOUPPER(Ch) (islower (Ch) ? toupper (Ch) : (Ch))
253 # define TOLOWER(Ch) (isupper (Ch) ? tolower (Ch) : (Ch))
254 #endif
255 /* We don't use `isdigit' here since the locale dependent
256 interpretation is not what we want here. We only need to accept
257 the arabic digits in the ASCII range. One day there is perhaps a
258 more reliable way to accept other sets of digits. */
259 #define ISDIGIT(Ch) ((unsigned int) (Ch) - '0' <= 9)
261 static char *memcpy_lowcase __P ((char *dest, const char *src, size_t len));
263 static char *
264 memcpy_lowcase (dest, src, len)
265 char *dest;
266 const char *src;
267 size_t len;
269 while (len-- > 0)
270 dest[len] = TOLOWER (src[len]);
271 return dest;
274 static char *memcpy_uppcase __P ((char *dest, const char *src, size_t len));
276 static char *
277 memcpy_uppcase (dest, src, len)
278 char *dest;
279 const char *src;
280 size_t len;
282 while (len-- > 0)
283 dest[len] = TOUPPER (src[len]);
284 return dest;
287 #if ! HAVE_TM_GMTOFF
288 /* Yield the difference between *A and *B,
289 measured in seconds, ignoring leap seconds. */
290 static int tm_diff __P ((const struct tm *, const struct tm *));
291 static int
292 tm_diff (a, b)
293 const struct tm *a;
294 const struct tm *b;
296 /* Compute intervening leap days correctly even if year is negative.
297 Take care to avoid int overflow in leap day calculations,
298 but it's OK to assume that A and B are close to each other. */
299 int a4 = (a->tm_year >> 2) + (TM_YEAR_BASE >> 2) - ! (a->tm_year & 3);
300 int b4 = (b->tm_year >> 2) + (TM_YEAR_BASE >> 2) - ! (b->tm_year & 3);
301 int a100 = a4 / 25 - (a4 % 25 < 0);
302 int b100 = b4 / 25 - (b4 % 25 < 0);
303 int a400 = a100 >> 2;
304 int b400 = b100 >> 2;
305 int intervening_leap_days = (a4 - b4) - (a100 - b100) + (a400 - b400);
306 int years = a->tm_year - b->tm_year;
307 int days = (365 * years + intervening_leap_days
308 + (a->tm_yday - b->tm_yday));
309 return (60 * (60 * (24 * days + (a->tm_hour - b->tm_hour))
310 + (a->tm_min - b->tm_min))
311 + (a->tm_sec - b->tm_sec));
313 #endif /* ! HAVE_TM_GMTOFF */
317 /* The number of days from the first day of the first ISO week of this
318 year to the year day YDAY with week day WDAY. ISO weeks start on
319 Monday; the first ISO week has the year's first Thursday. YDAY may
320 be as small as YDAY_MINIMUM. */
321 #define ISO_WEEK_START_WDAY 1 /* Monday */
322 #define ISO_WEEK1_WDAY 4 /* Thursday */
323 #define YDAY_MINIMUM (-366)
324 static int iso_week_days __P ((int, int));
325 #ifdef __GNUC__
326 inline
327 #endif
328 static int
329 iso_week_days (yday, wday)
330 int yday;
331 int wday;
333 /* Add enough to the first operand of % to make it nonnegative. */
334 int big_enough_multiple_of_7 = (-YDAY_MINIMUM / 7 + 2) * 7;
335 return (yday
336 - (yday - wday + ISO_WEEK1_WDAY + big_enough_multiple_of_7) % 7
337 + ISO_WEEK1_WDAY - ISO_WEEK_START_WDAY);
341 #ifndef _NL_CURRENT
342 static char const weekday_name[][10] =
344 "Sunday", "Monday", "Tuesday", "Wednesday",
345 "Thursday", "Friday", "Saturday"
347 static char const month_name[][10] =
349 "January", "February", "March", "April", "May", "June",
350 "July", "August", "September", "October", "November", "December"
352 #endif
355 #if !defined _LIBC && HAVE_TZNAME && HAVE_TZSET
356 /* Solaris 2.5 tzset sometimes modifies the storage returned by localtime.
357 Work around this bug by copying *tp before it might be munged. */
358 size_t _strftime_copytm __P ((char *, size_t, const char *,
359 const struct tm *));
360 size_t
361 strftime (s, maxsize, format, tp)
362 char *s;
363 size_t maxsize;
364 const char *format;
365 const struct tm *tp;
367 struct tm tmcopy;
368 tmcopy = *tp;
369 return _strftime_copytm (s, maxsize, format, &tmcopy);
371 # ifdef strftime
372 # undef strftime
373 # endif
374 # define strftime(S, Maxsize, Format, Tp) \
375 _strftime_copytm (S, Maxsize, Format, Tp)
376 #endif
379 /* Write information from TP into S according to the format
380 string FORMAT, writing no more that MAXSIZE characters
381 (including the terminating '\0') and returning number of
382 characters written. If S is NULL, nothing will be written
383 anywhere, so to determine how many characters would be
384 written, use NULL for S and (size_t) UINT_MAX for MAXSIZE. */
385 size_t
386 strftime (s, maxsize, format, tp)
387 char *s;
388 size_t maxsize;
389 const char *format;
390 const struct tm *tp;
392 int hour12 = tp->tm_hour;
393 #ifdef _NL_CURRENT
394 const char *const a_wkday = _NL_CURRENT (LC_TIME, ABDAY_1 + tp->tm_wday);
395 const char *const f_wkday = _NL_CURRENT (LC_TIME, DAY_1 + tp->tm_wday);
396 const char *const a_month = _NL_CURRENT (LC_TIME, ABMON_1 + tp->tm_mon);
397 const char *const f_month = _NL_CURRENT (LC_TIME, MON_1 + tp->tm_mon);
398 const char *const ampm = _NL_CURRENT (LC_TIME,
399 hour12 > 11 ? PM_STR : AM_STR);
400 size_t aw_len = strlen (a_wkday);
401 size_t am_len = strlen (a_month);
402 size_t ap_len = strlen (ampm);
403 #else
404 const char *const f_wkday = weekday_name[tp->tm_wday];
405 const char *const f_month = month_name[tp->tm_mon];
406 const char *const a_wkday = f_wkday;
407 const char *const a_month = f_month;
408 const char *const ampm = "AMPM" + 2 * (hour12 > 11);
409 size_t aw_len = 3;
410 size_t am_len = 3;
411 size_t ap_len = 2;
412 #endif
413 size_t wkday_len = strlen (f_wkday);
414 size_t month_len = strlen (f_month);
415 const char *zone;
416 size_t zonelen;
417 size_t i = 0;
418 char *p = s;
419 const char *f;
421 zone = NULL;
422 #if !defined _LIBC && HAVE_TM_ZONE
423 /* XXX We have some problems here. First, the string pointed to by
424 tm_zone is dynamically allocated while loading the zone data. But
425 when another zone is loaded since the information in TP were
426 computed this would be a stale pointer.
427 The second problem is the POSIX test suite which assumes setting
428 the environment variable TZ to a new value before calling strftime()
429 will influence the result (the %Z format) even if the information in
430 TP is computed with a totally different time zone. --drepper@gnu */
431 zone = (const char *) tp->tm_zone;
432 #endif
433 #if HAVE_TZNAME
434 /* POSIX.1 8.1.1 requires that whenever strftime() is called, the
435 time zone names contained in the external variable `tzname' shall
436 be set as if the tzset() function had been called. */
437 # if HAVE_TZSET
438 tzset ();
439 # endif
441 if (!(zone && *zone) && tp->tm_isdst >= 0)
442 zone = tzname[tp->tm_isdst];
443 #endif
444 if (! zone)
445 zone = ""; /* POSIX.2 requires the empty string here. */
447 zonelen = strlen (zone);
449 if (hour12 > 12)
450 hour12 -= 12;
451 else
452 if (hour12 == 0) hour12 = 12;
454 for (f = format; *f != '\0'; ++f)
456 int pad; /* Padding for number ('-', '_', or 0). */
457 int modifier; /* Field modifier ('E', 'O', or 0). */
458 int digits; /* Max digits for numeric format. */
459 int number_value; /* Numeric value to be printed. */
460 int negative_number; /* 1 if the number is negative. */
461 const char *subfmt;
462 char *bufp;
463 char buf[1 + (sizeof (int) < sizeof (time_t)
464 ? INT_STRLEN_BOUND (time_t)
465 : INT_STRLEN_BOUND (int))];
466 int width = -1;
467 int to_lowcase = 0;
468 int to_uppcase = 0;
470 #if DO_MULTIBYTE
472 switch (*f)
474 case '%':
475 break;
477 case '\a': case '\b': case '\t': case '\n':
478 case '\v': case '\f': case '\r':
479 case ' ': case '!': case '"': case '#': case '&': case'\'':
480 case '(': case ')': case '*': case '+': case ',': case '-':
481 case '.': case '/': case '0': case '1': case '2': case '3':
482 case '4': case '5': case '6': case '7': case '8': case '9':
483 case ':': case ';': case '<': case '=': case '>': case '?':
484 case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
485 case 'G': case 'H': case 'I': case 'J': case 'K': case 'L':
486 case 'M': case 'N': case 'O': case 'P': case 'Q': case 'R':
487 case 'S': case 'T': case 'U': case 'V': case 'W': case 'X':
488 case 'Y': case 'Z': case '[': case'\\': case ']': case '^':
489 case '_': case 'a': case 'b': case 'c': case 'd': case 'e':
490 case 'f': case 'g': case 'h': case 'i': case 'j': case 'k':
491 case 'l': case 'm': case 'n': case 'o': case 'p': case 'q':
492 case 'r': case 's': case 't': case 'u': case 'v': case 'w':
493 case 'x': case 'y': case 'z': case '{': case '|': case '}':
494 case '~':
495 /* The C Standard requires these 98 characters (plus '%') to
496 be in the basic execution character set. None of these
497 characters can start a multibyte sequence, so they need
498 not be analyzed further. */
499 add (1, *p = *f);
500 continue;
502 default:
503 /* Copy this multibyte sequence until we reach its end, find
504 an error, or come back to the initial shift state. */
506 mbstate_t mbstate = mbstate_zero;
507 size_t len = 0;
511 size_t bytes = mbrlen (f + len, (size_t) -1, &mbstate);
513 if (bytes == 0)
514 break;
516 if (bytes == (size_t) -2 || bytes == (size_t) -1)
518 len++;
519 break;
522 len += bytes;
524 while (! mbsinit (&mbstate));
526 cpy (len, f);
527 continue;
531 #else /* ! DO_MULTIBYTE */
533 /* Either multibyte encodings are not supported, or they are
534 safe for formats, so any non-'%' byte can be copied through. */
535 if (*f != '%')
537 add (1, *p = *f);
538 continue;
541 #endif /* ! DO_MULTIBYTE */
543 /* Check for flags that can modify a format. */
544 pad = 0;
545 while (1)
547 switch (*++f)
549 /* This influences the number formats. */
550 case '_':
551 case '-':
552 case '0':
553 pad = *f;
554 continue;
556 /* This changes textual output. */
557 case '^':
558 to_uppcase = 1;
559 continue;
561 default:
562 break;
564 break;
567 /* As a GNU extension we allow to specify the field width. */
568 if (ISDIGIT (*f))
570 width = 0;
573 width *= 10;
574 width += *f - '0';
575 ++f;
577 while (ISDIGIT (*f));
580 /* Check for modifiers. */
581 switch (*f)
583 case 'E':
584 case 'O':
585 modifier = *f++;
586 break;
588 default:
589 modifier = 0;
590 break;
593 /* Now do the specified format. */
594 switch (*f)
596 #define DO_NUMBER(d, v) \
597 digits = d; number_value = v; goto do_number
598 #define DO_NUMBER_SPACEPAD(d, v) \
599 digits = d; number_value = v; goto do_number_spacepad
601 case '%':
602 if (modifier != 0)
603 goto bad_format;
604 add (1, *p = *f);
605 break;
607 case 'a':
608 if (modifier != 0)
609 goto bad_format;
610 cpy (aw_len, a_wkday);
611 break;
613 case 'A':
614 if (modifier != 0)
615 goto bad_format;
616 cpy (wkday_len, f_wkday);
617 break;
619 case 'b':
620 case 'h': /* POSIX.2 extension. */
621 if (modifier != 0)
622 goto bad_format;
623 cpy (am_len, a_month);
624 break;
626 case 'B':
627 if (modifier != 0)
628 goto bad_format;
629 cpy (month_len, f_month);
630 break;
632 case 'c':
633 if (modifier == 'O')
634 goto bad_format;
635 #ifdef _NL_CURRENT
636 if (! (modifier == 'E'
637 && *(subfmt = _NL_CURRENT (LC_TIME, ERA_D_T_FMT)) != '\0'))
638 subfmt = _NL_CURRENT (LC_TIME, D_T_FMT);
639 #else
640 subfmt = "%a %b %e %H:%M:%S %Y";
641 #endif
643 subformat:
645 char *old_start = p;
646 size_t len = strftime (NULL, maxsize - i, subfmt, tp);
647 if (len == 0 && *subfmt)
648 return 0;
649 add (len, strftime (p, maxsize - i, subfmt, tp));
651 if (to_uppcase)
652 while (old_start < p)
654 *old_start = TOUPPER (*old_start);
655 ++old_start;
658 break;
660 case 'C': /* POSIX.2 extension. */
661 if (modifier == 'O')
662 goto bad_format;
663 #if HAVE_STRUCT_ERA_ENTRY
664 if (modifier == 'E')
666 struct era_entry *era = _nl_get_era_entry (tp);
667 if (era)
669 size_t len = strlen (era->name_fmt);
670 cpy (len, era->name_fmt);
671 break;
674 #endif
676 int year = tp->tm_year + TM_YEAR_BASE;
677 DO_NUMBER (1, year / 100 - (year % 100 < 0));
680 case 'x':
681 if (modifier == 'O')
682 goto bad_format;
683 #ifdef _NL_CURRENT
684 if (! (modifier == 'E'
685 && *(subfmt = _NL_CURRENT (LC_TIME, ERA_D_FMT)) != '\0'))
686 subfmt = _NL_CURRENT (LC_TIME, D_FMT);
687 goto subformat;
688 #endif
689 /* Fall through. */
690 case 'D': /* POSIX.2 extension. */
691 if (modifier != 0)
692 goto bad_format;
693 subfmt = "%m/%d/%y";
694 goto subformat;
696 case 'd':
697 if (modifier == 'E')
698 goto bad_format;
700 DO_NUMBER (2, tp->tm_mday);
702 case 'e': /* POSIX.2 extension. */
703 if (modifier == 'E')
704 goto bad_format;
706 DO_NUMBER_SPACEPAD (2, tp->tm_mday);
708 /* All numeric formats set DIGITS and NUMBER_VALUE and then
709 jump to one of these two labels. */
711 do_number_spacepad:
712 /* Force `_' flag unless overwritten by `0' flag. */
713 if (pad != '0')
714 pad = '_';
716 do_number:
717 /* Format the number according to the MODIFIER flag. */
719 #ifdef _NL_CURRENT
720 if (modifier == 'O' && 0 <= number_value)
722 /* Get the locale specific alternate representation of
723 the number NUMBER_VALUE. If none exist NULL is returned. */
724 const char *cp = _nl_get_alt_digit (number_value);
726 if (cp != NULL)
728 size_t digitlen = strlen (cp);
729 if (digitlen != 0)
731 cpy (digitlen, cp);
732 break;
736 #endif
738 unsigned int u = number_value;
740 bufp = buf + sizeof (buf);
741 negative_number = number_value < 0;
743 if (negative_number)
744 u = -u;
747 *--bufp = u % 10 + '0';
748 while ((u /= 10) != 0);
751 do_number_sign_and_padding:
752 if (negative_number)
753 *--bufp = '-';
755 if (pad != '-')
757 int padding = digits - (buf + sizeof (buf) - bufp);
759 if (pad == '_')
761 while (0 < padding--)
762 *--bufp = ' ';
764 else
766 bufp += negative_number;
767 while (0 < padding--)
768 *--bufp = '0';
769 if (negative_number)
770 *--bufp = '-';
774 cpy (buf + sizeof (buf) - bufp, bufp);
775 break;
778 case 'H':
779 if (modifier == 'E')
780 goto bad_format;
782 DO_NUMBER (2, tp->tm_hour);
784 case 'I':
785 if (modifier == 'E')
786 goto bad_format;
788 DO_NUMBER (2, hour12);
790 case 'k': /* GNU extension. */
791 if (modifier == 'E')
792 goto bad_format;
794 DO_NUMBER_SPACEPAD (2, tp->tm_hour);
796 case 'l': /* GNU extension. */
797 if (modifier == 'E')
798 goto bad_format;
800 DO_NUMBER_SPACEPAD (2, hour12);
802 case 'j':
803 if (modifier == 'E')
804 goto bad_format;
806 DO_NUMBER (3, 1 + tp->tm_yday);
808 case 'M':
809 if (modifier == 'E')
810 goto bad_format;
812 DO_NUMBER (2, tp->tm_min);
814 case 'm':
815 if (modifier == 'E')
816 goto bad_format;
818 DO_NUMBER (2, tp->tm_mon + 1);
820 case 'n': /* POSIX.2 extension. */
821 add (1, *p = '\n');
822 break;
824 case 'P':
825 to_lowcase = 1;
826 /* FALLTHROUGH */
828 case 'p':
829 cpy (ap_len, ampm);
830 break;
832 case 'R': /* GNU extension. */
833 subfmt = "%H:%M";
834 goto subformat;
836 case 'r': /* POSIX.2 extension. */
837 #ifdef _NL_CURRENT
838 if (*(subfmt = _NL_CURRENT (LC_TIME, T_FMT_AMPM)) == '\0')
839 #endif
840 subfmt = "%I:%M:%S %p";
841 goto subformat;
843 case 'S':
844 if (modifier == 'E')
845 goto bad_format;
847 DO_NUMBER (2, tp->tm_sec);
849 case 's': /* GNU extension. */
851 struct tm ltm;
852 time_t t;
854 ltm = *tp;
855 t = mktime (&ltm);
857 /* Generate string value for T using time_t arithmetic;
858 this works even if sizeof (long) < sizeof (time_t). */
860 bufp = buf + sizeof (buf);
861 negative_number = t < 0;
865 int d = t % 10;
866 t /= 10;
868 if (negative_number)
870 d = -d;
872 /* Adjust if division truncates to minus infinity. */
873 if (0 < -1 % 10 && d < 0)
875 t++;
876 d += 10;
880 *--bufp = d + '0';
882 while (t != 0);
884 digits = 1;
885 goto do_number_sign_and_padding;
888 case 'X':
889 if (modifier == 'O')
890 goto bad_format;
891 #ifdef _NL_CURRENT
892 if (! (modifier == 'E'
893 && *(subfmt = _NL_CURRENT (LC_TIME, ERA_T_FMT)) != '\0'))
894 subfmt = _NL_CURRENT (LC_TIME, T_FMT);
895 goto subformat;
896 #endif
897 /* Fall through. */
898 case 'T': /* POSIX.2 extension. */
899 subfmt = "%H:%M:%S";
900 goto subformat;
902 case 't': /* POSIX.2 extension. */
903 add (1, *p = '\t');
904 break;
906 case 'u': /* POSIX.2 extension. */
907 DO_NUMBER (1, (tp->tm_wday - 1 + 7) % 7 + 1);
909 case 'U':
910 if (modifier == 'E')
911 goto bad_format;
913 DO_NUMBER (2, (tp->tm_yday - tp->tm_wday + 7) / 7);
915 case 'V':
916 case 'g': /* GNU extension. */
917 case 'G': /* GNU extension. */
918 if (modifier == 'E')
919 goto bad_format;
921 int year = tp->tm_year + TM_YEAR_BASE;
922 int days = iso_week_days (tp->tm_yday, tp->tm_wday);
924 if (days < 0)
926 /* This ISO week belongs to the previous year. */
927 year--;
928 days = iso_week_days (tp->tm_yday + (365 + __isleap (year)),
929 tp->tm_wday);
931 else
933 int d = iso_week_days (tp->tm_yday - (365 + __isleap (year)),
934 tp->tm_wday);
935 if (0 <= d)
937 /* This ISO week belongs to the next year. */
938 year++;
939 days = d;
943 switch (*f)
945 case 'g':
946 DO_NUMBER (2, (year % 100 + 100) % 100);
948 case 'G':
949 DO_NUMBER (1, year);
951 default:
952 DO_NUMBER (2, days / 7 + 1);
956 case 'W':
957 if (modifier == 'E')
958 goto bad_format;
960 DO_NUMBER (2, (tp->tm_yday - (tp->tm_wday - 1 + 7) % 7 + 7) / 7);
962 case 'w':
963 if (modifier == 'E')
964 goto bad_format;
966 DO_NUMBER (1, tp->tm_wday);
968 case 'Y':
969 #if HAVE_STRUCT_ERA_ENTRY
970 if (modifier == 'E')
972 struct era_entry *era = _nl_get_era_entry (tp);
973 if (era)
975 subfmt = strchr (era->name_fmt, '\0') + 1;
976 goto subformat;
979 #endif
980 if (modifier == 'O')
981 goto bad_format;
982 else
983 DO_NUMBER (1, tp->tm_year + TM_YEAR_BASE);
985 case 'y':
986 #if HAVE_STRUCT_ERA_ENTRY
987 if (modifier == 'E')
989 struct era_entry *era = _nl_get_era_entry (tp);
990 if (era)
992 int delta = tp->tm_year - era->start_date[0];
993 DO_NUMBER (1, (era->offset
994 + (era->direction == '-' ? -delta : delta)));
997 #endif
998 DO_NUMBER (2, (tp->tm_year % 100 + 100) % 100);
1000 case 'Z':
1001 cpy (zonelen, zone);
1002 break;
1004 case 'z': /* GNU extension. */
1005 if (tp->tm_isdst < 0)
1006 break;
1009 int diff;
1010 #if HAVE_TM_GMTOFF
1011 diff = tp->tm_gmtoff;
1012 #else
1013 struct tm gtm;
1014 struct tm ltm;
1015 time_t lt;
1017 ltm = *tp;
1018 lt = mktime (&ltm);
1020 if (lt == (time_t) -1)
1022 /* mktime returns -1 for errors, but -1 is also a
1023 valid time_t value. Check whether an error really
1024 occurred. */
1025 struct tm tm;
1026 localtime_r (&lt, &tm);
1028 if ((ltm.tm_sec ^ tm.tm_sec)
1029 | (ltm.tm_min ^ tm.tm_min)
1030 | (ltm.tm_hour ^ tm.tm_hour)
1031 | (ltm.tm_mday ^ tm.tm_mday)
1032 | (ltm.tm_mon ^ tm.tm_mon)
1033 | (ltm.tm_year ^ tm.tm_year))
1034 break;
1037 if (! gmtime_r (&lt, &gtm))
1038 break;
1040 diff = tm_diff (&ltm, &gtm);
1041 #endif
1043 if (diff < 0)
1045 add (1, *p = '-');
1046 diff = -diff;
1048 else
1049 add (1, *p = '+');
1051 diff /= 60;
1052 DO_NUMBER (4, (diff / 60) * 100 + diff % 60);
1055 case '\0': /* GNU extension: % at end of format. */
1056 --f;
1057 /* Fall through. */
1058 default:
1059 /* Unknown format; output the format, including the '%',
1060 since this is most likely the right thing to do if a
1061 multibyte string has been misparsed. */
1062 bad_format:
1064 int flen;
1065 for (flen = 1; f[1 - flen] != '%'; flen++)
1066 continue;
1067 cpy (flen, &f[1 - flen]);
1069 break;
1073 if (p)
1074 *p = '\0';
1075 return i;