xref: /freebsd/crypto/heimdal/lib/roken/strftime.c (revision 39beb93c3f8bdbf72a61fda42300b5ebed7390c8)
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 
41 RCSID("$Id: strftime.c 21896 2007-08-09 08:46:08Z lha $");
42 
43 static const char *abb_weekdays[] = {
44     "Sun",
45     "Mon",
46     "Tue",
47     "Wed",
48     "Thu",
49     "Fri",
50     "Sat",
51 };
52 
53 static const char *full_weekdays[] = {
54     "Sunday",
55     "Monday",
56     "Tuesday",
57     "Wednesday",
58     "Thursday",
59     "Friday",
60     "Saturday",
61 };
62 
63 static 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 
78 static 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 
93 static 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 
102 static int
103 hour_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 
116 static const char *
117 hour_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 
127 static int
128 week_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 
138 static int
139 week_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 
152 static int
153 week_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 
173 size_t ROKEN_LIB_FUNCTION
174 strftime (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