strftime.c revision 178825
1/*
2 * Copyright (c) 1999 - 2002 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#ifdef TEST_STRPFTIME
37#include "strpftime-test.h"
38#endif
39#include "roken.h"
40
41RCSID("$Id: strftime.c 21896 2007-08-09 08:46:08Z lha $");
42
43static const char *abb_weekdays[] = {
44    "Sun",
45    "Mon",
46    "Tue",
47    "Wed",
48    "Thu",
49    "Fri",
50    "Sat",
51};
52
53static const char *full_weekdays[] = {
54    "Sunday",
55    "Monday",
56    "Tuesday",
57    "Wednesday",
58    "Thursday",
59    "Friday",
60    "Saturday",
61};
62
63static const char *abb_month[] = {
64    "Jan",
65    "Feb",
66    "Mar",
67    "Apr",
68    "May",
69    "Jun",
70    "Jul",
71    "Aug",
72    "Sep",
73    "Oct",
74    "Nov",
75    "Dec"
76};
77
78static const char *full_month[] = {
79    "January",
80    "February",
81    "Mars",
82    "April",
83    "May",
84    "June",
85    "July",
86    "August",
87    "September",
88    "October",
89    "November",
90    "December"
91};
92
93static const char *ampm[] = {
94    "AM",
95    "PM"
96};
97
98/*
99 * Convert hour in [0, 24] to [12 1 - 11 12 1 - 11 12]
100 */
101
102static int
103hour_24to12 (int hour)
104{
105    int ret = hour % 12;
106
107    if (ret == 0)
108	ret = 12;
109    return ret;
110}
111
112/*
113 * Return AM or PM for `hour'
114 */
115
116static const char *
117hour_to_ampm (int hour)
118{
119    return ampm[hour / 12];
120}
121
122/*
123 * Return the week number of `tm' (Sunday being the first day of the week)
124 * as [0, 53]
125 */
126
127static int
128week_number_sun (const struct tm *tm)
129{
130    return (tm->tm_yday + 7 - (tm->tm_yday % 7 - tm->tm_wday + 7) % 7) / 7;
131}
132
133/*
134 * Return the week number of `tm' (Monday being the first day of the week)
135 * as [0, 53]
136 */
137
138static int
139week_number_mon (const struct tm *tm)
140{
141    int wday = (tm->tm_wday + 6) % 7;
142
143    return (tm->tm_yday + 7 - (tm->tm_yday % 7 - wday + 7) % 7) / 7;
144}
145
146/*
147 * Return the week number of `tm' (Monday being the first day of the
148 * week) as [01, 53].  Week number one is the one that has four or more
149 * days in that year.
150 */
151
152static int
153week_number_mon4 (const struct tm *tm)
154{
155    int wday  = (tm->tm_wday + 6) % 7;
156    int w1day = (wday - tm->tm_yday % 7 + 7) % 7;
157    int ret;
158
159    ret = (tm->tm_yday + w1day) / 7;
160    if (w1day >= 4)
161	--ret;
162    if (ret == -1)
163	ret = 53;
164    else
165	++ret;
166    return ret;
167}
168
169/*
170 *
171 */
172
173size_t ROKEN_LIB_FUNCTION
174strftime (char *buf, size_t maxsize, const char *format,
175	  const struct tm *tm)
176{
177    size_t n = 0;
178    int ret;
179
180    while (*format != '\0' && n < maxsize) {
181	if (*format == '%') {
182	    ++format;
183	    if(*format == 'E' || *format == 'O')
184		++format;
185	    switch (*format) {
186	    case 'a' :
187		ret = snprintf (buf, maxsize - n,
188				"%s", abb_weekdays[tm->tm_wday]);
189		break;
190	    case 'A' :
191		ret = snprintf (buf, maxsize - n,
192				"%s", full_weekdays[tm->tm_wday]);
193		break;
194	    case 'h' :
195	    case 'b' :
196		ret = snprintf (buf, maxsize - n,
197				"%s", abb_month[tm->tm_mon]);
198		break;
199	    case 'B' :
200		ret = snprintf (buf, maxsize - n,
201				"%s", full_month[tm->tm_mon]);
202		break;
203	    case 'c' :
204		ret = snprintf (buf, maxsize - n,
205				"%d:%02d:%02d %02d:%02d:%02d",
206				tm->tm_year,
207				tm->tm_mon + 1,
208				tm->tm_mday,
209				tm->tm_hour,
210				tm->tm_min,
211				tm->tm_sec);
212		break;
213	    case 'C' :
214		ret = snprintf (buf, maxsize - n,
215				"%02d", (tm->tm_year + 1900) / 100);
216		break;
217	    case 'd' :
218		ret = snprintf (buf, maxsize - n,
219				"%02d", tm->tm_mday);
220		break;
221	    case 'D' :
222		ret = snprintf (buf, maxsize - n,
223				"%02d/%02d/%02d",
224				tm->tm_mon + 1,
225				tm->tm_mday,
226				(tm->tm_year + 1900) % 100);
227		break;
228	    case 'e' :
229		ret = snprintf (buf, maxsize - n,
230				"%2d", tm->tm_mday);
231		break;
232	    case 'F':
233		ret = snprintf (buf, maxsize - n,
234				"%04d-%02d-%02d", tm->tm_year + 1900,
235				tm->tm_mon + 1, tm->tm_mday);
236		break;
237	    case 'g':
238		/* last two digits of week-based year */
239		abort();
240	    case 'G':
241		/* week-based year */
242		abort();
243	    case 'H' :
244		ret = snprintf (buf, maxsize - n,
245				"%02d", tm->tm_hour);
246		break;
247	    case 'I' :
248		ret = snprintf (buf, maxsize - n,
249				"%02d",
250				hour_24to12 (tm->tm_hour));
251		break;
252	    case 'j' :
253		ret = snprintf (buf, maxsize - n,
254				"%03d", tm->tm_yday + 1);
255		break;
256	    case 'k' :
257		ret = snprintf (buf, maxsize - n,
258				"%2d", tm->tm_hour);
259		break;
260	    case 'l' :
261		ret = snprintf (buf, maxsize - n,
262				"%2d",
263				hour_24to12 (tm->tm_hour));
264		break;
265	    case 'm' :
266		ret = snprintf (buf, maxsize - n,
267				"%02d", tm->tm_mon + 1);
268		break;
269	    case 'M' :
270		ret = snprintf (buf, maxsize - n,
271				"%02d", tm->tm_min);
272		break;
273	    case 'n' :
274		ret = snprintf (buf, maxsize - n, "\n");
275		break;
276	    case 'p' :
277		ret = snprintf (buf, maxsize - n, "%s",
278				hour_to_ampm (tm->tm_hour));
279		break;
280	    case 'r' :
281		ret = snprintf (buf, maxsize - n,
282				"%02d:%02d:%02d %s",
283				hour_24to12 (tm->tm_hour),
284				tm->tm_min,
285				tm->tm_sec,
286				hour_to_ampm (tm->tm_hour));
287		break;
288	    case 'R' :
289		ret = snprintf (buf, maxsize - n,
290				"%02d:%02d",
291				tm->tm_hour,
292				tm->tm_min);
293
294	    case 's' :
295		ret = snprintf (buf, maxsize - n,
296				"%d", (int)mktime(rk_UNCONST(tm)));
297		break;
298	    case 'S' :
299		ret = snprintf (buf, maxsize - n,
300				"%02d", tm->tm_sec);
301		break;
302	    case 't' :
303		ret = snprintf (buf, maxsize - n, "\t");
304		break;
305	    case 'T' :
306	    case 'X' :
307		ret = snprintf (buf, maxsize - n,
308				"%02d:%02d:%02d",
309				tm->tm_hour,
310				tm->tm_min,
311				tm->tm_sec);
312		break;
313	    case 'u' :
314		ret = snprintf (buf, maxsize - n,
315				"%d", (tm->tm_wday == 0) ? 7 : tm->tm_wday);
316		break;
317	    case 'U' :
318		ret = snprintf (buf, maxsize - n,
319				"%02d", week_number_sun (tm));
320		break;
321	    case 'V' :
322		ret = snprintf (buf, maxsize - n,
323				"%02d", week_number_mon4 (tm));
324		break;
325	    case 'w' :
326		ret = snprintf (buf, maxsize - n,
327				"%d", tm->tm_wday);
328		break;
329	    case 'W' :
330		ret = snprintf (buf, maxsize - n,
331				"%02d", week_number_mon (tm));
332		break;
333	    case 'x' :
334		ret = snprintf (buf, maxsize - n,
335				"%d:%02d:%02d",
336				tm->tm_year,
337				tm->tm_mon + 1,
338				tm->tm_mday);
339		break;
340	    case 'y' :
341		ret = snprintf (buf, maxsize - n,
342				"%02d", (tm->tm_year + 1900) % 100);
343		break;
344	    case 'Y' :
345		ret = snprintf (buf, maxsize - n,
346				"%d", tm->tm_year + 1900);
347		break;
348	    case 'z':
349		ret = snprintf (buf, maxsize - n,
350				"%ld",
351#if defined(HAVE_STRUCT_TM_TM_GMTOFF)
352				(long)tm->tm_gmtoff
353#elif defined(HAVE_TIMEZONE)
354#ifdef HAVE_ALTZONE
355				tm->tm_isdst ?
356				(long)altzone :
357#endif
358				(long)timezone
359#else
360#error Where in timezone chaos are you?
361#endif
362				);
363		break;
364	    case 'Z' :
365		ret = snprintf (buf, maxsize - n,
366				"%s",
367
368#if defined(HAVE_STRUCT_TM_TM_ZONE)
369				tm->tm_zone
370#elif defined(HAVE_TIMEZONE)
371				tzname[tm->tm_isdst]
372#else
373#error what?
374#endif
375		    );
376		break;
377	    case '\0' :
378		--format;
379		/* FALLTHROUGH */
380	    case '%' :
381		ret = snprintf (buf, maxsize - n,
382				"%%");
383		break;
384	    default :
385		ret = snprintf (buf, maxsize - n,
386				"%%%c", *format);
387		break;
388	    }
389	    if (ret < 0 || ret >= maxsize - n)
390		return 0;
391	    n   += ret;
392	    buf += ret;
393	    ++format;
394	} else {
395	    *buf++ = *format++;
396	    ++n;
397	}
398    }
399    *buf++ = '\0';
400    return n;
401}
402