]> git.ipfire.org Git - thirdparty/glibc.git/blame - time/strftime.c
Update.
[thirdparty/glibc.git] / time / strftime.c
CommitLineData
8d57beea 1/* Copyright (C) 1991, 92, 93, 94, 95, 96, 97 Free Software Foundation, Inc.
26761c28 2 This file is part of the GNU C Library.
28f540f4 3
26761c28
UD
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.
28f540f4 8
26761c28
UD
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.
28f540f4 13
26761c28
UD
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. */
28f540f4 18
0793d348
RM
19#ifdef HAVE_CONFIG_H
20# include <config.h>
21#endif
22
23#ifdef _LIBC
24# define HAVE_LIMITS_H 1
25# define HAVE_MBLEN 1
ec4b0518 26# define HAVE_MBRLEN 1
d68171ed 27# define HAVE_STRUCT_ERA_ENTRY 1
f0f1bf85 28# define HAVE_TM_GMTOFF 1
0793d348 29# define HAVE_TM_ZONE 1
68dbb3a6
UD
30# define HAVE_TZNAME 1
31# define HAVE_TZSET 1
ec4b0518 32# define MULTIBYTE_IS_FORMAT_SAFE 1
0793d348 33# define STDC_HEADERS 1
0793d348
RM
34# include "../locale/localeinfo.h"
35#endif
36
f8b87ef0 37#include <ctype.h>
0793d348 38#include <sys/types.h> /* Some systems define `time_t' here. */
75cd5204
RM
39
40#ifdef TIME_WITH_SYS_TIME
41# include <sys/time.h>
42# include <time.h>
43#else
44# ifdef HAVE_SYS_TIME_H
45# include <sys/time.h>
46# else
47# include <time.h>
48# endif
49#endif
8a4b65b4
UD
50#if HAVE_TZNAME
51extern char *tzname[];
52#endif
28f540f4 53
ec4b0518
UD
54/* Do multibyte processing if multibytes are supported, unless
55 multibyte sequences are safe in formats. Multibyte sequences are
56 safe if they cannot contain byte sequences that look like format
57 conversion specifications. The GNU C Library uses UTF8 multibyte
58 encoding, which is safe for formats, but strftime.c can be used
59 with other C libraries that use unsafe encodings. */
60#define DO_MULTIBYTE (HAVE_MBLEN && ! MULTIBYTE_IS_FORMAT_SAFE)
61
62#if DO_MULTIBYTE
63# if HAVE_MBRLEN
64# include <wchar.h>
65# else
66 /* Simulate mbrlen with mblen as best we can. */
67# define mbstate_t int
68# define mbrlen(s, n, ps) mblen (s, n)
69# define mbsinit(ps) (*(ps) == 0)
70# endif
71 static const mbstate_t mbstate_zero;
0793d348
RM
72#endif
73
74#if HAVE_LIMITS_H
75# include <limits.h>
28f540f4
RM
76#endif
77
0793d348
RM
78#if STDC_HEADERS
79# include <stddef.h>
80# include <stdlib.h>
81# include <string.h>
82#else
ae1025be
UD
83# ifndef HAVE_MEMCPY
84# define memcpy(d, s, n) bcopy ((s), (d), (n))
85# endif
0793d348
RM
86#endif
87
88#ifndef __P
6d52618b
UD
89# if defined (__GNUC__) || (defined (__STDC__) && __STDC__)
90# define __P(args) args
91# else
92# define __P(args) ()
93# endif /* GCC. */
0793d348
RM
94#endif /* Not __P. */
95
96#ifndef PTR
6d52618b
UD
97# ifdef __STDC__
98# define PTR void *
99# else
100# define PTR char *
101# endif
0793d348
RM
102#endif
103
f0f1bf85 104#ifndef CHAR_BIT
6d52618b
UD
105# define CHAR_BIT 8
106#endif
107
108#ifndef NULL
109# define NULL 0
f0f1bf85
UD
110#endif
111
112#define TYPE_SIGNED(t) ((t) -1 < 0)
113
114/* Bound on length of the string representing an integer value of type t.
115 Subtract one for the sign bit if t is signed;
116 302 / 1000 is log10 (2) rounded up;
117 add one for integer division truncation;
118 add one more for a minus sign if t is signed. */
119#define INT_STRLEN_BOUND(t) \
120 ((sizeof (t) * CHAR_BIT - TYPE_SIGNED (t)) * 302 / 100 + 1 + TYPE_SIGNED (t))
121
122#define TM_YEAR_BASE 1900
123
ec4b0518
UD
124#ifndef __isleap
125/* Nonzero if YEAR is a leap year (every 4 years,
126 except every 100th isn't, and every 400th is). */
6d52618b 127# define __isleap(year) \
ec4b0518
UD
128 ((year) % 4 == 0 && ((year) % 100 != 0 || (year) % 400 == 0))
129#endif
130
2de99474 131
fafaa44e
UD
132#ifdef _LIBC
133# define gmtime_r __gmtime_r
134# define localtime_r __localtime_r
68dbb3a6 135extern int __tz_compute __P ((time_t timer, const struct tm *tm));
10dc2a90
UD
136# define tzname __tzname
137# define tzset __tzset
fafaa44e
UD
138#else
139# if ! HAVE_LOCALTIME_R
140# if ! HAVE_TM_GMTOFF
141/* Approximate gmtime_r as best we can in its absence. */
76b87c03 142# define gmtime_r my_gmtime_r
fafaa44e
UD
143static struct tm *gmtime_r __P ((const time_t *, struct tm *));
144static struct tm *
145gmtime_r (t, tp)
146 const time_t *t;
147 struct tm *tp;
148{
149 struct tm *l = gmtime (t);
150 if (! l)
151 return 0;
152 *tp = *l;
153 return tp;
154}
155# endif /* ! HAVE_TM_GMTOFF */
156
157/* Approximate localtime_r as best we can in its absence. */
9c2322bc 158# define localtime_r my_ftime_localtime_r
fafaa44e
UD
159static struct tm *localtime_r __P ((const time_t *, struct tm *));
160static struct tm *
161localtime_r (t, tp)
162 const time_t *t;
163 struct tm *tp;
164{
165 struct tm *l = localtime (t);
166 if (! l)
167 return 0;
168 *tp = *l;
169 return tp;
170}
171# endif /* ! HAVE_LOCALTIME_R */
172#endif /* ! defined (_LIBC) */
173
174
68dbb3a6
UD
175#if !defined (memset) && !defined (HAVE_MEMSET) && !defined (_LIBC)
176/* Some systems lack the `memset' function and we don't want to
177 introduce additional dependencies. */
f8b87ef0 178static const char spaces[16] = " ";
779ae82e 179static const char zeroes[16] = "0000000000000000";
68dbb3a6
UD
180
181# define memset_space(P, Len) \
182 do { \
183 int _len = (Len); \
63551311 184 \
68dbb3a6
UD
185 do \
186 { \
187 int _this = _len > 16 ? 16 : _len; \
188 memcpy ((P), spaces, _this); \
189 (P) += _this; \
190 _len -= _this; \
191 } \
192 while (_len > 0); \
193 } while (0)
779ae82e
UD
194
195# define memset_zero(P, Len) \
196 do { \
197 int _len = (Len); \
198 \
199 do \
200 { \
201 int _this = _len > 16 ? 16 : _len; \
202 memcpy ((P), zeroes, _this); \
203 (P) += _this; \
204 _len -= _this; \
205 } \
206 while (_len > 0); \
207 } while (0)
68dbb3a6 208#else
63551311 209# define memset_space(P, Len) (memset ((P), ' ', (Len)), (P) += (Len))
779ae82e 210# define memset_zero(P, Len) (memset ((P), '0', (Len)), (P) += (Len))
68dbb3a6
UD
211#endif
212
76b87c03 213#define add(n, f) \
28f540f4
RM
214 do \
215 { \
f8b87ef0
UD
216 int _n = (n); \
217 int _delta = width - _n; \
6d52618b
UD
218 int _incr = _n + (_delta > 0 ? _delta : 0); \
219 if (i + _incr >= maxsize) \
28f540f4 220 return 0; \
6d52618b
UD
221 if (p) \
222 { \
223 if (_delta > 0) \
779ae82e
UD
224 { \
225 if (pad == '0') \
226 memset_zero (p, _delta); \
227 else \
228 memset_space (p, _delta); \
229 } \
6d52618b
UD
230 f; \
231 p += _n; \
232 } \
233 i += _incr; \
28f540f4 234 } while (0)
f8b87ef0 235
76b87c03 236#define cpy(n, s) \
f8b87ef0
UD
237 add ((n), \
238 if (to_lowcase) \
239 memcpy_lowcase (p, (s), _n); \
6d52618b
UD
240 else if (to_uppcase) \
241 memcpy_uppcase (p, (s), _n); \
f8b87ef0
UD
242 else \
243 memcpy ((PTR) p, (PTR) (s), _n))
244
245
246
247#ifdef _LIBC
248# define TOUPPER(Ch) toupper (Ch)
249# define TOLOWER(Ch) tolower (Ch)
250#else
251# define TOUPPER(Ch) (islower (Ch) ? toupper (Ch) : (Ch))
252# define TOLOWER(Ch) (isupper (Ch) ? tolower (Ch) : (Ch))
253#endif
7e3be507
UD
254/* We don't use `isdigit' here since the locale dependent
255 interpretation is not what we want here. We only need to accept
256 the arabic digits in the ASCII range. One day there is perhaps a
257 more reliable way to accept other sets of digits. */
258#define ISDIGIT(Ch) ((unsigned int) (Ch) - '0' <= 9)
f8b87ef0
UD
259
260static char *memcpy_lowcase __P ((char *dest, const char *src, size_t len));
261
262static char *
263memcpy_lowcase (dest, src, len)
264 char *dest;
265 const char *src;
266 size_t len;
267{
268 while (len-- > 0)
269 dest[len] = TOLOWER (src[len]);
270 return dest;
271}
0793d348 272
6d52618b
UD
273static char *memcpy_uppcase __P ((char *dest, const char *src, size_t len));
274
275static char *
276memcpy_uppcase (dest, src, len)
277 char *dest;
278 const char *src;
279 size_t len;
280{
281 while (len-- > 0)
282 dest[len] = TOUPPER (src[len]);
283 return dest;
284}
285
cf29ffbe 286
f0f1bf85
UD
287#if ! HAVE_TM_GMTOFF
288/* Yield the difference between *A and *B,
289 measured in seconds, ignoring leap seconds. */
9c2322bc 290# define tm_diff ftime_tm_diff
f0f1bf85
UD
291static int tm_diff __P ((const struct tm *, const struct tm *));
292static int
293tm_diff (a, b)
294 const struct tm *a;
295 const struct tm *b;
296{
ec4b0518
UD
297 /* Compute intervening leap days correctly even if year is negative.
298 Take care to avoid int overflow in leap day calculations,
299 but it's OK to assume that A and B are close to each other. */
300 int a4 = (a->tm_year >> 2) + (TM_YEAR_BASE >> 2) - ! (a->tm_year & 3);
301 int b4 = (b->tm_year >> 2) + (TM_YEAR_BASE >> 2) - ! (b->tm_year & 3);
302 int a100 = a4 / 25 - (a4 % 25 < 0);
303 int b100 = b4 / 25 - (b4 % 25 < 0);
304 int a400 = a100 >> 2;
305 int b400 = b100 >> 2;
306 int intervening_leap_days = (a4 - b4) - (a100 - b100) + (a400 - b400);
307 int years = a->tm_year - b->tm_year;
f0f1bf85
UD
308 int days = (365 * years + intervening_leap_days
309 + (a->tm_yday - b->tm_yday));
310 return (60 * (60 * (24 * days + (a->tm_hour - b->tm_hour))
311 + (a->tm_min - b->tm_min))
312 + (a->tm_sec - b->tm_sec));
313}
314#endif /* ! HAVE_TM_GMTOFF */
28f540f4 315
b7459e56
RM
316
317
ec4b0518
UD
318/* The number of days from the first day of the first ISO week of this
319 year to the year day YDAY with week day WDAY. ISO weeks start on
320 Monday; the first ISO week has the year's first Thursday. YDAY may
321 be as small as YDAY_MINIMUM. */
322#define ISO_WEEK_START_WDAY 1 /* Monday */
323#define ISO_WEEK1_WDAY 4 /* Thursday */
324#define YDAY_MINIMUM (-366)
325static int iso_week_days __P ((int, int));
326#ifdef __GNUC__
28f540f4
RM
327inline
328#endif
ec4b0518
UD
329static int
330iso_week_days (yday, wday)
331 int yday;
332 int wday;
28f540f4 333{
ec4b0518
UD
334 /* Add enough to the first operand of % to make it nonnegative. */
335 int big_enough_multiple_of_7 = (-YDAY_MINIMUM / 7 + 2) * 7;
336 return (yday
337 - (yday - wday + ISO_WEEK1_WDAY + big_enough_multiple_of_7) % 7
338 + ISO_WEEK1_WDAY - ISO_WEEK_START_WDAY);
28f540f4
RM
339}
340
ec4b0518 341
0793d348
RM
342#ifndef _NL_CURRENT
343static char const weekday_name[][10] =
344 {
345 "Sunday", "Monday", "Tuesday", "Wednesday",
346 "Thursday", "Friday", "Saturday"
347 };
348static char const month_name[][10] =
349 {
350 "January", "February", "March", "April", "May", "June",
351 "July", "August", "September", "October", "November", "December"
352 };
353#endif
28f540f4 354
8d57beea
UD
355
356#if !defined _LIBC && HAVE_TZNAME && HAVE_TZSET
357 /* Solaris 2.5 tzset sometimes modifies the storage returned by localtime.
358 Work around this bug by copying *tp before it might be munged. */
359 size_t _strftime_copytm __P ((char *, size_t, const char *,
360 const struct tm *));
361 size_t
362 strftime (s, maxsize, format, tp)
363 char *s;
364 size_t maxsize;
365 const char *format;
366 const struct tm *tp;
367 {
368 struct tm tmcopy;
369 tmcopy = *tp;
370 return _strftime_copytm (s, maxsize, format, &tmcopy);
371 }
372# ifdef strftime
373# undef strftime
374# endif
375# define strftime(S, Maxsize, Format, Tp) \
c0e45674 376 _strftime_copytm (S, Maxsize, Format, Tp)
8d57beea
UD
377#endif
378
379
28f540f4
RM
380/* Write information from TP into S according to the format
381 string FORMAT, writing no more that MAXSIZE characters
382 (including the terminating '\0') and returning number of
383 characters written. If S is NULL, nothing will be written
384 anywhere, so to determine how many characters would be
385 written, use NULL for S and (size_t) UINT_MAX for MAXSIZE. */
386size_t
0793d348
RM
387strftime (s, maxsize, format, tp)
388 char *s;
389 size_t maxsize;
390 const char *format;
f8b87ef0 391 const struct tm *tp;
28f540f4 392{
0793d348
RM
393 int hour12 = tp->tm_hour;
394#ifdef _NL_CURRENT
395 const char *const a_wkday = _NL_CURRENT (LC_TIME, ABDAY_1 + tp->tm_wday);
396 const char *const f_wkday = _NL_CURRENT (LC_TIME, DAY_1 + tp->tm_wday);
397 const char *const a_month = _NL_CURRENT (LC_TIME, ABMON_1 + tp->tm_mon);
398 const char *const f_month = _NL_CURRENT (LC_TIME, MON_1 + tp->tm_mon);
399 const char *const ampm = _NL_CURRENT (LC_TIME,
b20e47cb 400 hour12 > 11 ? PM_STR : AM_STR);
d68171ed
UD
401 size_t aw_len = strlen (a_wkday);
402 size_t am_len = strlen (a_month);
0793d348
RM
403 size_t ap_len = strlen (ampm);
404#else
405 const char *const f_wkday = weekday_name[tp->tm_wday];
406 const char *const f_month = month_name[tp->tm_mon];
407 const char *const a_wkday = f_wkday;
408 const char *const a_month = f_month;
b20e47cb 409 const char *const ampm = "AMPM" + 2 * (hour12 > 11);
0793d348
RM
410 size_t aw_len = 3;
411 size_t am_len = 3;
412 size_t ap_len = 2;
413#endif
2de99474
UD
414 size_t wkday_len = strlen (f_wkday);
415 size_t month_len = strlen (f_month);
0793d348 416 const char *zone;
28f540f4 417 size_t zonelen;
f8b87ef0
UD
418 size_t i = 0;
419 char *p = s;
420 const char *f;
28f540f4 421
68dbb3a6
UD
422 zone = NULL;
423#if !defined _LIBC && HAVE_TM_ZONE
424 /* XXX We have some problems here. First, the string pointed to by
425 tm_zone is dynamically allocated while loading the zone data. But
426 when another zone is loaded since the information in TP were
427 computed this would be a stale pointer.
428 The second problem is the POSIX test suite which assumes setting
429 the environment variable TZ to a new value before calling strftime()
430 will influence the result (the %Z format) even if the information in
431 TP is computed with a totally different time zone. --drepper@gnu */
0793d348
RM
432 zone = (const char *) tp->tm_zone;
433#endif
434#if HAVE_TZNAME
68dbb3a6
UD
435 /* POSIX.1 8.1.1 requires that whenever strftime() is called, the
436 time zone names contained in the external variable `tzname' shall
437 be set as if the tzset() function had been called. */
438# if HAVE_TZSET
439 tzset ();
440# endif
441
0793d348
RM
442 if (!(zone && *zone) && tp->tm_isdst >= 0)
443 zone = tzname[tp->tm_isdst];
444#endif
ec4b0518
UD
445 if (! zone)
446 zone = ""; /* POSIX.2 requires the empty string here. */
0793d348
RM
447
448 zonelen = strlen (zone);
28f540f4
RM
449
450 if (hour12 > 12)
451 hour12 -= 12;
452 else
453 if (hour12 == 0) hour12 = 12;
454
455 for (f = format; *f != '\0'; ++f)
456 {
ec4b0518
UD
457 int pad; /* Padding for number ('-', '_', or 0). */
458 int modifier; /* Field modifier ('E', 'O', or 0). */
459 int digits; /* Max digits for numeric format. */
460 int number_value; /* Numeric value to be printed. */
f0f1bf85 461 int negative_number; /* 1 if the number is negative. */
ec4b0518 462 const char *subfmt;
f0f1bf85
UD
463 char *bufp;
464 char buf[1 + (sizeof (int) < sizeof (time_t)
465 ? INT_STRLEN_BOUND (time_t)
466 : INT_STRLEN_BOUND (int))];
f8b87ef0
UD
467 int width = -1;
468 int to_lowcase = 0;
6d52618b 469 int to_uppcase = 0;
cf29ffbe 470 int change_case = 0;
28f540f4 471
ec4b0518
UD
472#if DO_MULTIBYTE
473
474 switch (*f)
28f540f4 475 {
ec4b0518
UD
476 case '%':
477 break;
478
479 case '\a': case '\b': case '\t': case '\n':
480 case '\v': case '\f': case '\r':
481 case ' ': case '!': case '"': case '#': case '&': case'\'':
482 case '(': case ')': case '*': case '+': case ',': case '-':
483 case '.': case '/': case '0': case '1': case '2': case '3':
484 case '4': case '5': case '6': case '7': case '8': case '9':
485 case ':': case ';': case '<': case '=': case '>': case '?':
486 case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
487 case 'G': case 'H': case 'I': case 'J': case 'K': case 'L':
488 case 'M': case 'N': case 'O': case 'P': case 'Q': case 'R':
489 case 'S': case 'T': case 'U': case 'V': case 'W': case 'X':
490 case 'Y': case 'Z': case '[': case'\\': case ']': case '^':
491 case '_': case 'a': case 'b': case 'c': case 'd': case 'e':
492 case 'f': case 'g': case 'h': case 'i': case 'j': case 'k':
493 case 'l': case 'm': case 'n': case 'o': case 'p': case 'q':
494 case 'r': case 's': case 't': case 'u': case 'v': case 'w':
495 case 'x': case 'y': case 'z': case '{': case '|': case '}':
496 case '~':
497 /* The C Standard requires these 98 characters (plus '%') to
498 be in the basic execution character set. None of these
499 characters can start a multibyte sequence, so they need
500 not be analyzed further. */
501 add (1, *p = *f);
502 continue;
503
504 default:
505 /* Copy this multibyte sequence until we reach its end, find
506 an error, or come back to the initial shift state. */
507 {
508 mbstate_t mbstate = mbstate_zero;
509 size_t len = 0;
510
511 do
512 {
513 size_t bytes = mbrlen (f + len, (size_t) -1, &mbstate);
514
515 if (bytes == 0)
516 break;
517
518 if (bytes == (size_t) -2 || bytes == (size_t) -1)
519 {
520 len++;
521 break;
522 }
523
524 len += bytes;
525 }
526 while (! mbsinit (&mbstate));
527
528 cpy (len, f);
529 continue;
530 }
28f540f4
RM
531 }
532
ec4b0518
UD
533#else /* ! DO_MULTIBYTE */
534
535 /* Either multibyte encodings are not supported, or they are
536 safe for formats, so any non-'%' byte can be copied through. */
28f540f4
RM
537 if (*f != '%')
538 {
2de99474 539 add (1, *p = *f);
28f540f4
RM
540 continue;
541 }
542
ec4b0518
UD
543#endif /* ! DO_MULTIBYTE */
544
7e3be507 545 /* Check for flags that can modify a format. */
01c901a5 546 pad = 0;
f8b87ef0 547 while (1)
b7459e56 548 {
6d52618b 549 switch (*++f)
f8b87ef0 550 {
7e3be507 551 /* This influences the number formats. */
f8b87ef0
UD
552 case '_':
553 case '-':
554 case '0':
6d52618b
UD
555 pad = *f;
556 continue;
557
7e3be507 558 /* This changes textual output. */
6d52618b
UD
559 case '^':
560 to_uppcase = 1;
561 continue;
cf29ffbe
UD
562 case '#':
563 change_case = 1;
564 continue;
f8b87ef0
UD
565
566 default:
f8b87ef0
UD
567 break;
568 }
b7459e56 569 break;
f8b87ef0 570 }
ec4b0518 571
f8b87ef0 572 /* As a GNU extension we allow to specify the field width. */
7e3be507 573 if (ISDIGIT (*f))
f8b87ef0
UD
574 {
575 width = 0;
576 do
577 {
578 width *= 10;
579 width += *f - '0';
7e3be507 580 ++f;
f8b87ef0 581 }
7e3be507 582 while (ISDIGIT (*f));
b7459e56
RM
583 }
584
f0f1bf85
UD
585 /* Check for modifiers. */
586 switch (*f)
587 {
588 case 'E':
f0f1bf85 589 case 'O':
ec4b0518 590 modifier = *f++;
f0f1bf85 591 break;
ec4b0518 592
f0f1bf85 593 default:
ec4b0518 594 modifier = 0;
f0f1bf85
UD
595 break;
596 }
597
b7459e56 598 /* Now do the specified format. */
28f540f4
RM
599 switch (*f)
600 {
f0f1bf85 601#define DO_NUMBER(d, v) \
cf29ffbe
UD
602 digits = width == -1 ? d : width; \
603 number_value = v; goto do_number
f0f1bf85 604#define DO_NUMBER_SPACEPAD(d, v) \
cf29ffbe
UD
605 digits = width == -1 ? d : width; \
606 number_value = v; goto do_number_spacepad
f0f1bf85 607
28f540f4 608 case '%':
ec4b0518 609 if (modifier != 0)
f0f1bf85 610 goto bad_format;
2de99474 611 add (1, *p = *f);
28f540f4
RM
612 break;
613
614 case 'a':
ec4b0518 615 if (modifier != 0)
f0f1bf85 616 goto bad_format;
cf29ffbe
UD
617 if (change_case)
618 {
619 to_uppcase = 1;
620 to_lowcase = 0;
621 }
2de99474 622 cpy (aw_len, a_wkday);
28f540f4
RM
623 break;
624
625 case 'A':
ec4b0518 626 if (modifier != 0)
f0f1bf85 627 goto bad_format;
cf29ffbe
UD
628 if (change_case)
629 {
630 to_uppcase = 1;
631 to_lowcase = 0;
632 }
2de99474 633 cpy (wkday_len, f_wkday);
28f540f4
RM
634 break;
635
636 case 'b':
ec4b0518
UD
637 case 'h': /* POSIX.2 extension. */
638 if (modifier != 0)
f0f1bf85 639 goto bad_format;
2de99474 640 cpy (am_len, a_month);
28f540f4
RM
641 break;
642
643 case 'B':
ec4b0518 644 if (modifier != 0)
f0f1bf85 645 goto bad_format;
cf29ffbe
UD
646 if (change_case)
647 {
648 to_uppcase = 1;
649 to_lowcase = 0;
650 }
2de99474 651 cpy (month_len, f_month);
28f540f4
RM
652 break;
653
654 case 'c':
ec4b0518 655 if (modifier == 'O')
f0f1bf85 656 goto bad_format;
0793d348 657#ifdef _NL_CURRENT
ec4b0518
UD
658 if (! (modifier == 'E'
659 && *(subfmt = _NL_CURRENT (LC_TIME, ERA_D_T_FMT)) != '\0'))
f0f1bf85 660 subfmt = _NL_CURRENT (LC_TIME, D_T_FMT);
0793d348 661#else
f65fd747 662 subfmt = "%a %b %e %H:%M:%S %Y";
0793d348 663#endif
f0f1bf85 664
28f540f4
RM
665 subformat:
666 {
6d52618b 667 char *old_start = p;
f8b87ef0 668 size_t len = strftime (NULL, maxsize - i, subfmt, tp);
0793d348
RM
669 if (len == 0 && *subfmt)
670 return 0;
f8b87ef0 671 add (len, strftime (p, maxsize - i, subfmt, tp));
6d52618b
UD
672
673 if (to_uppcase)
674 while (old_start < p)
675 {
676 *old_start = TOUPPER (*old_start);
677 ++old_start;
678 }
28f540f4
RM
679 }
680 break;
681
ec4b0518
UD
682 case 'C': /* POSIX.2 extension. */
683 if (modifier == 'O')
f0f1bf85 684 goto bad_format;
d68171ed
UD
685#if HAVE_STRUCT_ERA_ENTRY
686 if (modifier == 'E')
687 {
688 struct era_entry *era = _nl_get_era_entry (tp);
689 if (era)
690 {
691 size_t len = strlen (era->name_fmt);
692 cpy (len, era->name_fmt);
693 break;
694 }
695 }
f0f1bf85 696#endif
ec4b0518
UD
697 {
698 int year = tp->tm_year + TM_YEAR_BASE;
699 DO_NUMBER (1, year / 100 - (year % 100 < 0));
700 }
28f540f4 701
0793d348 702 case 'x':
ec4b0518 703 if (modifier == 'O')
f0f1bf85 704 goto bad_format;
0793d348 705#ifdef _NL_CURRENT
ec4b0518
UD
706 if (! (modifier == 'E'
707 && *(subfmt = _NL_CURRENT (LC_TIME, ERA_D_FMT)) != '\0'))
f0f1bf85 708 subfmt = _NL_CURRENT (LC_TIME, D_FMT);
0793d348
RM
709 goto subformat;
710#endif
711 /* Fall through. */
ec4b0518
UD
712 case 'D': /* POSIX.2 extension. */
713 if (modifier != 0)
714 goto bad_format;
28f540f4
RM
715 subfmt = "%m/%d/%y";
716 goto subformat;
717
718 case 'd':
ec4b0518 719 if (modifier == 'E')
f0f1bf85
UD
720 goto bad_format;
721
b7459e56 722 DO_NUMBER (2, tp->tm_mday);
28f540f4 723
ec4b0518
UD
724 case 'e': /* POSIX.2 extension. */
725 if (modifier == 'E')
f0f1bf85
UD
726 goto bad_format;
727
2064087b 728 DO_NUMBER_SPACEPAD (2, tp->tm_mday);
b7459e56 729
f0f1bf85 730 /* All numeric formats set DIGITS and NUMBER_VALUE and then
b7459e56
RM
731 jump to one of these two labels. */
732
2064087b 733 do_number_spacepad:
f8b87ef0
UD
734 /* Force `_' flag unless overwritten by `0' flag. */
735 if (pad != '0')
736 pad = '_';
b7459e56
RM
737
738 do_number:
f0f1bf85
UD
739 /* Format the number according to the MODIFIER flag. */
740
741#ifdef _NL_CURRENT
ec4b0518 742 if (modifier == 'O' && 0 <= number_value)
f0f1bf85 743 {
c4029823
UD
744 /* Get the locale specific alternate representation of
745 the number NUMBER_VALUE. If none exist NULL is returned. */
746 const char *cp = _nl_get_alt_digit (number_value);
ec4b0518 747
c4029823 748 if (cp != NULL)
ec4b0518
UD
749 {
750 size_t digitlen = strlen (cp);
751 if (digitlen != 0)
752 {
753 cpy (digitlen, cp);
754 break;
755 }
756 }
f0f1bf85
UD
757 }
758#endif
b7459e56 759 {
f0f1bf85 760 unsigned int u = number_value;
b7459e56 761
f0f1bf85
UD
762 bufp = buf + sizeof (buf);
763 negative_number = number_value < 0;
b7459e56 764
f0f1bf85
UD
765 if (negative_number)
766 u = -u;
b7459e56 767
f0f1bf85
UD
768 do
769 *--bufp = u % 10 + '0';
770 while ((u /= 10) != 0);
771 }
b7459e56 772
f0f1bf85
UD
773 do_number_sign_and_padding:
774 if (negative_number)
775 *--bufp = '-';
776
ec4b0518 777 if (pad != '-')
f0f1bf85
UD
778 {
779 int padding = digits - (buf + sizeof (buf) - bufp);
780
ec4b0518 781 if (pad == '_')
f0f1bf85
UD
782 {
783 while (0 < padding--)
784 *--bufp = ' ';
785 }
786 else
787 {
788 bufp += negative_number;
789 while (0 < padding--)
790 *--bufp = '0';
791 if (negative_number)
792 *--bufp = '-';
793 }
794 }
795
796 cpy (buf + sizeof (buf) - bufp, bufp);
f0f1bf85 797 break;
b7459e56 798
28f540f4
RM
799
800 case 'H':
ec4b0518 801 if (modifier == 'E')
f0f1bf85
UD
802 goto bad_format;
803
b7459e56 804 DO_NUMBER (2, tp->tm_hour);
28f540f4
RM
805
806 case 'I':
ec4b0518 807 if (modifier == 'E')
f0f1bf85
UD
808 goto bad_format;
809
b7459e56 810 DO_NUMBER (2, hour12);
28f540f4
RM
811
812 case 'k': /* GNU extension. */
ec4b0518 813 if (modifier == 'E')
f0f1bf85
UD
814 goto bad_format;
815
2064087b 816 DO_NUMBER_SPACEPAD (2, tp->tm_hour);
28f540f4
RM
817
818 case 'l': /* GNU extension. */
ec4b0518 819 if (modifier == 'E')
f0f1bf85
UD
820 goto bad_format;
821
2064087b 822 DO_NUMBER_SPACEPAD (2, hour12);
28f540f4
RM
823
824 case 'j':
ec4b0518 825 if (modifier == 'E')
f0f1bf85
UD
826 goto bad_format;
827
b7459e56 828 DO_NUMBER (3, 1 + tp->tm_yday);
28f540f4
RM
829
830 case 'M':
ec4b0518 831 if (modifier == 'E')
f0f1bf85
UD
832 goto bad_format;
833
b7459e56 834 DO_NUMBER (2, tp->tm_min);
28f540f4
RM
835
836 case 'm':
ec4b0518 837 if (modifier == 'E')
f0f1bf85
UD
838 goto bad_format;
839
b7459e56 840 DO_NUMBER (2, tp->tm_mon + 1);
28f540f4 841
ec4b0518 842 case 'n': /* POSIX.2 extension. */
28f540f4
RM
843 add (1, *p = '\n');
844 break;
845
f8b87ef0
UD
846 case 'P':
847 to_lowcase = 1;
848 /* FALLTHROUGH */
849
28f540f4 850 case 'p':
cf29ffbe
UD
851 if (change_case)
852 {
853 to_uppcase = 0;
854 to_lowcase = 1;
855 }
2de99474 856 cpy (ap_len, ampm);
28f540f4
RM
857 break;
858
859 case 'R': /* GNU extension. */
860 subfmt = "%H:%M";
861 goto subformat;
862
ec4b0518
UD
863 case 'r': /* POSIX.2 extension. */
864#ifdef _NL_CURRENT
865 if (*(subfmt = _NL_CURRENT (LC_TIME, T_FMT_AMPM)) == '\0')
866#endif
867 subfmt = "%I:%M:%S %p";
28f540f4
RM
868 goto subformat;
869
870 case 'S':
ec4b0518
UD
871 if (modifier == 'E')
872 goto bad_format;
f0f1bf85 873
b7459e56 874 DO_NUMBER (2, tp->tm_sec);
28f540f4 875
2de99474 876 case 's': /* GNU extension. */
f0f1bf85 877 {
b33f91e9
UD
878 struct tm ltm;
879 time_t t;
880
881 ltm = *tp;
882 t = mktime (&ltm);
f0f1bf85
UD
883
884 /* Generate string value for T using time_t arithmetic;
885 this works even if sizeof (long) < sizeof (time_t). */
886
887 bufp = buf + sizeof (buf);
888 negative_number = t < 0;
889
890 do
891 {
892 int d = t % 10;
893 t /= 10;
894
895 if (negative_number)
896 {
897 d = -d;
898
899 /* Adjust if division truncates to minus infinity. */
900 if (0 < -1 % 10 && d < 0)
901 {
902 t++;
903 d += 10;
904 }
905 }
906
907 *--bufp = d + '0';
908 }
909 while (t != 0);
910
911 digits = 1;
912 goto do_number_sign_and_padding;
2de99474 913 }
2de99474 914
0793d348 915 case 'X':
ec4b0518 916 if (modifier == 'O')
f0f1bf85 917 goto bad_format;
0793d348 918#ifdef _NL_CURRENT
ec4b0518
UD
919 if (! (modifier == 'E'
920 && *(subfmt = _NL_CURRENT (LC_TIME, ERA_T_FMT)) != '\0'))
f0f1bf85 921 subfmt = _NL_CURRENT (LC_TIME, T_FMT);
0793d348
RM
922 goto subformat;
923#endif
924 /* Fall through. */
ec4b0518 925 case 'T': /* POSIX.2 extension. */
28f540f4
RM
926 subfmt = "%H:%M:%S";
927 goto subformat;
928
ec4b0518 929 case 't': /* POSIX.2 extension. */
28f540f4
RM
930 add (1, *p = '\t');
931 break;
932
ec4b0518
UD
933 case 'u': /* POSIX.2 extension. */
934 DO_NUMBER (1, (tp->tm_wday - 1 + 7) % 7 + 1);
935
28f540f4 936 case 'U':
ec4b0518 937 if (modifier == 'E')
f0f1bf85
UD
938 goto bad_format;
939
ec4b0518 940 DO_NUMBER (2, (tp->tm_yday - tp->tm_wday + 7) / 7);
28f540f4 941
339841f9 942 case 'V':
ec4b0518
UD
943 case 'g': /* GNU extension. */
944 case 'G': /* GNU extension. */
945 if (modifier == 'E')
f0f1bf85 946 goto bad_format;
ec4b0518
UD
947 {
948 int year = tp->tm_year + TM_YEAR_BASE;
949 int days = iso_week_days (tp->tm_yday, tp->tm_wday);
950
951 if (days < 0)
952 {
953 /* This ISO week belongs to the previous year. */
954 year--;
955 days = iso_week_days (tp->tm_yday + (365 + __isleap (year)),
956 tp->tm_wday);
957 }
958 else
959 {
960 int d = iso_week_days (tp->tm_yday - (365 + __isleap (year)),
961 tp->tm_wday);
962 if (0 <= d)
963 {
964 /* This ISO week belongs to the next year. */
965 year++;
966 days = d;
967 }
968 }
969
970 switch (*f)
971 {
972 case 'g':
973 DO_NUMBER (2, (year % 100 + 100) % 100);
974
975 case 'G':
976 DO_NUMBER (1, year);
f0f1bf85 977
ec4b0518
UD
978 default:
979 DO_NUMBER (2, days / 7 + 1);
980 }
981 }
339841f9 982
28f540f4 983 case 'W':
ec4b0518 984 if (modifier == 'E')
f0f1bf85
UD
985 goto bad_format;
986
ec4b0518 987 DO_NUMBER (2, (tp->tm_yday - (tp->tm_wday - 1 + 7) % 7 + 7) / 7);
28f540f4
RM
988
989 case 'w':
ec4b0518 990 if (modifier == 'E')
f0f1bf85
UD
991 goto bad_format;
992
ec4b0518 993 DO_NUMBER (1, tp->tm_wday);
28f540f4 994
28f540f4 995 case 'Y':
d68171ed
UD
996#if HAVE_STRUCT_ERA_ENTRY
997 if (modifier == 'E')
998 {
999 struct era_entry *era = _nl_get_era_entry (tp);
1000 if (era)
1001 {
1002 subfmt = strchr (era->name_fmt, '\0') + 1;
1003 goto subformat;
1004 }
1005 }
f0f1bf85 1006#endif
ec4b0518
UD
1007 if (modifier == 'O')
1008 goto bad_format;
1009 else
1010 DO_NUMBER (1, tp->tm_year + TM_YEAR_BASE);
28f540f4
RM
1011
1012 case 'y':
d68171ed
UD
1013#if HAVE_STRUCT_ERA_ENTRY
1014 if (modifier == 'E')
1015 {
1016 struct era_entry *era = _nl_get_era_entry (tp);
1017 if (era)
1018 {
1019 int delta = tp->tm_year - era->start_date[0];
1020 DO_NUMBER (1, (era->offset
1021 + (era->direction == '-' ? -delta : delta)));
1022 }
1023 }
f0f1bf85 1024#endif
ec4b0518 1025 DO_NUMBER (2, (tp->tm_year % 100 + 100) % 100);
28f540f4
RM
1026
1027 case 'Z':
cf29ffbe
UD
1028 if (change_case)
1029 {
1030 to_uppcase = 0;
1031 to_lowcase = 1;
1032 }
26761c28 1033 cpy (zonelen, zone);
28f540f4
RM
1034 break;
1035
f0f1bf85
UD
1036 case 'z': /* GNU extension. */
1037 if (tp->tm_isdst < 0)
1038 break;
1039
2de99474 1040 {
2de99474 1041 int diff;
f0f1bf85
UD
1042#if HAVE_TM_GMTOFF
1043 diff = tp->tm_gmtoff;
1044#else
1045 struct tm gtm;
b33f91e9
UD
1046 struct tm ltm;
1047 time_t lt;
1048
1049 ltm = *tp;
1050 lt = mktime (&ltm);
2de99474 1051
f0f1bf85 1052 if (lt == (time_t) -1)
2de99474 1053 {
f0f1bf85
UD
1054 /* mktime returns -1 for errors, but -1 is also a
1055 valid time_t value. Check whether an error really
1056 occurred. */
1057 struct tm tm;
1058 localtime_r (&lt, &tm);
1059
1060 if ((ltm.tm_sec ^ tm.tm_sec)
1061 | (ltm.tm_min ^ tm.tm_min)
1062 | (ltm.tm_hour ^ tm.tm_hour)
1063 | (ltm.tm_mday ^ tm.tm_mday)
1064 | (ltm.tm_mon ^ tm.tm_mon)
1065 | (ltm.tm_year ^ tm.tm_year))
1066 break;
1067 }
dcf0671d 1068
f0f1bf85
UD
1069 if (! gmtime_r (&lt, &gtm))
1070 break;
dcf0671d 1071
f0f1bf85
UD
1072 diff = tm_diff (&ltm, &gtm);
1073#endif
2de99474
UD
1074
1075 if (diff < 0)
1076 {
1077 add (1, *p = '-');
1078 diff = -diff;
1079 }
1080 else
1081 add (1, *p = '+');
1082
f0f1bf85
UD
1083 diff /= 60;
1084 DO_NUMBER (4, (diff / 60) * 100 + diff % 60);
2de99474
UD
1085 }
1086
d68171ed
UD
1087 case '\0': /* GNU extension: % at end of format. */
1088 --f;
1089 /* Fall through. */
28f540f4 1090 default:
ec4b0518
UD
1091 /* Unknown format; output the format, including the '%',
1092 since this is most likely the right thing to do if a
1093 multibyte string has been misparsed. */
f0f1bf85 1094 bad_format:
ec4b0518
UD
1095 {
1096 int flen;
d68171ed 1097 for (flen = 1; f[1 - flen] != '%'; flen++)
ec4b0518
UD
1098 continue;
1099 cpy (flen, &f[1 - flen]);
1100 }
28f540f4
RM
1101 break;
1102 }
1103 }
1104
0793d348 1105 if (p)
28f540f4
RM
1106 *p = '\0';
1107 return i;
1108}