1 /* Copyright (C) 1991, 92, 93, 94, 95, 96 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
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,
26 # define HAVE_LIMITS_H 1
28 # define HAVE_TM_ZONE 1
29 # define STDC_HEADERS 1
30 # include <ansidecl.h>
31 # include "../locale/localeinfo.h"
35 #include <sys/types.h> /* Some systems define `time_t' here. */
37 #ifdef TIME_WITH_SYS_TIME
38 # include <sys/time.h>
41 # ifdef HAVE_SYS_TIME_H
42 # include <sys/time.h>
61 # define memcpy(d, s, n) bcopy (s, d, n)
65 #if defined (__GNUC__) || (defined (__STDC__) && __STDC__)
66 #define __P(args) args
80 /* Uncomment following line in the production version. */
84 static unsigned int week __P ((const struct tm *const, int, int));
100 #define cpy(n, s) add ((n), memcpy((PTR) p, (PTR) (s), (n)))
103 #define fmt(n, args) add((n), if (sprintf args != (n)) return 0)
105 #define fmt(n, args) add((n), sprintf args; if (strlen (p) != (n)) return 0)
110 /* Return the week in the year specified by TP,
111 with weeks starting on STARTING_DAY. */
116 week (tp, starting_day, max_preceding)
117 const struct tm *const tp;
123 wday = tp->tm_wday - starting_day;
127 /* Set DL to the day in the year of the first day of the week
128 containing the day specified in TP. */
129 dl = tp->tm_yday - wday;
131 /* For the computation following ISO 8601:1988 we set the number of
132 the week containing January 1st to 1 if this week has more than
133 MAX_PRECEDING days in the new year. For ISO 8601 this number is
134 3, for the other representation it is 7 (i.e., not to be
136 base = ((dl + 7) % 7) > max_preceding ? 1 : 0;
138 /* If DL is negative we compute the result as 0 unless we have to
139 compute it according ISO 8601. In this case we have to return 53
140 or 1 if the week containing January 1st has less than 4 days in
141 the new year or not. If DL is not negative we calculate the
142 number of complete weeks for our week (DL / 7) plus 1 (because
143 only for DL < 0 we are in week 0/53 and plus the number of the
144 first week computed in the last step. */
145 return dl < 0 ? (dl < -max_preceding ? 53 : base)
150 static char const weekday_name[][10] =
152 "Sunday", "Monday", "Tuesday", "Wednesday",
153 "Thursday", "Friday", "Saturday"
155 static char const month_name[][10] =
157 "January", "February", "March", "April", "May", "June",
158 "July", "August", "September", "October", "November", "December"
162 /* Write information from TP into S according to the format
163 string FORMAT, writing no more that MAXSIZE characters
164 (including the terminating '\0') and returning number of
165 characters written. If S is NULL, nothing will be written
166 anywhere, so to determine how many characters would be
167 written, use NULL for S and (size_t) UINT_MAX for MAXSIZE. */
169 strftime (s, maxsize, format, tp)
173 register const struct tm *tp;
175 int hour12 = tp->tm_hour;
177 const char *const a_wkday = _NL_CURRENT (LC_TIME, ABDAY_1 + tp->tm_wday);
178 const char *const f_wkday = _NL_CURRENT (LC_TIME, DAY_1 + tp->tm_wday);
179 const char *const a_month = _NL_CURRENT (LC_TIME, ABMON_1 + tp->tm_mon);
180 const char *const f_month = _NL_CURRENT (LC_TIME, MON_1 + tp->tm_mon);
181 const char *const ampm = _NL_CURRENT (LC_TIME,
182 hour12 > 11 ? PM_STR : AM_STR);
183 size_t aw_len = strlen(a_wkday);
184 size_t am_len = strlen(a_month);
185 size_t ap_len = strlen (ampm);
187 const char *const f_wkday = weekday_name[tp->tm_wday];
188 const char *const f_month = month_name[tp->tm_mon];
189 const char *const a_wkday = f_wkday;
190 const char *const a_month = f_month;
191 const char *const ampm = "AMPM" + 2 * (hour12 > 11);
196 size_t wkday_len = strlen (f_wkday);
197 size_t month_len = strlen (f_month);
198 const unsigned int y_week0 = week (tp, 0, 7);
199 const unsigned int y_week1 = week (tp, 1, 7);
200 const unsigned int y_week2 = week (tp, 1, 3);
203 register size_t i = 0;
204 register char *p = s;
205 register const char *f;
208 /* Initialize the buffer we will use for the sprintf format for numbers. */
213 zone = (const char *) tp->tm_zone;
216 if (!(zone && *zone) && tp->tm_isdst >= 0)
217 zone = tzname[tp->tm_isdst];
219 if (!(zone && *zone))
222 zonelen = strlen (zone);
227 if (hour12 == 0) hour12 = 12;
229 for (f = format; *f != '\0'; ++f)
231 enum { pad_zero, pad_space, pad_none } pad; /* Padding for number. */
232 unsigned int maxdigits; /* Max digits for numeric format. */
233 unsigned int number_value; /* Numeric value to be printed. */
239 /* Non-ASCII, may be a multibyte. */
240 int len = mblen (f, strlen (f));
255 /* Check for flags that can modify a number format. */
272 /* Now do the specified format. */
281 cpy (aw_len, a_wkday);
285 cpy (wkday_len, f_wkday);
289 case 'h': /* GNU extension. */
290 cpy (am_len, a_month);
294 cpy (month_len, f_month);
299 subfmt = _NL_CURRENT (LC_TIME, D_T_FMT);
301 subfmt = "%a %b %d %H:%M:%S %Z %Y";
305 size_t len = strftime (p, maxsize - i, subfmt, tp);
306 if (len == 0 && *subfmt)
312 #define DO_NUMBER(digits, value) \
313 maxdigits = digits; number_value = value; goto do_number
314 #define DO_NUMBER_SPACEPAD(digits, value) \
315 maxdigits = digits; number_value = value; goto do_number_spacepad
318 DO_NUMBER (2, (1900 + tp->tm_year) / 100);
322 subfmt = _NL_CURRENT (LC_TIME, D_FMT);
326 case 'D': /* GNU extension. */
331 DO_NUMBER (2, tp->tm_mday);
333 case 'e': /* GNU extension: %d, but blank-padded. */
334 DO_NUMBER_SPACEPAD (2, tp->tm_mday);
336 /* All numeric formats set MAXDIGITS and NUMBER_VALUE and then
337 jump to one of these two labels. */
340 /* Force `_' flag. */
345 /* Format the number according to the PAD flag. */
347 register char *nf = &number_fmt[1];
348 int printed = maxdigits;
355 *nf++ = '0' + maxdigits;
362 add (maxdigits, printed = sprintf (p, number_fmt, number_value));
364 add (maxdigits, sprintf (p, number_fmt, number_value);
365 printed = strlen (p));
367 /* Back up if fewer than MAXDIGITS chars written for pad_none. */
368 p -= maxdigits - printed;
369 i -= maxdigits - printed;
376 DO_NUMBER (2, tp->tm_hour);
379 DO_NUMBER (2, hour12);
381 case 'k': /* GNU extension. */
382 DO_NUMBER_SPACEPAD (2, tp->tm_hour);
384 case 'l': /* GNU extension. */
385 DO_NUMBER_SPACEPAD (2, hour12);
388 DO_NUMBER (3, 1 + tp->tm_yday);
391 DO_NUMBER (2, tp->tm_min);
394 DO_NUMBER (2, tp->tm_mon + 1);
396 case 'n': /* GNU extension. */
404 case 'R': /* GNU extension. */
408 case 'r': /* GNU extension. */
409 subfmt = "%I:%M:%S %p";
413 DO_NUMBER (2, tp->tm_sec);
415 case 's': /* GNU extension. */
417 struct tm writable_tm = *tp;
418 unsigned long int num = (unsigned long int) mktime (&writable_tm);
419 /* `3 * sizeof (unsigned long int)' is an approximation of
420 the size of the decimal representation of NUM, valid
422 int printed = 3 * sizeof (unsigned long int);
424 assert (sizeof (unsigned long int) <= 16);
426 add (maxdigits, printed = sprintf (p, "%lu", num));
428 add (maxdigits, sprintf (p, "%lu", num); printed = strlen (p));
430 /* Back up if fewer than MAXDIGITS chars written for pad_none. */
431 p -= maxdigits - printed;
432 i -= maxdigits - printed;
438 subfmt = _NL_CURRENT (LC_TIME, T_FMT);
442 case 'T': /* GNU extension. */
446 case 't': /* GNU extension. */
451 DO_NUMBER (2, y_week0);
454 DO_NUMBER (2, y_week2);
457 DO_NUMBER (2, y_week1);
460 DO_NUMBER (2, tp->tm_wday);
463 DO_NUMBER (4, 1900 + tp->tm_year);
466 DO_NUMBER (2, tp->tm_year % 100);
480 t = __mktime_internal (&tml, __localtime_r, &offset);
482 /* Canonicalize the local time. */
483 if (t == (time_t) -1 || __localtime_r (&t, &tml) == NULL)
484 /* We didn't managed to get the local time. Assume it
485 GMT as a reasonable default value. */
489 __gmtime_r (&t, &tmg);
491 /* Compute the difference. */
492 diff = tml.tm_min - tmg.tm_min;
493 diff += 60 * (tml.tm_hour - tmg.tm_hour);
495 if (tml.tm_mon != tmg.tm_mon)
497 /* We assume no timezone differs from UTC by more
498 than +- 23 hours. This should be safe. */
499 if (tmg.tm_mday == 1)
501 else /* tml.tm_mday == 1 */
504 diff += 1440 * (tml.tm_mday - tmg.tm_mday);
516 DO_NUMBER (4, ((diff / 60) % 24) * 100 + diff % 60);