1 /*
2  * Copyright (c) 1989, 1993
3  *	The Regents of the University of California.  All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. All advertising materials mentioning features or use of this software
14  *    must display the following acknowledgement:
15  *	This product includes software developed by the University of
16  *	California, Berkeley and its contributors.
17  * 4. Neither the name of the University nor the names of its contributors
18  *    may be used to endorse or promote products derived from this software
19  *    without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  */
33 
34 #if defined(LIBC_SCCS) && !defined(lint)
35 static char sccsid[] = "@(#)strftime.c	8.1 (Berkeley) 6/4/93";
36 #endif /* LIBC_SCCS and not lint */
37 
38 #include <sys/types.h>
39 #include <sys/time.h>
40 #include <tzfile.h>
41 #include <limits.h>
42 #include <stdio.h>
43 
44 /*
45 ** 302 / 1000 is log10(2.0) rounded up.
46 ** Subtract one for the sign bit;
47 ** add one for integer division truncation;
48 ** add one more for a minus sign.
49 */
50 #define INT_STRLEN_MAXIMUM(type) \
51 	((sizeof(type) * CHAR_BIT - 1) * 302 / 1000 + 2)
52 
53 /*
54 ** Based on elsieid[] = "@(#)strftime.c	7.15"
55 **
56 ** This is ANSIish only when time is treated identically in all locales and
57 ** when "multibyte character == plain character".
58 */
59 
60 static const char afmt[][4] = {
61 	"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
62 };
63 static const char Afmt[][10] = {
64 	"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday",
65 	"Saturday"
66 };
67 static const char bfmt[][4] = {
68 	"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep",
69 	"Oct", "Nov", "Dec"
70 };
71 static const char Bfmt[][10] = {
72 	"January", "February", "March", "April", "May", "June", "July",
73 	"August", "September", "October", "November", "December"
74 };
75 
76 static char *_add __P((const char *, char *, const char *));
77 static char *_conv __P((int, const char *, char *, const char *));
78 static char *_secs __P((const struct tm *, char *, const char *));
79 static char *_fmt __P((const char *, const struct tm *, char *, const char *));
80 
81 extern char *tzname[];
82 
83 size_t
strftime(s,maxsize,format,t)84 strftime(s, maxsize, format, t)
85 	char *s;
86 	size_t maxsize;
87 	const char *format;
88 	const struct tm *t;
89 {
90 	char *p;
91 
92 	p = _fmt(format, t, s, s + maxsize);
93 	if (p == s + maxsize)
94 		return (0);
95 	*p = '\0';
96 	return (p - s);
97 }
98 
99 static char *
_fmt(format,t,pt,ptlim)100 _fmt(format, t, pt, ptlim)
101 	const char *format;
102 	const struct tm *t;
103 	char *pt;
104 	const char *ptlim;
105 {
106 	for (; *format; ++format) {
107 		if (*format == '%') {
108 label:
109 			switch(*++format) {
110 			case '\0':
111 				--format;
112 				break;
113 			case 'A':
114 				pt = _add((t->tm_wday < 0 || t->tm_wday > 6) ?
115 					"?" : Afmt[t->tm_wday], pt, ptlim);
116 				continue;
117 			case 'a':
118 				pt = _add((t->tm_wday < 0 || t->tm_wday > 6) ?
119 					"?" : afmt[t->tm_wday], pt, ptlim);
120 				continue;
121 			case 'B':
122 				pt = _add((t->tm_mon < 0 || t->tm_mon > 11) ?
123 					"?" : Bfmt[t->tm_mon], pt, ptlim);
124 				continue;
125 			case 'b':
126 			case 'h':
127 				pt = _add((t->tm_mon < 0 || t->tm_mon > 11) ?
128 					"?" : bfmt[t->tm_mon], pt, ptlim);
129 				continue;
130 			case 'c':
131 				pt = _fmt("%D %X", t, pt, ptlim);
132 				continue;
133 			case 'C':
134 				/*
135 				** %C used to do a...
136 				**	_fmt("%a %b %e %X %Y", t);
137 				** ...whereas now POSIX 1003.2 calls for
138 				** something completely different.
139 				** (ado, 5/24/93)
140 				*/
141 				pt = _conv((t->tm_year + TM_YEAR_BASE) / 100,
142 					"%02d", pt, ptlim);
143 				continue;
144 			case 'D':
145 				pt = _fmt("%m/%d/%y", t, pt, ptlim);
146 				continue;
147 			case 'x':
148 				/*
149 				** Version 3.0 of strftime from Arnold Robbins
150 				** (arnold@skeeve.atl.ga.us) does the
151 				** equivalent of...
152 				**	_fmt("%a %b %e %Y");
153 				** ...for %x; since the X3J11 C language
154 				** standard calls for "date, using locale's
155 				** date format," anything goes.  Using just
156 				** numbers (as here) makes Quakers happier.
157 				** Word from Paul Eggert (eggert@twinsun.com)
158 				** is that %Y-%m-%d is the ISO standard date
159 				** format, specified in ISO 2014 and later
160 				** ISO 8601:1988, with a summary available in
161 				** pub/doc/ISO/english/ISO8601.ps.Z on
162 				** ftp.uni-erlangen.de.
163 				** (ado, 5/30/93)
164 				*/
165 				pt = _fmt("%m/%d/%y", t, pt, ptlim);
166 				continue;
167 			case 'd':
168 				pt = _conv(t->tm_mday, "%02d", pt, ptlim);
169 				continue;
170 			case 'E':
171 			case 'O':
172 				/*
173 				** POSIX locale extensions, a la
174 				** Arnold Robbins' strftime version 3.0.
175 				** The sequences
176 				**	%Ec %EC %Ex %Ey %EY
177 				**	%Od %oe %OH %OI %Om %OM
178 				**	%OS %Ou %OU %OV %Ow %OW %Oy
179 				** are supposed to provide alternate
180 				** representations.
181 				** (ado, 5/24/93)
182 				*/
183 				goto label;
184 			case 'e':
185 				pt = _conv(t->tm_mday, "%2d", pt, ptlim);
186 				continue;
187 			case 'H':
188 				pt = _conv(t->tm_hour, "%02d", pt, ptlim);
189 				continue;
190 			case 'I':
191 				pt = _conv((t->tm_hour % 12) ?
192 					(t->tm_hour % 12) : 12,
193 					"%02d", pt, ptlim);
194 				continue;
195 			case 'j':
196 				pt = _conv(t->tm_yday + 1, "%03d", pt, ptlim);
197 				continue;
198 			case 'k':
199 				/*
200 				** This used to be...
201 				**	_conv(t->tm_hour % 12 ?
202 				**		t->tm_hour % 12 : 12, 2, ' ');
203 				** ...and has been changed to the below to
204 				** match SunOS 4.1.1 and Arnold Robbins'
205 				** strftime version 3.0.  That is, "%k" and
206 				** "%l" have been swapped.
207 				** (ado, 5/24/93)
208 				*/
209 				pt = _conv(t->tm_hour, "%2d", pt, ptlim);
210 				continue;
211 #ifdef KITCHEN_SINK
212 			case 'K':
213 				/*
214 				** After all this time, still unclaimed!
215 				*/
216 				pt = _add("kitchen sink", pt, ptlim);
217 				continue;
218 #endif /* defined KITCHEN_SINK */
219 			case 'l':
220 				/*
221 				** This used to be...
222 				**	_conv(t->tm_hour, 2, ' ');
223 				** ...and has been changed to the below to
224 				** match SunOS 4.1.1 and Arnold Robbin's
225 				** strftime version 3.0.  That is, "%k" and
226 				** "%l" have been swapped.
227 				** (ado, 5/24/93)
228 				*/
229 				pt = _conv((t->tm_hour % 12) ?
230 					(t->tm_hour % 12) : 12,
231 					"%2d", pt, ptlim);
232 				continue;
233 			case 'M':
234 				pt = _conv(t->tm_min, "%02d", pt, ptlim);
235 				continue;
236 			case 'm':
237 				pt = _conv(t->tm_mon + 1, "%02d", pt, ptlim);
238 				continue;
239 			case 'n':
240 				pt = _add("\n", pt, ptlim);
241 				continue;
242 			case 'p':
243 				pt = _add(t->tm_hour >= 12 ? "PM" : "AM",
244 					pt, ptlim);
245 				continue;
246 			case 'R':
247 				pt = _fmt("%H:%M", t, pt, ptlim);
248 				continue;
249 			case 'r':
250 				pt = _fmt("%I:%M:%S %p", t, pt, ptlim);
251 				continue;
252 			case 'S':
253 				pt = _conv(t->tm_sec, "%02d", pt, ptlim);
254 				continue;
255 			case 's':
256 				pt = _secs(t, pt, ptlim);
257 				continue;
258 			case 'T':
259 			case 'X':
260 				pt = _fmt("%H:%M:%S", t, pt, ptlim);
261 				continue;
262 			case 't':
263 				pt = _add("\t", pt, ptlim);
264 				continue;
265 			case 'U':
266 				pt = _conv((t->tm_yday + 7 - t->tm_wday) / 7,
267 					"%02d", pt, ptlim);
268 				continue;
269 			case 'u':
270 				/*
271 				** From Arnold Robbins' strftime version 3.0:
272 				** "ISO 8601: Weekday as a decimal number
273 				** [1 (Monday) - 7]"
274 				** (ado, 5/24/93)
275 				*/
276 				pt = _conv((t->tm_wday == 0) ? 7 : t->tm_wday,
277 					"%d", pt, ptlim);
278 				continue;
279 			case 'V':
280 				/*
281 				** From Arnold Robbins' strftime version 3.0:
282 				** "the week number of the year (the first
283 				** Monday as the first day of week 1) as a
284 				** decimal number (01-53).  The method for
285 				** determining the week number is as specified
286 				** by ISO 8601 (to wit: if the week containing
287 				** January 1 has four or more days in the new
288 				** year, then it is week 1, otherwise it is
289 				** week 53 of the previous year and the next
290 				** week is week 1)."
291 				** (ado, 5/24/93)
292 				*/
293 				/*
294 				** XXX--If January 1 falls on a Friday,
295 				** January 1-3 are part of week 53 of the
296 				** previous year.  By analogy, if January
297 				** 1 falls on a Thursday, are December 29-31
298 				** of the PREVIOUS year part of week 1???
299 				** (ado 5/24/93)
300 				**
301 				** You are understood not to expect this.
302 				*/
303 				{
304 					int i;
305 
306 					i = (t->tm_yday + 10 - (t->tm_wday ?
307 						(t->tm_wday - 1) : 6)) / 7;
308 					pt = _conv((i == 0) ? 53 : i,
309 						"%02d", pt, ptlim);
310 				}
311 				continue;
312 #ifdef notdef
313 				/* Not in POSIX date(1), System V or ANSI C. */
314 			case 'v':
315 				/*
316 				** From Arnold Robbins' strftime version 3.0:
317 				** "date as dd-bbb-YYYY"
318 				** (ado, 5/24/93)
319 				*/
320 				pt = _fmt("%e-%b-%Y", t, pt, ptlim);
321 				continue;
322 #endif
323 			case 'W':
324 				pt = _conv((t->tm_yday + 7 -
325 					(t->tm_wday ?
326 					(t->tm_wday - 1) : 6)) / 7,
327 					"%02d", pt, ptlim);
328 				continue;
329 			case 'w':
330 				pt = _conv(t->tm_wday, "%d", pt, ptlim);
331 				continue;
332 			case 'y':
333 				pt = _conv((t->tm_year + TM_YEAR_BASE) % 100,
334 					"%02d", pt, ptlim);
335 				continue;
336 			case 'Y':
337 				pt = _conv(t->tm_year + TM_YEAR_BASE, "%04d",
338 					pt, ptlim);
339 				continue;
340 			case 'Z':
341 #ifdef TM_ZONE
342 				if (t->TM_ZONE)
343 					pt = _add(t->TM_ZONE, pt, ptlim);
344 				else
345 #endif /* defined TM_ZONE */
346 				if (t->tm_isdst == 0 || t->tm_isdst == 1) {
347 					pt = _add(tzname[t->tm_isdst],
348 						pt, ptlim);
349 				} else  pt = _add("?", pt, ptlim);
350 				continue;
351 			case '%':
352 			/*
353 			 * X311J/88-090 (4.12.3.5): if conversion char is
354 			 * undefined, behavior is undefined.  Print out the
355 			 * character itself as printf(3) does.
356 			 */
357 			default:
358 				break;
359 			}
360 		}
361 		if (pt == ptlim)
362 			break;
363 		*pt++ = *format;
364 	}
365 	return (pt);
366 }
367 
368 static char *
_secs(t,pt,ptlim)369 _secs(t, pt, ptlim)
370 	const struct tm *t;
371 	char *pt;
372 	const char *ptlim;
373 {
374 	struct tm tmp;
375 	time_t s;
376 
377 	tmp = *t;
378 	s = mktime(&tmp);
379 	return (_conv((int)s, "%d", pt, ptlim));
380 }
381 
382 static char *
_conv(n,format,pt,ptlim)383 _conv(n, format, pt, ptlim)
384 	int n;
385 	const char *format;
386 	char *pt;
387 	const char *ptlim;
388 {
389 	char buf[INT_STRLEN_MAXIMUM(int) + 1];
390 
391 	(void) sprintf(buf, format, n);
392 	return (_add(buf, pt, ptlim));
393 }
394 
395 static char *
_add(str,pt,ptlim)396 _add(str, pt, ptlim)
397 	const char *str;
398 	char *pt;
399 	const char *ptlim;
400 {
401 
402 	while (pt < ptlim && (*pt = *str++) != '\0')
403 		++pt;
404 	return (pt);
405 }
406