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. */
49 # define memcpy(d, s, n) bcopy (s, d, n)
53 #if defined (__GNUC__) || (defined (__STDC__) && __STDC__)
54 #define __P(args) args
68 static unsigned int week __P((const struct tm *const, int));
84 #define cpy(n, s) add((n), memcpy((PTR) p, (PTR) (s), (n)))
87 #define fmt(n, args) add((n), if (sprintf args != (n)) return 0)
89 #define fmt(n, args) add((n), sprintf args; if (strlen (p) != (n)) return 0)
94 /* Return the week in the year specified by TP,
95 with weeks starting on STARTING_DAY. */
100 week (tp, starting_day)
101 const struct tm *const tp;
106 wday = tp->tm_wday - starting_day;
110 /* Set DL to the day in the year of the last day of the week previous to the
111 one containing the day specified in TP. If DL is negative or zero, the
112 day specified in TP is in the first week of the year. Otherwise,
113 calculate the number of complete weeks before our week (DL / 7) and
114 add any partial week at the start of the year (DL % 7). */
115 dl = tp->tm_yday - wday;
116 return dl <= 0 ? 0 : ((dl / 7) + ((dl % 7) == 0 ? 0 : 1));
120 static char const weekday_name[][10] =
122 "Sunday", "Monday", "Tuesday", "Wednesday",
123 "Thursday", "Friday", "Saturday"
125 static char const month_name[][10] =
127 "January", "February", "March", "April", "May", "June",
128 "July", "August", "September", "October", "November", "December"
132 /* Write information from TP into S according to the format
133 string FORMAT, writing no more that MAXSIZE characters
134 (including the terminating '\0') and returning number of
135 characters written. If S is NULL, nothing will be written
136 anywhere, so to determine how many characters would be
137 written, use NULL for S and (size_t) UINT_MAX for MAXSIZE. */
139 strftime (s, maxsize, format, tp)
143 register const struct tm *tp;
145 int hour12 = tp->tm_hour;
147 const char *const a_wkday = _NL_CURRENT (LC_TIME, ABDAY_1 + tp->tm_wday);
148 const char *const f_wkday = _NL_CURRENT (LC_TIME, DAY_1 + tp->tm_wday);
149 const char *const a_month = _NL_CURRENT (LC_TIME, ABMON_1 + tp->tm_mon);
150 const char *const f_month = _NL_CURRENT (LC_TIME, MON_1 + tp->tm_mon);
151 const char *const ampm = _NL_CURRENT (LC_TIME,
152 hour12 > 11 ? PM_STR : AM_STR);
153 size_t aw_len = strlen(a_wkday);
154 size_t am_len = strlen(a_month);
155 size_t ap_len = strlen (ampm);
157 const char *const f_wkday = weekday_name[tp->tm_wday];
158 const char *const f_month = month_name[tp->tm_mon];
159 const char *const a_wkday = f_wkday;
160 const char *const a_month = f_month;
161 const char *const ampm = "AMPM" + 2 * (hour12 > 11);
166 size_t wkday_len = strlen(f_wkday);
167 size_t month_len = strlen(f_month);
168 const unsigned int y_week0 = week (tp, 0);
169 const unsigned int y_week1 = week (tp, 1);
172 register size_t i = 0;
173 register char *p = s;
174 register const char *f;
177 /* Initialize the buffer we will use for the sprintf format for numbers. */
182 zone = (const char *) tp->tm_zone;
185 if (!(zone && *zone) && tp->tm_isdst >= 0)
186 zone = tzname[tp->tm_isdst];
188 if (!(zone && *zone))
191 zonelen = strlen (zone);
196 if (hour12 == 0) hour12 = 12;
198 for (f = format; *f != '\0'; ++f)
200 enum { pad_zero, pad_space, pad_none } pad; /* Padding for number. */
201 unsigned int maxdigits; /* Max digits for numeric format. */
202 unsigned int number_value; /* Numeric value to be printed. */
208 /* Non-ASCII, may be a multibyte. */
209 int len = mblen(f, strlen(f));
224 /* Check for flags that can modify a number format. */
241 /* Now do the specified format. */
250 cpy(aw_len, a_wkday);
254 cpy(wkday_len, f_wkday);
258 case 'h': /* GNU extension. */
259 cpy(am_len, a_month);
263 cpy(month_len, f_month);
268 subfmt = _NL_CURRENT (LC_TIME, D_T_FMT);
270 subfmt = "%a %b %d %H:%M:%S %Z %Y";
274 size_t len = strftime (p, maxsize - i, subfmt, tp);
275 if (len == 0 && *subfmt)
281 #define DO_NUMBER(digits, value) \
282 maxdigits = digits; number_value = value; goto do_number
283 #define DO_NUMBER_NOPAD(digits, value) \
284 maxdigits = digits; number_value = value; goto do_number_nopad
287 DO_NUMBER (2, (1900 + tp->tm_year) / 100);
291 subfmt = _NL_CURRENT (LC_TIME, D_FMT);
295 case 'D': /* GNU extension. */
300 DO_NUMBER (2, tp->tm_mday);
302 case 'e': /* GNU extension: %d, but blank-padded. */
303 DO_NUMBER_NOPAD (2, tp->tm_mday);
305 /* All numeric formats set MAXDIGITS and NUMBER_VALUE and then
306 jump to one of these two labels. */
309 /* Force `-' flag. */
314 /* Format the number according to the PAD flag. */
316 register char *nf = &number_fmt[1];
324 *nf++ = '0' + maxdigits;
331 add (maxdigits, printed = sprintf (p, number_fmt, number_value));
333 add (sprintf (p, number_fmt, number_value);
334 printed = strlen (p));
342 DO_NUMBER (2, tp->tm_hour);
345 DO_NUMBER (2, hour12);
347 case 'k': /* GNU extension. */
348 DO_NUMBER_NOPAD (2, tp->tm_hour);
350 case 'l': /* GNU extension. */
351 DO_NUMBER_NOPAD (2, hour12);
354 DO_NUMBER (3, 1 + tp->tm_yday);
357 DO_NUMBER (2, tp->tm_min);
360 DO_NUMBER (2, tp->tm_mon + 1);
362 case 'n': /* GNU extension. */
370 case 'R': /* GNU extension. */
374 case 'r': /* GNU extension. */
375 subfmt = "%I:%M:%S %p";
379 DO_NUMBER (2, tp->tm_sec);
383 subfmt = _NL_CURRENT (LC_TIME, T_FMT);
387 case 'T': /* GNU extenstion. */
391 case 't': /* GNU extenstion. */
396 DO_NUMBER (2, y_week0);
399 DO_NUMBER (2, y_week1);
402 DO_NUMBER (2, tp->tm_wday);
405 DO_NUMBER (4, 1900 + tp->tm_year);
408 DO_NUMBER (2, tp->tm_year % 100);