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
16 not, write to the Free Software Foundation, Inc., 675 Mass Ave,
17 Cambridge, MA 02139, USA. */
24 # define HAVE_LIMITS_H 1
26 # define HAVE_TM_GMTOFF 1
27 # define HAVE_TM_ZONE 1
28 # define STDC_HEADERS 1
29 # include <ansidecl.h>
30 # include "../locale/localeinfo.h"
33 #include <sys/types.h> /* Some systems define `time_t' here. */
35 #ifdef TIME_WITH_SYS_TIME
36 # include <sys/time.h>
39 # ifdef HAVE_SYS_TIME_H
40 # include <sys/time.h>
59 # define memcpy(d, s, n) bcopy (s, d, n)
63 #if defined (__GNUC__) || (defined (__STDC__) && __STDC__)
64 #define __P(args) args
82 #define TYPE_SIGNED(t) ((t) -1 < 0)
84 /* Bound on length of the string representing an integer value of type t.
85 Subtract one for the sign bit if t is signed;
86 302 / 1000 is log10 (2) rounded up;
87 add one for integer division truncation;
88 add one more for a minus sign if t is signed. */
89 #define INT_STRLEN_BOUND(t) \
90 ((sizeof (t) * CHAR_BIT - TYPE_SIGNED (t)) * 302 / 100 + 1 + TYPE_SIGNED (t))
92 #define TM_YEAR_BASE 1900
96 # define gmtime_r __gmtime_r
97 # define localtime_r __localtime_r
99 # if ! HAVE_LOCALTIME_R
100 # if ! HAVE_TM_GMTOFF
101 /* Approximate gmtime_r as best we can in its absence. */
102 #define gmtime_r my_gmtime_r
103 static struct tm *gmtime_r __P ((const time_t *, struct tm *));
109 struct tm *l = gmtime (t);
115 # endif /* ! HAVE_TM_GMTOFF */
117 /* Approximate localtime_r as best we can in its absence. */
118 #define localtime_r my_localtime_r
119 static struct tm *localtime_r __P ((const time_t *, struct tm *));
125 struct tm *l = localtime (t);
131 # endif /* ! HAVE_LOCALTIME_R */
132 #endif /* ! defined (_LIBC) */
135 static unsigned int week __P ((const struct tm *const, int, int));
151 #define cpy(n, s) add ((n), memcpy((PTR) p, (PTR) (s), (n)))
154 /* Yield the difference between *A and *B,
155 measured in seconds, ignoring leap seconds. */
156 static int tm_diff __P ((const struct tm *, const struct tm *));
162 int ay = a->tm_year + TM_YEAR_BASE - 1;
163 int by = b->tm_year + TM_YEAR_BASE - 1;
164 /* Divide years by 100, rounding towards minus infinity. */
165 int ac = ay / 100 - (ay % 100 < 0);
166 int bc = by / 100 - (by % 100 < 0);
167 int intervening_leap_days =
168 ((ay >> 2) - (by >> 2)) - (ac - bc) + ((ac >> 2) - (bc >> 2));
170 int days = (365 * years + intervening_leap_days
171 + (a->tm_yday - b->tm_yday));
172 return (60 * (60 * (24 * days + (a->tm_hour - b->tm_hour))
173 + (a->tm_min - b->tm_min))
174 + (a->tm_sec - b->tm_sec));
176 #endif /* ! HAVE_TM_GMTOFF */
180 /* Return the week in the year specified by TP,
181 with weeks starting on STARTING_DAY. */
186 week (tp, starting_day, max_preceding)
187 const struct tm *const tp;
193 wday = tp->tm_wday - starting_day;
197 /* Set DL to the day in the year of the first day of the week
198 containing the day specified in TP. */
199 dl = tp->tm_yday - wday;
201 /* For the computation following ISO 8601:1988 we set the number of
202 the week containing January 1st to 1 if this week has more than
203 MAX_PRECEDING days in the new year. For ISO 8601 this number is
204 3, for the other representation it is 7 (i.e., not to be
206 base = ((dl + 7) % 7) > max_preceding ? 1 : 0;
208 /* If DL is negative we compute the result as 0 unless we have to
209 compute it according ISO 8601. In this case we have to return 53
210 or 1 if the week containing January 1st has less than 4 days in
211 the new year or not. If DL is not negative we calculate the
212 number of complete weeks for our week (DL / 7) plus 1 (because
213 only for DL < 0 we are in week 0/53 and plus the number of the
214 first week computed in the last step. */
215 return dl < 0 ? (dl < -max_preceding ? 53 : base)
220 static char const weekday_name[][10] =
222 "Sunday", "Monday", "Tuesday", "Wednesday",
223 "Thursday", "Friday", "Saturday"
225 static char const month_name[][10] =
227 "January", "February", "March", "April", "May", "June",
228 "July", "August", "September", "October", "November", "December"
232 /* Write information from TP into S according to the format
233 string FORMAT, writing no more that MAXSIZE characters
234 (including the terminating '\0') and returning number of
235 characters written. If S is NULL, nothing will be written
236 anywhere, so to determine how many characters would be
237 written, use NULL for S and (size_t) UINT_MAX for MAXSIZE. */
239 strftime (s, maxsize, format, tp)
243 register const struct tm *tp;
245 int hour12 = tp->tm_hour;
247 const char *const a_wkday = _NL_CURRENT (LC_TIME, ABDAY_1 + tp->tm_wday);
248 const char *const f_wkday = _NL_CURRENT (LC_TIME, DAY_1 + tp->tm_wday);
249 const char *const a_month = _NL_CURRENT (LC_TIME, ABMON_1 + tp->tm_mon);
250 const char *const f_month = _NL_CURRENT (LC_TIME, MON_1 + tp->tm_mon);
251 const char *const ampm = _NL_CURRENT (LC_TIME,
252 hour12 > 11 ? PM_STR : AM_STR);
253 size_t aw_len = strlen(a_wkday);
254 size_t am_len = strlen(a_month);
255 size_t ap_len = strlen (ampm);
257 const char * const*alt_digits = &_NL_CURRENT (LC_TIME, ALT_DIGITS);
258 int nr_alt_digits = (_NL_CURRENT (LC_TIME, ALT_DIGITS + 1) - *alt_digits);
260 const char *const f_wkday = weekday_name[tp->tm_wday];
261 const char *const f_month = month_name[tp->tm_mon];
262 const char *const a_wkday = f_wkday;
263 const char *const a_month = f_month;
264 const char *const ampm = "AMPM" + 2 * (hour12 > 11);
269 size_t wkday_len = strlen (f_wkday);
270 size_t month_len = strlen (f_month);
271 const unsigned int y_week0 = week (tp, 0, 7);
272 const unsigned int y_week1 = week (tp, 1, 7);
273 const unsigned int y_week2 = week (tp, 1, 3);
276 register size_t i = 0;
277 register char *p = s;
278 register const char *f;
282 zone = (const char *) tp->tm_zone;
285 if (!(zone && *zone) && tp->tm_isdst >= 0)
286 zone = tzname[tp->tm_isdst];
288 if (!(zone && *zone))
291 zonelen = strlen (zone);
296 if (hour12 == 0) hour12 = 12;
298 for (f = format; *f != '\0'; ++f)
300 enum { pad_zero, pad_space, pad_none } pad; /* Padding for number. */
301 unsigned int digits; /* Max digits for numeric format. */
302 unsigned int number_value; /* Numeric value to be printed. */
303 int negative_number; /* 1 if the number is negative. */
304 const char *subfmt = "";
305 enum { none, alternate, era } modifier;
307 char buf[1 + (sizeof (int) < sizeof (time_t)
308 ? INT_STRLEN_BOUND (time_t)
309 : INT_STRLEN_BOUND (int))];
314 /* Non-ASCII, may be a multibyte. */
315 int len = mblen (f, strlen (f));
330 /* Check for flags that can modify a number format. */
347 /* Check for modifiers. */
356 modifier = alternate;
363 /* Now do the specified format. */
366 #define DO_NUMBER(d, v) \
367 digits = d; number_value = v; goto do_number
368 #define DO_NUMBER_SPACEPAD(d, v) \
369 digits = d; number_value = v; goto do_number_spacepad
371 case '\0': /* GNU extension: % at end of format. */
375 if (modifier != none)
381 if (modifier != none)
383 cpy (aw_len, a_wkday);
387 if (modifier != none)
389 cpy (wkday_len, f_wkday);
393 case 'h': /* GNU extension. */
394 if (modifier != none)
396 cpy (am_len, a_month);
400 if (modifier != none)
402 cpy (month_len, f_month);
406 if (modifier == alternate)
410 subfmt = _NL_CURRENT (LC_TIME, ERA_D_T_FMT);
412 subfmt = _NL_CURRENT (LC_TIME, D_T_FMT);
414 subfmt = "%a %b %e %H:%M:%S %Z %Y";
419 size_t len = strftime (p, maxsize - i, subfmt, tp);
420 if (len == 0 && *subfmt)
427 if (modifier == alternate)
430 /* XXX I'm not sure about this. --drepper@gnu */
431 if (modifier == era &&
432 *(subfmt = _NL_CURRENT (LC_TIME, ERA)) != '\0')
435 DO_NUMBER (2, (1900 + tp->tm_year) / 100);
438 if (modifier == alternate)
442 subfmt = _NL_CURRENT (LC_TIME, ERA_D_FMT);
444 subfmt = _NL_CURRENT (LC_TIME, D_FMT);
448 case 'D': /* GNU extension. */
456 DO_NUMBER (2, tp->tm_mday);
458 case 'e': /* GNU extension: %d, but blank-padded. */
462 DO_NUMBER_SPACEPAD (2, tp->tm_mday);
464 /* All numeric formats set DIGITS and NUMBER_VALUE and then
465 jump to one of these two labels. */
468 /* Force `_' flag. */
472 /* Format the number according to the MODIFIER flag. */
475 if (modifier == alternate && 0 <= number_value
476 && number_value < (unsigned int) nr_alt_digits)
478 /* ALT_DIGITS is the first entry in an array with
479 alternative digit symbols. */
480 size_t digitlen = strlen (*(alt_digits + number_value));
483 cpy (digitlen, *(alt_digits + number_value));
484 goto done_with_number;
488 unsigned int u = number_value;
490 bufp = buf + sizeof (buf);
491 negative_number = number_value < 0;
497 *--bufp = u % 10 + '0';
498 while ((u /= 10) != 0);
501 do_number_sign_and_padding:
507 int padding = digits - (buf + sizeof (buf) - bufp);
509 if (pad == pad_space)
511 while (0 < padding--)
516 bufp += negative_number;
517 while (0 < padding--)
524 cpy (buf + sizeof (buf) - bufp, bufp);
536 DO_NUMBER (2, tp->tm_hour);
542 DO_NUMBER (2, hour12);
544 case 'k': /* GNU extension. */
548 DO_NUMBER_SPACEPAD (2, tp->tm_hour);
550 case 'l': /* GNU extension. */
554 DO_NUMBER_SPACEPAD (2, hour12);
560 DO_NUMBER (3, 1 + tp->tm_yday);
566 DO_NUMBER (2, tp->tm_min);
572 DO_NUMBER (2, tp->tm_mon + 1);
574 case 'n': /* GNU extension. */
582 case 'R': /* GNU extension. */
586 case 'r': /* GNU extension. */
587 subfmt = "%I:%M:%S %p";
594 DO_NUMBER (2, tp->tm_sec);
596 case 's': /* GNU extension. */
599 time_t t = mktime (<m);
601 /* Generate string value for T using time_t arithmetic;
602 this works even if sizeof (long) < sizeof (time_t). */
604 bufp = buf + sizeof (buf);
605 negative_number = t < 0;
616 /* Adjust if division truncates to minus infinity. */
617 if (0 < -1 % 10 && d < 0)
629 goto do_number_sign_and_padding;
633 if (modifier == alternate)
637 subfmt = _NL_CURRENT (LC_TIME, ERA_T_FMT);
639 subfmt = _NL_CURRENT (LC_TIME, T_FMT);
643 case 'T': /* GNU extension. */
647 case 't': /* GNU extension. */
655 DO_NUMBER (2, y_week0);
661 DO_NUMBER (2, y_week2);
667 DO_NUMBER (2, y_week1);
673 DO_NUMBER (2, tp->tm_wday);
678 && *(subfmt = _NL_CURRENT (LC_TIME, ERA_YEAR)) != '\0')
682 if (modifier == alternate)
685 DO_NUMBER (4, 1900 + tp->tm_year);
690 && *(subfmt = _NL_CURRENT (LC_TIME, ERA_YEAR)) != '\0')
693 DO_NUMBER (2, tp->tm_year % 100);
699 case 'z': /* GNU extension. */
700 if (tp->tm_isdst < 0)
706 diff = tp->tm_gmtoff;
710 time_t lt = mktime (<m);
712 if (lt == (time_t) -1)
714 /* mktime returns -1 for errors, but -1 is also a
715 valid time_t value. Check whether an error really
718 localtime_r (<, &tm);
720 if ((ltm.tm_sec ^ tm.tm_sec)
721 | (ltm.tm_min ^ tm.tm_min)
722 | (ltm.tm_hour ^ tm.tm_hour)
723 | (ltm.tm_mday ^ tm.tm_mday)
724 | (ltm.tm_mon ^ tm.tm_mon)
725 | (ltm.tm_year ^ tm.tm_year))
729 if (! gmtime_r (<, >m))
732 diff = tm_diff (<m, >m);
746 DO_NUMBER (4, (diff / 60) * 100 + diff % 60);
752 if (pad == pad_space)
754 else if (pad == pad_zero)
759 else if (modifier == alternate)