Sun Dec 17 15:56:35 1995 Miles Bader <miles@gnu.ai.mit.edu>
[glibc.git] / time / strftime.c
blobc47fc0754847adf2b3f7141490af52dbd5b990dc
1 /* Extensions for GNU date that are still missing here:
4 */
6 /* Copyright (C) 1991, 1992, 1993, 1994, 1995 Free Software Foundation, Inc.
7 This file is part of the GNU C Library.
9 The GNU C Library is free software; you can redistribute it and/or
10 modify it under the terms of the GNU Library General Public License as
11 published by the Free Software Foundation; either version 2 of the
12 License, or (at your option) any later version.
14 The GNU C Library is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 Library General Public License for more details.
19 You should have received a copy of the GNU Library General Public
20 License along with the GNU C Library; see the file COPYING.LIB. If
21 not, write to the Free Software Foundation, Inc., 675 Mass Ave,
22 Cambridge, MA 02139, USA. */
24 #include <ansidecl.h>
25 #include "../locale/localeinfo.h"
26 #include <ctype.h>
27 #include <limits.h>
28 #include <stddef.h>
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <time.h>
34 #ifndef HAVE_GNU_LD
35 #define __tzname tzname
36 #define __daylight daylight
37 #define __timezone timezone
38 #endif
41 #define add(n, f) \
42 do \
43 { \
44 i += (n); \
45 if (i >= maxsize) \
46 return 0; \
47 else \
48 if (p != NULL) \
49 { \
50 f; \
51 p += (n); \
52 } \
53 } while (0)
54 #define cpy(n, s) add((n), memcpy((PTR) p, (PTR) (s), (n)))
55 #define fmt(n, args) add((n), if (sprintf args != (n)) return 0)
57 /* Return the week in the year specified by TP,
58 with weeks starting on STARTING_DAY. */
59 #ifdef __GNUC__
60 inline
61 #endif
62 static unsigned int
63 DEFUN(week, (tp, starting_day),
64 CONST struct tm *CONST tp AND int starting_day)
66 int wday, dl;
68 wday = tp->tm_wday - starting_day;
69 if (wday < 0)
70 wday += 7;
72 /* Set DL to the day in the year of the last day of the week previous to the
73 one containing the day specified in TP. If DL is negative or zero, the
74 day specified in TP is in the first week of the year. Otherwise,
75 calculate the number of complete weeks before our week (DL / 7) and
76 add any partial week at the start of the year (DL % 7). */
77 dl = tp->tm_yday - wday;
78 return dl <= 0 ? 0 : ((dl / 7) + ((dl % 7) == 0 ? 0 : 1));
82 /* Write information from TP into S according to the format
83 string FORMAT, writing no more that MAXSIZE characters
84 (including the terminating '\0') and returning number of
85 characters written. If S is NULL, nothing will be written
86 anywhere, so to determine how many characters would be
87 written, use NULL for S and (size_t) UINT_MAX for MAXSIZE. */
88 size_t
89 DEFUN(strftime, (s, maxsize, format, tp),
90 char *s AND size_t maxsize AND
91 CONST char *format AND register CONST struct tm *tp)
93 CONST char *CONST a_wkday = _NL_CURRENT (LC_TIME, ABDAY_1 + tp->tm_wday);
94 CONST char *CONST f_wkday = _NL_CURRENT (LC_TIME, DAY_1 + tp->tm_wday);
95 CONST char *CONST a_month = _NL_CURRENT (LC_TIME, ABMON_1 + tp->tm_mon);
96 CONST char *CONST f_month = _NL_CURRENT (LC_TIME, MON_1 + tp->tm_mon);
97 size_t aw_len = strlen(a_wkday);
98 size_t am_len = strlen(a_month);
99 size_t wkday_len = strlen(f_wkday);
100 size_t month_len = strlen(f_month);
101 int hour12 = tp->tm_hour;
102 CONST char *CONST ampm = _NL_CURRENT (LC_TIME,
103 hour12 > 12 ? PM_STR : AM_STR);
104 size_t ap_len = strlen (ampm);
105 CONST unsigned int y_week0 = week (tp, 0);
106 CONST unsigned int y_week1 = week (tp, 1);
107 CONST char *zone;
108 size_t zonelen;
109 register size_t i = 0;
110 register char *p = s;
111 register CONST char *f;
113 if (tp->tm_isdst < 0)
115 zone = "";
116 zonelen = 0;
118 else
120 zone = __tzname[tp->tm_isdst];
121 zonelen = strlen(zone);
124 if (hour12 > 12)
125 hour12 -= 12;
126 else
127 if (hour12 == 0) hour12 = 12;
129 for (f = format; *f != '\0'; ++f)
131 CONST char *subfmt;
133 if (!isascii(*f))
135 /* Non-ASCII, may be a multibyte. */
136 int len = mblen(f, strlen(f));
137 if (len > 0)
139 cpy(len, f);
140 continue;
144 if (*f != '%')
146 add(1, *p = *f);
147 continue;
150 ++f;
151 switch (*f)
153 case '\0':
154 case '%':
155 add(1, *p = *f);
156 break;
158 case 'a':
159 cpy(aw_len, a_wkday);
160 break;
162 case 'A':
163 cpy(wkday_len, f_wkday);
164 break;
166 case 'b':
167 case 'h': /* GNU extension. */
168 cpy(am_len, a_month);
169 break;
171 case 'B':
172 cpy(month_len, f_month);
173 break;
175 case 'c':
176 subfmt = _NL_CURRENT (LC_TIME, D_T_FMT);
177 subformat:
179 size_t len = strftime (p, maxsize - i, subfmt, tp);
180 add(len, );
182 break;
184 case 'C':
185 fmt (2, (p, "%.2d", (1900 + tp->tm_year) / 100));
186 break;
188 case 'D': /* GNU extension. */
189 subfmt = "%m/%d/%y";
190 goto subformat;
192 case 'd':
193 fmt(2, (p, "%.2d", tp->tm_mday));
194 break;
196 case 'e': /* GNU extension: %d, but blank-padded. */
197 fmt(2, (p, "%2d", tp->tm_mday));
198 break;
200 case 'H':
201 fmt(2, (p, "%.2d", tp->tm_hour));
202 break;
204 case 'I':
205 fmt(2, (p, "%.2d", hour12));
206 break;
208 case 'k': /* GNU extension. */
209 fmt(2, (p, "%2d", tp->tm_hour));
210 break;
212 case 'l': /* GNU extension. */
213 fmt(2, (p, "%2d", hour12));
214 break;
216 case 'j':
217 fmt(3, (p, "%.3d", 1 + tp->tm_yday));
218 break;
220 case 'M':
221 fmt(2, (p, "%.2d", tp->tm_min));
222 break;
224 case 'm':
225 fmt(2, (p, "%.2d", tp->tm_mon + 1));
226 break;
228 case 'n': /* GNU extension. */
229 add (1, *p = '\n');
230 break;
232 case 'p':
233 cpy(ap_len, ampm);
234 break;
236 case 'R': /* GNU extension. */
237 subfmt = "%H:%M";
238 goto subformat;
240 case 'r': /* GNU extension. */
241 subfmt = "%I:%M:%S %p";
242 goto subformat;
244 case 'S':
245 fmt(2, (p, "%.2d", tp->tm_sec));
246 break;
248 case 'T': /* GNU extenstion. */
249 subfmt = "%H:%M:%S";
250 goto subformat;
252 case 't': /* GNU extenstion. */
253 add (1, *p = '\t');
254 break;
256 case 'U':
257 fmt(2, (p, "%.2u", y_week0));
258 break;
260 case 'W':
261 fmt(2, (p, "%.2u", y_week1));
262 break;
264 case 'w':
265 fmt(2, (p, "%.2d", tp->tm_wday));
266 break;
268 case 'X':
269 subfmt = _NL_CURRENT (LC_TIME, T_FMT);
270 goto subformat;
272 case 'x':
273 subfmt = _NL_CURRENT (LC_TIME, D_FMT);
274 goto subformat;
276 case 'Y':
277 fmt(4, (p, "%.4d", 1900 + tp->tm_year));
278 break;
280 case 'y':
281 fmt(2, (p, "%.2d", tp->tm_year % 100));
282 break;
284 case 'Z':
285 cpy(zonelen, zone);
286 break;
288 default:
289 /* Bad format. */
290 break;
294 if (p != NULL)
295 *p = '\0';
296 return i;