strftime.c revision 90926
1/*
2 * Copyright (c) 1999 - 2001 Kungliga Tekniska H�gskolan
3 * (Royal Institute of Technology, Stockholm, Sweden).
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 *
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 *
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 *
17 * 3. Neither the name of KTH nor the names of its contributors may be
18 *    used to endorse or promote products derived from this software without
19 *    specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY KTH AND ITS CONTRIBUTORS ``AS IS'' AND ANY
22 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
24 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL KTH OR ITS CONTRIBUTORS BE
25 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
26 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
27 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
28 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
29 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
30 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
31 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
32
33#ifdef HAVE_CONFIG_H
34#include <config.h>
35#endif
36#include "roken.h"
37
38RCSID("$Id: strftime.c,v 1.12 2001/09/04 09:53:51 assar Exp $");
39
40static const char *abb_weekdays[] = {
41    "Sun",
42    "Mon",
43    "Tue",
44    "Wed",
45    "Thu",
46    "Fri",
47    "Sat",
48};
49
50static const char *full_weekdays[] = {
51    "Sunday",
52    "Monday",
53    "Tuesday",
54    "Wednesday",
55    "Thursday",
56    "Friday",
57    "Saturday",
58};
59
60static const char *abb_month[] = {
61    "Jan",
62    "Feb",
63    "Mar",
64    "Apr",
65    "May",
66    "Jun",
67    "Jul",
68    "Aug",
69    "Sep",
70    "Oct",
71    "Nov",
72    "Dec"
73};
74
75static const char *full_month[] = {
76    "January",
77    "February",
78    "Mars",
79    "April",
80    "May",
81    "June",
82    "July",
83    "August",
84    "September",
85    "October",
86    "November",
87    "December"
88};
89
90static const char *ampm[] = {
91    "AM",
92    "PM"
93};
94
95/*
96 * Convert hour in [0, 24] to [12 1 - 11 12 1 - 11 12]
97 */
98
99static int
100hour_24to12 (int hour)
101{
102    int ret = hour % 12;
103
104    if (ret == 0)
105	ret = 12;
106    return ret;
107}
108
109/*
110 * Return AM or PM for `hour'
111 */
112
113static const char *
114hour_to_ampm (int hour)
115{
116    return ampm[hour / 12];
117}
118
119/*
120 * Return the week number of `tm' (Sunday being the first day of the week)
121 * as [0, 53]
122 */
123
124static int
125week_number_sun (const struct tm *tm)
126{
127    return (tm->tm_yday + 7 - (tm->tm_yday % 7 - tm->tm_wday + 7) % 7) / 7;
128}
129
130/*
131 * Return the week number of `tm' (Monday being the first day of the week)
132 * as [0, 53]
133 */
134
135static int
136week_number_mon (const struct tm *tm)
137{
138    int wday = (tm->tm_wday + 6) % 7;
139
140    return (tm->tm_yday + 7 - (tm->tm_yday % 7 - wday + 7) % 7) / 7;
141}
142
143/*
144 * Return the week number of `tm' (Monday being the first day of the
145 * week) as [01, 53].  Week number one is the one that has four or more
146 * days in that year.
147 */
148
149static int
150week_number_mon4 (const struct tm *tm)
151{
152    int wday  = (tm->tm_wday + 6) % 7;
153    int w1day = (wday - tm->tm_yday % 7 + 7) % 7;
154    int ret;
155
156    ret = (tm->tm_yday + w1day) / 7;
157    if (w1day >= 4)
158	--ret;
159    if (ret == -1)
160	ret = 53;
161    else
162	++ret;
163    return ret;
164}
165
166/*
167 *
168 */
169
170size_t
171strftime (char *buf, size_t maxsize, const char *format,
172	  const struct tm *tm)
173{
174    size_t n = 0;
175    int ret;
176
177    while (*format != '\0' && n < maxsize) {
178	if (*format == '%') {
179	    ++format;
180	    if(*format == 'E' || *format == 'O')
181		++format;
182	    switch (*format) {
183	    case 'a' :
184		ret = snprintf (buf, maxsize - n,
185				"%s", abb_weekdays[tm->tm_wday]);
186		break;
187	    case 'A' :
188		ret = snprintf (buf, maxsize - n,
189				"%s", full_weekdays[tm->tm_wday]);
190		break;
191	    case 'h' :
192	    case 'b' :
193		ret = snprintf (buf, maxsize - n,
194				"%s", abb_month[tm->tm_mon]);
195		break;
196	    case 'B' :
197		ret = snprintf (buf, maxsize - n,
198				"%s", full_month[tm->tm_mon]);
199		break;
200	    case 'c' :
201		ret = snprintf (buf, maxsize - n,
202				"%d:%02d:%02d %02d:%02d:%02d",
203				tm->tm_year,
204				tm->tm_mon + 1,
205				tm->tm_mday,
206				tm->tm_hour,
207				tm->tm_min,
208				tm->tm_sec);
209		break;
210	    case 'C' :
211		ret = snprintf (buf, maxsize - n,
212				"%02d", (tm->tm_year + 1900) / 100);
213		break;
214	    case 'd' :
215		ret = snprintf (buf, maxsize - n,
216				"%02d", tm->tm_mday);
217		break;
218	    case 'D' :
219		ret = snprintf (buf, maxsize - n,
220				"%02d/%02d/%02d",
221				tm->tm_mon + 1,
222				tm->tm_mday,
223				(tm->tm_year + 1900) % 100);
224		break;
225	    case 'e' :
226		ret = snprintf (buf, maxsize - n,
227				"%2d", tm->tm_mday);
228		break;
229	    case 'F':
230		ret = snprintf (buf, maxsize - n,
231				"%04d-%02d-%02d", tm->tm_year + 1900,
232				tm->tm_mon + 1, tm->tm_mday);
233		break;
234	    case 'g':
235		/* last two digits of week-based year */
236		abort();
237	    case 'G':
238		/* week-based year */
239		abort();
240	    case 'H' :
241		ret = snprintf (buf, maxsize - n,
242				"%02d", tm->tm_hour);
243		break;
244	    case 'I' :
245		ret = snprintf (buf, maxsize - n,
246				"%02d",
247				hour_24to12 (tm->tm_hour));
248		break;
249	    case 'j' :
250		ret = snprintf (buf, maxsize - n,
251				"%03d", tm->tm_yday + 1);
252		break;
253	    case 'k' :
254		ret = snprintf (buf, maxsize - n,
255				"%2d", tm->tm_hour);
256		break;
257	    case 'l' :
258		ret = snprintf (buf, maxsize - n,
259				"%2d",
260				hour_24to12 (tm->tm_hour));
261		break;
262	    case 'm' :
263		ret = snprintf (buf, maxsize - n,
264				"%02d", tm->tm_mon + 1);
265		break;
266	    case 'M' :
267		ret = snprintf (buf, maxsize - n,
268				"%02d", tm->tm_min);
269		break;
270	    case 'n' :
271		ret = snprintf (buf, maxsize - n, "\n");
272		break;
273	    case 'p' :
274		ret = snprintf (buf, maxsize - n, "%s",
275				hour_to_ampm (tm->tm_hour));
276		break;
277	    case 'r' :
278		ret = snprintf (buf, maxsize - n,
279				"%02d:%02d:%02d %s",
280				hour_24to12 (tm->tm_hour),
281				tm->tm_min,
282				tm->tm_sec,
283				hour_to_ampm (tm->tm_hour));
284		break;
285	    case 'R' :
286		ret = snprintf (buf, maxsize - n,
287				"%02d:%02d",
288				tm->tm_hour,
289				tm->tm_min);
290
291	    case 's' :
292		ret = snprintf (buf, maxsize - n,
293				"%d", (int)mktime((struct tm *)tm));
294		break;
295	    case 'S' :
296		ret = snprintf (buf, maxsize - n,
297				"%02d", tm->tm_sec);
298		break;
299	    case 't' :
300		ret = snprintf (buf, maxsize - n, "\t");
301		break;
302	    case 'T' :
303	    case 'X' :
304		ret = snprintf (buf, maxsize - n,
305				"%02d:%02d:%02d",
306				tm->tm_hour,
307				tm->tm_min,
308				tm->tm_sec);
309		break;
310	    case 'u' :
311		ret = snprintf (buf, maxsize - n,
312				"%d", (tm->tm_wday == 0) ? 7 : tm->tm_wday);
313		break;
314	    case 'U' :
315		ret = snprintf (buf, maxsize - n,
316				"%02d", week_number_sun (tm));
317		break;
318	    case 'V' :
319		ret = snprintf (buf, maxsize - n,
320				"%02d", week_number_mon4 (tm));
321		break;
322	    case 'w' :
323		ret = snprintf (buf, maxsize - n,
324				"%d", tm->tm_wday);
325		break;
326	    case 'W' :
327		ret = snprintf (buf, maxsize - n,
328				"%02d", week_number_mon (tm));
329		break;
330	    case 'x' :
331		ret = snprintf (buf, maxsize - n,
332				"%d:%02d:%02d",
333				tm->tm_year,
334				tm->tm_mon + 1,
335				tm->tm_mday);
336		break;
337	    case 'y' :
338		ret = snprintf (buf, maxsize - n,
339				"%02d", (tm->tm_year + 1900) % 100);
340		break;
341	    case 'Y' :
342		ret = snprintf (buf, maxsize - n,
343				"%d", tm->tm_year + 1900);
344		break;
345	    case 'z':
346		ret = snprintf (buf, maxsize - n,
347				"%ld",
348#if defined(HAVE_STRUCT_TM_TM_GMTOFF)
349				(long)tm->tm_gmtoff
350#elif defined(HAVE_TIMEZONE)
351				tm->tm_isdst ?
352				(long)altzone :
353				(long)timezone
354#else
355#error Where in timezone chaos are you?
356#endif
357				);
358		break;
359	    case 'Z' :
360		ret = snprintf (buf, maxsize - n,
361				"%s",
362
363#if defined(HAVE_STRUCT_TM_TM_ZONE)
364				tm->tm_zone
365#elif defined(HAVE_TIMEZONE)
366				tzname[tm->tm_isdst]
367#else
368#error what?
369#endif
370		    );
371		break;
372	    case '\0' :
373		--format;
374		/* FALLTHROUGH */
375	    case '%' :
376		ret = snprintf (buf, maxsize - n,
377				"%%");
378		break;
379	    default :
380		ret = snprintf (buf, maxsize - n,
381				"%%%c", *format);
382		break;
383	    }
384	    if (ret < 0 || ret >= maxsize - n)
385		return 0;
386	    n   += ret;
387	    buf += ret;
388	    ++format;
389	} else {
390	    *buf++ = *format++;
391	    ++n;
392	}
393    }
394    *buf++ = '\0';
395    return n;
396}
397