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