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_ZONE 1
27 # define STDC_HEADERS 1
28 # include <ansidecl.h>
29 # 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
78 /* Uncomment following line in the production version. */
82 static unsigned int week __P ((const struct tm *const, int, int));
98 #define cpy(n, s) add ((n), memcpy((PTR) p, (PTR) (s), (n)))
101 #define fmt(n, args) add((n), if (sprintf args != (n)) return 0)
103 #define fmt(n, args) add((n), sprintf args; if (strlen (p) != (n)) return 0)
108 /* Return the week in the year specified by TP,
109 with weeks starting on STARTING_DAY. */
114 week (tp, starting_day, max_preceding)
115 const struct tm *const tp;
121 wday = tp->tm_wday - starting_day;
125 /* Set DL to the day in the year of the first day of the week
126 containing the day specified in TP. */
127 dl = tp->tm_yday - wday;
129 /* For the computation following ISO 8601:1988 we set the number of
130 the week containing January 1st to 1 if this week has more than
131 MAX_PRECEDING days in the new year. For ISO 8601 this number is
132 3, for the other representation it is 7 (i.e., not to be
134 base = ((dl + 7) % 7) > max_preceding ? 1 : 0;
136 /* If DL is negative we compute the result as 0 unless we have to
137 compute it according ISO 8601. In this case we have to return 53
138 or 1 if the week containing January 1st has less than 4 days in
139 the new year or not. If DL is not negative we calculate the
140 number of complete weeks for our week (DL / 7) plus 1 (because
141 only for DL < 0 we are in week 0/53 and plus the number of the
142 first week computed in the last step. */
143 return dl < 0 ? (dl < -max_preceding ? 53 : base)
148 static char const weekday_name[][10] =
150 "Sunday", "Monday", "Tuesday", "Wednesday",
151 "Thursday", "Friday", "Saturday"
153 static char const month_name[][10] =
155 "January", "February", "March", "April", "May", "June",
156 "July", "August", "September", "October", "November", "December"
160 /* Write information from TP into S according to the format
161 string FORMAT, writing no more that MAXSIZE characters
162 (including the terminating '\0') and returning number of
163 characters written. If S is NULL, nothing will be written
164 anywhere, so to determine how many characters would be
165 written, use NULL for S and (size_t) UINT_MAX for MAXSIZE. */
167 strftime (s, maxsize, format, tp)
171 register const struct tm *tp;
173 int hour12 = tp->tm_hour;
175 const char *const a_wkday = _NL_CURRENT (LC_TIME, ABDAY_1 + tp->tm_wday);
176 const char *const f_wkday = _NL_CURRENT (LC_TIME, DAY_1 + tp->tm_wday);
177 const char *const a_month = _NL_CURRENT (LC_TIME, ABMON_1 + tp->tm_mon);
178 const char *const f_month = _NL_CURRENT (LC_TIME, MON_1 + tp->tm_mon);
179 const char *const ampm = _NL_CURRENT (LC_TIME,
180 hour12 > 11 ? PM_STR : AM_STR);
181 size_t aw_len = strlen(a_wkday);
182 size_t am_len = strlen(a_month);
183 size_t ap_len = strlen (ampm);
185 const char *const f_wkday = weekday_name[tp->tm_wday];
186 const char *const f_month = month_name[tp->tm_mon];
187 const char *const a_wkday = f_wkday;
188 const char *const a_month = f_month;
189 const char *const ampm = "AMPM" + 2 * (hour12 > 11);
194 size_t wkday_len = strlen (f_wkday);
195 size_t month_len = strlen (f_month);
196 const unsigned int y_week0 = week (tp, 0, 7);
197 const unsigned int y_week1 = week (tp, 1, 7);
198 const unsigned int y_week2 = week (tp, 1, 3);
201 register size_t i = 0;
202 register char *p = s;
203 register const char *f;
206 /* Initialize the buffer we will use for the sprintf format for numbers. */
211 zone = (const char *) tp->tm_zone;
214 if (!(zone && *zone) && tp->tm_isdst >= 0)
215 zone = tzname[tp->tm_isdst];
217 if (!(zone && *zone))
220 zonelen = strlen (zone);
225 if (hour12 == 0) hour12 = 12;
227 for (f = format; *f != '\0'; ++f)
229 enum { pad_zero, pad_space, pad_none } pad; /* Padding for number. */
230 unsigned int maxdigits; /* Max digits for numeric format. */
231 unsigned int number_value; /* Numeric value to be printed. */
237 /* Non-ASCII, may be a multibyte. */
238 int len = mblen (f, strlen (f));
253 /* Check for flags that can modify a number format. */
270 /* Now do the specified format. */
279 cpy (aw_len, a_wkday);
283 cpy (wkday_len, f_wkday);
287 case 'h': /* GNU extension. */
288 cpy (am_len, a_month);
292 cpy (month_len, f_month);
297 subfmt = _NL_CURRENT (LC_TIME, D_T_FMT);
299 subfmt = "%a %b %d %H:%M:%S %Z %Y";
303 size_t len = strftime (p, maxsize - i, subfmt, tp);
304 if (len == 0 && *subfmt)
310 #define DO_NUMBER(digits, value) \
311 maxdigits = digits; number_value = value; goto do_number
312 #define DO_NUMBER_SPACEPAD(digits, value) \
313 maxdigits = digits; number_value = value; goto do_number_spacepad
316 DO_NUMBER (2, (1900 + tp->tm_year) / 100);
320 subfmt = _NL_CURRENT (LC_TIME, D_FMT);
324 case 'D': /* GNU extension. */
329 DO_NUMBER (2, tp->tm_mday);
331 case 'e': /* GNU extension: %d, but blank-padded. */
332 DO_NUMBER_SPACEPAD (2, tp->tm_mday);
334 /* All numeric formats set MAXDIGITS and NUMBER_VALUE and then
335 jump to one of these two labels. */
338 /* Force `_' flag. */
343 /* Format the number according to the PAD flag. */
345 register char *nf = &number_fmt[1];
346 int printed = maxdigits;
353 *nf++ = '0' + maxdigits;
360 add (maxdigits, printed = sprintf (p, number_fmt, number_value));
362 add (maxdigits, sprintf (p, number_fmt, number_value);
363 printed = strlen (p));
365 /* Back up if fewer than MAXDIGITS chars written for pad_none. */
366 p -= maxdigits - printed;
367 i -= maxdigits - printed;
374 DO_NUMBER (2, tp->tm_hour);
377 DO_NUMBER (2, hour12);
379 case 'k': /* GNU extension. */
380 DO_NUMBER_SPACEPAD (2, tp->tm_hour);
382 case 'l': /* GNU extension. */
383 DO_NUMBER_SPACEPAD (2, hour12);
386 DO_NUMBER (3, 1 + tp->tm_yday);
389 DO_NUMBER (2, tp->tm_min);
392 DO_NUMBER (2, tp->tm_mon + 1);
394 case 'n': /* GNU extension. */
402 case 'R': /* GNU extension. */
406 case 'r': /* GNU extension. */
407 subfmt = "%I:%M:%S %p";
411 DO_NUMBER (2, tp->tm_sec);
413 case 's': /* GNU extension. */
415 struct tm writable_tm = *tp;
416 unsigned long int num = (unsigned long int) mktime (&writable_tm);
417 /* `3 * sizeof (unsigned long int)' is an approximation of
418 the size of the decimal representation of NUM, valid
420 int printed = 3 * sizeof (unsigned long int);
422 assert (sizeof (unsigned long int) <= 16);
424 add (maxdigits, printed = sprintf (p, "%lu", num));
426 add (maxdigits, sprintf (p, "%lu", num); printed = strlen (p));
428 /* Back up if fewer than MAXDIGITS chars written for pad_none. */
429 p -= maxdigits - printed;
430 i -= maxdigits - printed;
436 subfmt = _NL_CURRENT (LC_TIME, T_FMT);
440 case 'T': /* GNU extension. */
444 case 't': /* GNU extension. */
449 DO_NUMBER (2, y_week0);
452 DO_NUMBER (2, y_week2);
455 DO_NUMBER (2, y_week1);
458 DO_NUMBER (2, tp->tm_wday);
461 DO_NUMBER (4, 1900 + tp->tm_year);
464 DO_NUMBER (2, tp->tm_year % 100);
478 t = __mktime_internal (&tml, __localtime_r, &offset);
480 /* Canonicalize the local time. */
481 if (t == (time_t) -1 || __localtime_r (&t, &tml) == NULL)
482 /* We didn't managed to get the local time. Assume it
483 GMT as a reasonable default value. */
487 __gmtime_r (&t, &tmg);
489 /* Compute the difference. */
490 diff = tml.tm_min - tmg.tm_min;
491 diff += 60 * (tml.tm_hour - tmg.tm_hour);
493 if (tml.tm_mon != tmg.tm_mon)
495 /* We assume no timezone differs from UTC by more
496 than +- 23 hours. This should be safe. */
497 if (tmg.tm_mday == 1)
499 else /* tml.tm_mday == 1 */
502 diff += 1440 * (tml.tm_mday - tmg.tm_mday);
514 DO_NUMBER (4, ((diff / 60) % 24) * 100 + diff % 60);