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