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 static unsigned int week __P((const struct tm *const, int, int));
96 #define cpy(n, s) add((n), memcpy((PTR) p, (PTR) (s), (n)))
99 #define fmt(n, args) add((n), if (sprintf args != (n)) return 0)
101 #define fmt(n, args) add((n), sprintf args; if (strlen (p) != (n)) return 0)
106 /* Return the week in the year specified by TP,
107 with weeks starting on STARTING_DAY. */
112 week (tp, starting_day, max_preceding)
113 const struct tm *const tp;
119 wday = tp->tm_wday - starting_day;
123 /* Set DL to the day in the year of the first day of the week
124 containing the day specified in TP. */
125 dl = tp->tm_yday - wday;
127 /* For the computation following ISO 8601:1988 we set the number of
128 the week containing January 1st to 1 if this week has more than
129 MAX_PRECEDING days in the new year. For ISO 8601 this number is
130 3, for the other representation it is 7 (i.e., not to be
132 base = ((dl + 7) % 7) > max_preceding ? 1 : 0;
134 /* If DL is negative we compute the result as 0 unless we have to
135 compute it according ISO 8601. In this case we have to return 53
136 or 1 if the week containing January 1st has less than 4 days in
137 the new year or not. If DL is not negative we calculate the
138 number of complete weeks for our week (DL / 7) plus 1 (because
139 only for DL < 0 we are in week 0/53 and plus the number of the
140 first week computed in the last step. */
141 return dl < 0 ? (dl < -max_preceding ? 53 : base)
146 static char const weekday_name[][10] =
148 "Sunday", "Monday", "Tuesday", "Wednesday",
149 "Thursday", "Friday", "Saturday"
151 static char const month_name[][10] =
153 "January", "February", "March", "April", "May", "June",
154 "July", "August", "September", "October", "November", "December"
158 /* Write information from TP into S according to the format
159 string FORMAT, writing no more that MAXSIZE characters
160 (including the terminating '\0') and returning number of
161 characters written. If S is NULL, nothing will be written
162 anywhere, so to determine how many characters would be
163 written, use NULL for S and (size_t) UINT_MAX for MAXSIZE. */
165 strftime (s, maxsize, format, tp)
169 register const struct tm *tp;
171 int hour12 = tp->tm_hour;
173 const char *const a_wkday = _NL_CURRENT (LC_TIME, ABDAY_1 + tp->tm_wday);
174 const char *const f_wkday = _NL_CURRENT (LC_TIME, DAY_1 + tp->tm_wday);
175 const char *const a_month = _NL_CURRENT (LC_TIME, ABMON_1 + tp->tm_mon);
176 const char *const f_month = _NL_CURRENT (LC_TIME, MON_1 + tp->tm_mon);
177 const char *const ampm = _NL_CURRENT (LC_TIME,
178 hour12 > 11 ? PM_STR : AM_STR);
179 size_t aw_len = strlen(a_wkday);
180 size_t am_len = strlen(a_month);
181 size_t ap_len = strlen (ampm);
183 const char *const f_wkday = weekday_name[tp->tm_wday];
184 const char *const f_month = month_name[tp->tm_mon];
185 const char *const a_wkday = f_wkday;
186 const char *const a_month = f_month;
187 const char *const ampm = "AMPM" + 2 * (hour12 > 11);
192 size_t wkday_len = strlen(f_wkday);
193 size_t month_len = strlen(f_month);
194 const unsigned int y_week0 = week (tp, 0, 7);
195 const unsigned int y_week1 = week (tp, 1, 7);
196 const unsigned int y_week2 = week (tp, 1, 3);
199 register size_t i = 0;
200 register char *p = s;
201 register const char *f;
204 /* Initialize the buffer we will use for the sprintf format for numbers. */
209 zone = (const char *) tp->tm_zone;
212 if (!(zone && *zone) && tp->tm_isdst >= 0)
213 zone = tzname[tp->tm_isdst];
215 if (!(zone && *zone))
218 zonelen = strlen (zone);
223 if (hour12 == 0) hour12 = 12;
225 for (f = format; *f != '\0'; ++f)
227 enum { pad_zero, pad_space, pad_none } pad; /* Padding for number. */
228 unsigned int maxdigits; /* Max digits for numeric format. */
229 unsigned int number_value; /* Numeric value to be printed. */
235 /* Non-ASCII, may be a multibyte. */
236 int len = mblen(f, strlen(f));
251 /* Check for flags that can modify a number format. */
268 /* Now do the specified format. */
277 cpy(aw_len, a_wkday);
281 cpy(wkday_len, f_wkday);
285 case 'h': /* GNU extension. */
286 cpy(am_len, a_month);
290 cpy(month_len, f_month);
295 subfmt = _NL_CURRENT (LC_TIME, D_T_FMT);
297 subfmt = "%a %b %d %H:%M:%S %Z %Y";
301 size_t len = strftime (p, maxsize - i, subfmt, tp);
302 if (len == 0 && *subfmt)
308 #define DO_NUMBER(digits, value) \
309 maxdigits = digits; number_value = value; goto do_number
310 #define DO_NUMBER_SPACEPAD(digits, value) \
311 maxdigits = digits; number_value = value; goto do_number_spacepad
314 DO_NUMBER (2, (1900 + tp->tm_year) / 100);
318 subfmt = _NL_CURRENT (LC_TIME, D_FMT);
322 case 'D': /* GNU extension. */
327 DO_NUMBER (2, tp->tm_mday);
329 case 'e': /* GNU extension: %d, but blank-padded. */
330 DO_NUMBER_SPACEPAD (2, tp->tm_mday);
332 /* All numeric formats set MAXDIGITS and NUMBER_VALUE and then
333 jump to one of these two labels. */
336 /* Force `_' flag. */
341 /* Format the number according to the PAD flag. */
343 register char *nf = &number_fmt[1];
351 *nf++ = '0' + maxdigits;
358 add (maxdigits, printed = sprintf (p, number_fmt, number_value));
360 add (maxdigits, sprintf (p, number_fmt, number_value);
361 printed = strlen (p));
363 /* Back up if fewer than MAXDIGITS chars written for pad_none. */
364 p -= maxdigits - printed;
365 i -= maxdigits - printed;
372 DO_NUMBER (2, tp->tm_hour);
375 DO_NUMBER (2, hour12);
377 case 'k': /* GNU extension. */
378 DO_NUMBER_SPACEPAD (2, tp->tm_hour);
380 case 'l': /* GNU extension. */
381 DO_NUMBER_SPACEPAD (2, hour12);
384 DO_NUMBER (3, 1 + tp->tm_yday);
387 DO_NUMBER (2, tp->tm_min);
390 DO_NUMBER (2, tp->tm_mon + 1);
392 case 'n': /* GNU extension. */
400 case 'R': /* GNU extension. */
404 case 'r': /* GNU extension. */
405 subfmt = "%I:%M:%S %p";
409 DO_NUMBER (2, tp->tm_sec);
413 subfmt = _NL_CURRENT (LC_TIME, T_FMT);
417 case 'T': /* GNU extenstion. */
421 case 't': /* GNU extenstion. */
426 DO_NUMBER (2, y_week0);
429 DO_NUMBER (2, y_week2);
432 DO_NUMBER (2, y_week1);
435 DO_NUMBER (2, tp->tm_wday);
438 DO_NUMBER (4, 1900 + tp->tm_year);
441 DO_NUMBER (2, tp->tm_year % 100);