xref: /freebsd/crypto/heimdal/lib/roken/strftime.c (revision 1e413cf93298b5b97441a21d9a50fdcd0ee9945e)
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 #include "roken.h"
37 
38 RCSID("$Id: strftime.c,v 1.13 2002/08/20 12:42:37 joda Exp $");
39 
40 static const char *abb_weekdays[] = {
41     "Sun",
42     "Mon",
43     "Tue",
44     "Wed",
45     "Thu",
46     "Fri",
47     "Sat",
48 };
49 
50 static const char *full_weekdays[] = {
51     "Sunday",
52     "Monday",
53     "Tuesday",
54     "Wednesday",
55     "Thursday",
56     "Friday",
57     "Saturday",
58 };
59 
60 static 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 
75 static 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 
90 static 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 
99 static int
100 hour_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 
113 static const char *
114 hour_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 
124 static int
125 week_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 
135 static int
136 week_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 
149 static int
150 week_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 
170 size_t
171 strftime (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 #ifdef HAVE_ALTZONE
352 				tm->tm_isdst ?
353 				(long)altzone :
354 #endif
355 				(long)timezone
356 #else
357 #error Where in timezone chaos are you?
358 #endif
359 				);
360 		break;
361 	    case 'Z' :
362 		ret = snprintf (buf, maxsize - n,
363 				"%s",
364 
365 #if defined(HAVE_STRUCT_TM_TM_ZONE)
366 				tm->tm_zone
367 #elif defined(HAVE_TIMEZONE)
368 				tzname[tm->tm_isdst]
369 #else
370 #error what?
371 #endif
372 		    );
373 		break;
374 	    case '\0' :
375 		--format;
376 		/* FALLTHROUGH */
377 	    case '%' :
378 		ret = snprintf (buf, maxsize - n,
379 				"%%");
380 		break;
381 	    default :
382 		ret = snprintf (buf, maxsize - n,
383 				"%%%c", *format);
384 		break;
385 	    }
386 	    if (ret < 0 || ret >= maxsize - n)
387 		return 0;
388 	    n   += ret;
389 	    buf += ret;
390 	    ++format;
391 	} else {
392 	    *buf++ = *format++;
393 	    ++n;
394 	}
395     }
396     *buf++ = '\0';
397     return n;
398 }
399