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