]> git.ipfire.org Git - thirdparty/glibc.git/blame - time/strftime.c
update from main archive 961207
[thirdparty/glibc.git] / time / strftime.c
CommitLineData
b7459e56 1/* Copyright (C) 1991, 92, 93, 94, 95, 96 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
ec4b0518 30# define MULTIBYTE_IS_FORMAT_SAFE 1
0793d348
RM
31# define STDC_HEADERS 1
32# include <ansidecl.h>
33# include "../locale/localeinfo.h"
34#endif
35
0793d348 36#include <sys/types.h> /* Some systems define `time_t' here. */
75cd5204
RM
37
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
28f540f4 48
ec4b0518
UD
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)
56
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;
0793d348
RM
67#endif
68
69#if HAVE_LIMITS_H
70# include <limits.h>
28f540f4
RM
71#endif
72
0793d348
RM
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
80
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. */
88
89#ifndef PTR
90#ifdef __STDC__
91#define PTR void *
92#else
93#define PTR char *
94#endif
95#endif
96
f0f1bf85
UD
97#ifndef CHAR_BIT
98#define CHAR_BIT 8
99#endif
100
101#define TYPE_SIGNED(t) ((t) -1 < 0)
102
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))
110
111#define TM_YEAR_BASE 1900
112
ec4b0518
UD
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
119
2de99474 120
fafaa44e
UD
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
129static struct tm *gmtime_r __P ((const time_t *, struct tm *));
130static struct tm *
131gmtime_r (t, tp)
132 const time_t *t;
133 struct tm *tp;
134{
135 struct tm *l = gmtime (t);
136 if (! l)
137 return 0;
138 *tp = *l;
139 return tp;
140}
141# endif /* ! HAVE_TM_GMTOFF */
142
143/* Approximate localtime_r as best we can in its absence. */
144#define localtime_r my_localtime_r
145static struct tm *localtime_r __P ((const time_t *, struct tm *));
146static struct tm *
147localtime_r (t, tp)
148 const time_t *t;
149 struct tm *tp;
150{
151 struct tm *l = localtime (t);
152 if (! l)
153 return 0;
154 *tp = *l;
155 return tp;
156}
157# endif /* ! HAVE_LOCALTIME_R */
158#endif /* ! defined (_LIBC) */
159
160
28f540f4
RM
161#define add(n, f) \
162 do \
163 { \
164 i += (n); \
165 if (i >= maxsize) \
166 return 0; \
167 else \
0793d348 168 if (p) \
28f540f4
RM
169 { \
170 f; \
171 p += (n); \
172 } \
173 } while (0)
2de99474 174#define cpy(n, s) add ((n), memcpy((PTR) p, (PTR) (s), (n)))
0793d348 175
f0f1bf85
UD
176#if ! HAVE_TM_GMTOFF
177/* Yield the difference between *A and *B,
178 measured in seconds, ignoring leap seconds. */
179static int tm_diff __P ((const struct tm *, const struct tm *));
180static int
181tm_diff (a, b)
182 const struct tm *a;
183 const struct tm *b;
184{
ec4b0518
UD
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;
f0f1bf85
UD
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));
201}
202#endif /* ! HAVE_TM_GMTOFF */
28f540f4 203
b7459e56
RM
204
205
ec4b0518
UD
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)
213static int iso_week_days __P ((int, int));
214#ifdef __GNUC__
28f540f4
RM
215inline
216#endif
ec4b0518
UD
217static int
218iso_week_days (yday, wday)
219 int yday;
220 int wday;
28f540f4 221{
ec4b0518
UD
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);
28f540f4
RM
227}
228
ec4b0518 229
0793d348
RM
230#ifndef _NL_CURRENT
231static char const weekday_name[][10] =
232 {
233 "Sunday", "Monday", "Tuesday", "Wednesday",
234 "Thursday", "Friday", "Saturday"
235 };
236static char const month_name[][10] =
237 {
238 "January", "February", "March", "April", "May", "June",
239 "July", "August", "September", "October", "November", "December"
240 };
241#endif
28f540f4
RM
242
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. */
249size_t
0793d348
RM
250strftime (s, maxsize, format, tp)
251 char *s;
252 size_t maxsize;
253 const char *format;
254 register const struct tm *tp;
28f540f4 255{
0793d348
RM
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,
b20e47cb 263 hour12 > 11 ? PM_STR : AM_STR);
d68171ed
UD
264 size_t aw_len = strlen (a_wkday);
265 size_t am_len = strlen (a_month);
0793d348
RM
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;
b20e47cb 272 const char *const ampm = "AMPM" + 2 * (hour12 > 11);
0793d348
RM
273 size_t aw_len = 3;
274 size_t am_len = 3;
275 size_t ap_len = 2;
276#endif
2de99474
UD
277 size_t wkday_len = strlen (f_wkday);
278 size_t month_len = strlen (f_month);
0793d348 279 const char *zone;
28f540f4
RM
280 size_t zonelen;
281 register size_t i = 0;
282 register char *p = s;
0793d348 283 register const char *f;
28f540f4 284
0793d348
RM
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
ec4b0518
UD
293 if (! zone)
294 zone = ""; /* POSIX.2 requires the empty string here. */
0793d348
RM
295
296 zonelen = strlen (zone);
28f540f4
RM
297
298 if (hour12 > 12)
299 hour12 -= 12;
300 else
301 if (hour12 == 0) hour12 = 12;
302
303 for (f = format; *f != '\0'; ++f)
304 {
ec4b0518
UD
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. */
f0f1bf85 309 int negative_number; /* 1 if the number is negative. */
ec4b0518 310 const char *subfmt;
f0f1bf85
UD
311 char *bufp;
312 char buf[1 + (sizeof (int) < sizeof (time_t)
313 ? INT_STRLEN_BOUND (time_t)
314 : INT_STRLEN_BOUND (int))];
28f540f4 315
ec4b0518
UD
316#if DO_MULTIBYTE
317
318 switch (*f)
28f540f4 319 {
ec4b0518
UD
320 case '%':
321 break;
322
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;
347
348 default:
349 /* Copy this multibyte sequence until we reach its end, find
350 an error, or come back to the initial shift state. */
351 {
352 mbstate_t mbstate = mbstate_zero;
353 size_t len = 0;
354
355 do
356 {
357 size_t bytes = mbrlen (f + len, (size_t) -1, &mbstate);
358
359 if (bytes == 0)
360 break;
361
362 if (bytes == (size_t) -2 || bytes == (size_t) -1)
363 {
364 len++;
365 break;
366 }
367
368 len += bytes;
369 }
370 while (! mbsinit (&mbstate));
371
372 cpy (len, f);
373 continue;
374 }
28f540f4
RM
375 }
376
ec4b0518
UD
377#else /* ! DO_MULTIBYTE */
378
379 /* Either multibyte encodings are not supported, or they are
380 safe for formats, so any non-'%' byte can be copied through. */
28f540f4
RM
381 if (*f != '%')
382 {
2de99474 383 add (1, *p = *f);
28f540f4
RM
384 continue;
385 }
386
ec4b0518
UD
387#endif /* ! DO_MULTIBYTE */
388
b7459e56 389 /* Check for flags that can modify a number format. */
28f540f4 390 ++f;
b7459e56
RM
391 switch (*f)
392 {
393 case '_':
b7459e56 394 case '-':
ec4b0518 395 pad = *f++;
b7459e56 396 break;
ec4b0518 397
b7459e56 398 default:
ec4b0518 399 pad = 0;
b7459e56
RM
400 break;
401 }
402
f0f1bf85
UD
403 /* Check for modifiers. */
404 switch (*f)
405 {
406 case 'E':
f0f1bf85 407 case 'O':
ec4b0518 408 modifier = *f++;
f0f1bf85 409 break;
ec4b0518 410
f0f1bf85 411 default:
ec4b0518 412 modifier = 0;
f0f1bf85
UD
413 break;
414 }
415
b7459e56 416 /* Now do the specified format. */
28f540f4
RM
417 switch (*f)
418 {
f0f1bf85
UD
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
423
28f540f4 424 case '%':
ec4b0518 425 if (modifier != 0)
f0f1bf85 426 goto bad_format;
2de99474 427 add (1, *p = *f);
28f540f4
RM
428 break;
429
430 case 'a':
ec4b0518 431 if (modifier != 0)
f0f1bf85 432 goto bad_format;
2de99474 433 cpy (aw_len, a_wkday);
28f540f4
RM
434 break;
435
436 case 'A':
ec4b0518 437 if (modifier != 0)
f0f1bf85 438 goto bad_format;
2de99474 439 cpy (wkday_len, f_wkday);
28f540f4
RM
440 break;
441
442 case 'b':
ec4b0518
UD
443 case 'h': /* POSIX.2 extension. */
444 if (modifier != 0)
f0f1bf85 445 goto bad_format;
2de99474 446 cpy (am_len, a_month);
28f540f4
RM
447 break;
448
449 case 'B':
ec4b0518 450 if (modifier != 0)
f0f1bf85 451 goto bad_format;
2de99474 452 cpy (month_len, f_month);
28f540f4
RM
453 break;
454
455 case 'c':
ec4b0518 456 if (modifier == 'O')
f0f1bf85 457 goto bad_format;
0793d348 458#ifdef _NL_CURRENT
ec4b0518
UD
459 if (! (modifier == 'E'
460 && *(subfmt = _NL_CURRENT (LC_TIME, ERA_D_T_FMT)) != '\0'))
f0f1bf85 461 subfmt = _NL_CURRENT (LC_TIME, D_T_FMT);
0793d348 462#else
f65fd747 463 subfmt = "%a %b %e %H:%M:%S %Y";
0793d348 464#endif
f0f1bf85 465
28f540f4
RM
466 subformat:
467 {
468 size_t len = strftime (p, maxsize - i, subfmt, tp);
0793d348
RM
469 if (len == 0 && *subfmt)
470 return 0;
ec42724d 471 add (len, ;);
28f540f4
RM
472 }
473 break;
474
ec4b0518
UD
475 case 'C': /* POSIX.2 extension. */
476 if (modifier == 'O')
f0f1bf85 477 goto bad_format;
d68171ed
UD
478#if HAVE_STRUCT_ERA_ENTRY
479 if (modifier == 'E')
480 {
481 struct era_entry *era = _nl_get_era_entry (tp);
482 if (era)
483 {
484 size_t len = strlen (era->name_fmt);
485 cpy (len, era->name_fmt);
486 break;
487 }
488 }
f0f1bf85 489#endif
ec4b0518
UD
490 {
491 int year = tp->tm_year + TM_YEAR_BASE;
492 DO_NUMBER (1, year / 100 - (year % 100 < 0));
493 }
28f540f4 494
0793d348 495 case 'x':
ec4b0518 496 if (modifier == 'O')
f0f1bf85 497 goto bad_format;
0793d348 498#ifdef _NL_CURRENT
ec4b0518
UD
499 if (! (modifier == 'E'
500 && *(subfmt = _NL_CURRENT (LC_TIME, ERA_D_FMT)) != '\0'))
f0f1bf85 501 subfmt = _NL_CURRENT (LC_TIME, D_FMT);
0793d348
RM
502 goto subformat;
503#endif
504 /* Fall through. */
ec4b0518
UD
505 case 'D': /* POSIX.2 extension. */
506 if (modifier != 0)
507 goto bad_format;
28f540f4
RM
508 subfmt = "%m/%d/%y";
509 goto subformat;
510
511 case 'd':
ec4b0518 512 if (modifier == 'E')
f0f1bf85
UD
513 goto bad_format;
514
b7459e56 515 DO_NUMBER (2, tp->tm_mday);
28f540f4 516
ec4b0518
UD
517 case 'e': /* POSIX.2 extension. */
518 if (modifier == 'E')
f0f1bf85
UD
519 goto bad_format;
520
2064087b 521 DO_NUMBER_SPACEPAD (2, tp->tm_mday);
b7459e56 522
f0f1bf85 523 /* All numeric formats set DIGITS and NUMBER_VALUE and then
b7459e56
RM
524 jump to one of these two labels. */
525
2064087b
RM
526 do_number_spacepad:
527 /* Force `_' flag. */
ec4b0518 528 pad = '_';
b7459e56
RM
529
530 do_number:
f0f1bf85
UD
531 /* Format the number according to the MODIFIER flag. */
532
533#ifdef _NL_CURRENT
ec4b0518 534 if (modifier == 'O' && 0 <= number_value)
f0f1bf85 535 {
c4029823
UD
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);
ec4b0518 539
c4029823 540 if (cp != NULL)
ec4b0518
UD
541 {
542 size_t digitlen = strlen (cp);
543 if (digitlen != 0)
544 {
545 cpy (digitlen, cp);
546 break;
547 }
548 }
f0f1bf85
UD
549 }
550#endif
b7459e56 551 {
f0f1bf85 552 unsigned int u = number_value;
b7459e56 553
f0f1bf85
UD
554 bufp = buf + sizeof (buf);
555 negative_number = number_value < 0;
b7459e56 556
f0f1bf85
UD
557 if (negative_number)
558 u = -u;
b7459e56 559
f0f1bf85
UD
560 do
561 *--bufp = u % 10 + '0';
562 while ((u /= 10) != 0);
563 }
b7459e56 564
f0f1bf85
UD
565 do_number_sign_and_padding:
566 if (negative_number)
567 *--bufp = '-';
568
ec4b0518 569 if (pad != '-')
f0f1bf85
UD
570 {
571 int padding = digits - (buf + sizeof (buf) - bufp);
572
ec4b0518 573 if (pad == '_')
f0f1bf85
UD
574 {
575 while (0 < padding--)
576 *--bufp = ' ';
577 }
578 else
579 {
580 bufp += negative_number;
581 while (0 < padding--)
582 *--bufp = '0';
583 if (negative_number)
584 *--bufp = '-';
585 }
586 }
587
588 cpy (buf + sizeof (buf) - bufp, bufp);
f0f1bf85 589 break;
b7459e56 590
28f540f4
RM
591
592 case 'H':
ec4b0518 593 if (modifier == 'E')
f0f1bf85
UD
594 goto bad_format;
595
b7459e56 596 DO_NUMBER (2, tp->tm_hour);
28f540f4
RM
597
598 case 'I':
ec4b0518 599 if (modifier == 'E')
f0f1bf85
UD
600 goto bad_format;
601
b7459e56 602 DO_NUMBER (2, hour12);
28f540f4
RM
603
604 case 'k': /* GNU extension. */
ec4b0518 605 if (modifier == 'E')
f0f1bf85
UD
606 goto bad_format;
607
2064087b 608 DO_NUMBER_SPACEPAD (2, tp->tm_hour);
28f540f4
RM
609
610 case 'l': /* GNU extension. */
ec4b0518 611 if (modifier == 'E')
f0f1bf85
UD
612 goto bad_format;
613
2064087b 614 DO_NUMBER_SPACEPAD (2, hour12);
28f540f4
RM
615
616 case 'j':
ec4b0518 617 if (modifier == 'E')
f0f1bf85
UD
618 goto bad_format;
619
b7459e56 620 DO_NUMBER (3, 1 + tp->tm_yday);
28f540f4
RM
621
622 case 'M':
ec4b0518 623 if (modifier == 'E')
f0f1bf85
UD
624 goto bad_format;
625
b7459e56 626 DO_NUMBER (2, tp->tm_min);
28f540f4
RM
627
628 case 'm':
ec4b0518 629 if (modifier == 'E')
f0f1bf85
UD
630 goto bad_format;
631
b7459e56 632 DO_NUMBER (2, tp->tm_mon + 1);
28f540f4 633
ec4b0518 634 case 'n': /* POSIX.2 extension. */
28f540f4
RM
635 add (1, *p = '\n');
636 break;
637
638 case 'p':
2de99474 639 cpy (ap_len, ampm);
28f540f4
RM
640 break;
641
642 case 'R': /* GNU extension. */
643 subfmt = "%H:%M";
644 goto subformat;
645
ec4b0518
UD
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";
28f540f4
RM
651 goto subformat;
652
653 case 'S':
ec4b0518
UD
654 if (modifier == 'E')
655 goto bad_format;
f0f1bf85 656
b7459e56 657 DO_NUMBER (2, tp->tm_sec);
28f540f4 658
2de99474 659 case 's': /* GNU extension. */
f0f1bf85 660 {
b33f91e9
UD
661 struct tm ltm;
662 time_t t;
663
664 ltm = *tp;
665 t = mktime (&ltm);
f0f1bf85
UD
666
667 /* Generate string value for T using time_t arithmetic;
668 this works even if sizeof (long) < sizeof (time_t). */
669
670 bufp = buf + sizeof (buf);
671 negative_number = t < 0;
672
673 do
674 {
675 int d = t % 10;
676 t /= 10;
677
678 if (negative_number)
679 {
680 d = -d;
681
682 /* Adjust if division truncates to minus infinity. */
683 if (0 < -1 % 10 && d < 0)
684 {
685 t++;
686 d += 10;
687 }
688 }
689
690 *--bufp = d + '0';
691 }
692 while (t != 0);
693
694 digits = 1;
695 goto do_number_sign_and_padding;
2de99474 696 }
2de99474 697
0793d348 698 case 'X':
ec4b0518 699 if (modifier == 'O')
f0f1bf85 700 goto bad_format;
0793d348 701#ifdef _NL_CURRENT
ec4b0518
UD
702 if (! (modifier == 'E'
703 && *(subfmt = _NL_CURRENT (LC_TIME, ERA_T_FMT)) != '\0'))
f0f1bf85 704 subfmt = _NL_CURRENT (LC_TIME, T_FMT);
0793d348
RM
705 goto subformat;
706#endif
707 /* Fall through. */
ec4b0518 708 case 'T': /* POSIX.2 extension. */
28f540f4
RM
709 subfmt = "%H:%M:%S";
710 goto subformat;
711
ec4b0518 712 case 't': /* POSIX.2 extension. */
28f540f4
RM
713 add (1, *p = '\t');
714 break;
715
ec4b0518
UD
716 case 'u': /* POSIX.2 extension. */
717 DO_NUMBER (1, (tp->tm_wday - 1 + 7) % 7 + 1);
718
28f540f4 719 case 'U':
ec4b0518 720 if (modifier == 'E')
f0f1bf85
UD
721 goto bad_format;
722
ec4b0518 723 DO_NUMBER (2, (tp->tm_yday - tp->tm_wday + 7) / 7);
28f540f4 724
339841f9 725 case 'V':
ec4b0518
UD
726 case 'g': /* GNU extension. */
727 case 'G': /* GNU extension. */
728 if (modifier == 'E')
f0f1bf85 729 goto bad_format;
ec4b0518
UD
730 {
731 int year = tp->tm_year + TM_YEAR_BASE;
732 int days = iso_week_days (tp->tm_yday, tp->tm_wday);
733
734 if (days < 0)
735 {
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);
740 }
741 else
742 {
743 int d = iso_week_days (tp->tm_yday - (365 + __isleap (year)),
744 tp->tm_wday);
745 if (0 <= d)
746 {
747 /* This ISO week belongs to the next year. */
748 year++;
749 days = d;
750 }
751 }
752
753 switch (*f)
754 {
755 case 'g':
756 DO_NUMBER (2, (year % 100 + 100) % 100);
757
758 case 'G':
759 DO_NUMBER (1, year);
f0f1bf85 760
ec4b0518
UD
761 default:
762 DO_NUMBER (2, days / 7 + 1);
763 }
764 }
339841f9 765
28f540f4 766 case 'W':
ec4b0518 767 if (modifier == 'E')
f0f1bf85
UD
768 goto bad_format;
769
ec4b0518 770 DO_NUMBER (2, (tp->tm_yday - (tp->tm_wday - 1 + 7) % 7 + 7) / 7);
28f540f4
RM
771
772 case 'w':
ec4b0518 773 if (modifier == 'E')
f0f1bf85
UD
774 goto bad_format;
775
ec4b0518 776 DO_NUMBER (1, tp->tm_wday);
28f540f4 777
28f540f4 778 case 'Y':
d68171ed
UD
779#if HAVE_STRUCT_ERA_ENTRY
780 if (modifier == 'E')
781 {
782 struct era_entry *era = _nl_get_era_entry (tp);
783 if (era)
784 {
785 subfmt = strchr (era->name_fmt, '\0') + 1;
786 goto subformat;
787 }
788 }
f0f1bf85 789#endif
ec4b0518
UD
790 if (modifier == 'O')
791 goto bad_format;
792 else
793 DO_NUMBER (1, tp->tm_year + TM_YEAR_BASE);
28f540f4
RM
794
795 case 'y':
d68171ed
UD
796#if HAVE_STRUCT_ERA_ENTRY
797 if (modifier == 'E')
798 {
799 struct era_entry *era = _nl_get_era_entry (tp);
800 if (era)
801 {
802 int delta = tp->tm_year - era->start_date[0];
803 DO_NUMBER (1, (era->offset
804 + (era->direction == '-' ? -delta : delta)));
805 }
806 }
f0f1bf85 807#endif
ec4b0518 808 DO_NUMBER (2, (tp->tm_year % 100 + 100) % 100);
28f540f4
RM
809
810 case 'Z':
26761c28 811 cpy (zonelen, zone);
28f540f4
RM
812 break;
813
f0f1bf85
UD
814 case 'z': /* GNU extension. */
815 if (tp->tm_isdst < 0)
816 break;
817
2de99474 818 {
2de99474 819 int diff;
f0f1bf85
UD
820#if HAVE_TM_GMTOFF
821 diff = tp->tm_gmtoff;
822#else
823 struct tm gtm;
b33f91e9
UD
824 struct tm ltm;
825 time_t lt;
826
827 ltm = *tp;
828 lt = mktime (&ltm);
2de99474 829
f0f1bf85 830 if (lt == (time_t) -1)
2de99474 831 {
f0f1bf85
UD
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);
837
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;
845 }
dcf0671d 846
f0f1bf85
UD
847 if (! gmtime_r (&lt, &gtm))
848 break;
dcf0671d 849
f0f1bf85
UD
850 diff = tm_diff (&ltm, &gtm);
851#endif
2de99474
UD
852
853 if (diff < 0)
854 {
855 add (1, *p = '-');
856 diff = -diff;
857 }
858 else
859 add (1, *p = '+');
860
f0f1bf85
UD
861 diff /= 60;
862 DO_NUMBER (4, (diff / 60) * 100 + diff % 60);
2de99474
UD
863 }
864
d68171ed
UD
865 case '\0': /* GNU extension: % at end of format. */
866 --f;
867 /* Fall through. */
28f540f4 868 default:
ec4b0518
UD
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. */
f0f1bf85 872 bad_format:
ec4b0518
UD
873 {
874 int flen;
d68171ed 875 for (flen = 1; f[1 - flen] != '%'; flen++)
ec4b0518
UD
876 continue;
877 cpy (flen, &f[1 - flen]);
878 }
28f540f4
RM
879 break;
880 }
881 }
882
0793d348 883 if (p)
28f540f4
RM
884 *p = '\0';
885 return i;
886}