xref: /freebsd/crypto/heimdal/lib/roken/strftime.c (revision c19800e8)
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