1 /*	$NetBSD: strftime.c,v 1.1.1.1 2011/04/13 18:15:43 elric Exp $	*/
2 
3 /*
4  * Copyright (c) 1999 - 2002 Kungliga Tekniska Högskolan
5  * (Royal Institute of Technology, Stockholm, Sweden).
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  *
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  *
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  *
19  * 3. Neither the name of KTH nor the names of its contributors may be
20  *    used to endorse or promote products derived from this software without
21  *    specific prior written permission.
22  *
23  * THIS SOFTWARE IS PROVIDED BY KTH AND ITS CONTRIBUTORS ``AS IS'' AND ANY
24  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
26  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL KTH OR ITS CONTRIBUTORS BE
27  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
28  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
29  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
30  * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
31  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
32  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
33  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
34 
35 #include <config.h>
36 #include <krb5/roken.h>
37 #ifdef TEST_STRPFTIME
38 #include "strpftime-test.h"
39 #endif
40 
41 static const char *abb_weekdays[] = {
42     "Sun",
43     "Mon",
44     "Tue",
45     "Wed",
46     "Thu",
47     "Fri",
48     "Sat",
49 };
50 
51 static const char *full_weekdays[] = {
52     "Sunday",
53     "Monday",
54     "Tuesday",
55     "Wednesday",
56     "Thursday",
57     "Friday",
58     "Saturday",
59 };
60 
61 static const char *abb_month[] = {
62     "Jan",
63     "Feb",
64     "Mar",
65     "Apr",
66     "May",
67     "Jun",
68     "Jul",
69     "Aug",
70     "Sep",
71     "Oct",
72     "Nov",
73     "Dec"
74 };
75 
76 static const char *full_month[] = {
77     "January",
78     "February",
79     "Mars",
80     "April",
81     "May",
82     "June",
83     "July",
84     "August",
85     "September",
86     "October",
87     "November",
88     "December"
89 };
90 
91 static const char *ampm[] = {
92     "AM",
93     "PM"
94 };
95 
96 /*
97  * Convert hour in [0, 24] to [12 1 - 11 12 1 - 11 12]
98  */
99 
100 static int
101 hour_24to12 (int hour)
102 {
103     int ret = hour % 12;
104 
105     if (ret == 0)
106 	ret = 12;
107     return ret;
108 }
109 
110 /*
111  * Return AM or PM for `hour'
112  */
113 
114 static const char *
115 hour_to_ampm (int hour)
116 {
117     return ampm[hour / 12];
118 }
119 
120 /*
121  * Return the week number of `tm' (Sunday being the first day of the week)
122  * as [0, 53]
123  */
124 
125 static int
126 week_number_sun (const struct tm *tm)
127 {
128     return (tm->tm_yday + 7 - (tm->tm_yday % 7 - tm->tm_wday + 7) % 7) / 7;
129 }
130 
131 /*
132  * Return the week number of `tm' (Monday being the first day of the week)
133  * as [0, 53]
134  */
135 
136 static int
137 week_number_mon (const struct tm *tm)
138 {
139     int wday = (tm->tm_wday + 6) % 7;
140 
141     return (tm->tm_yday + 7 - (tm->tm_yday % 7 - wday + 7) % 7) / 7;
142 }
143 
144 /*
145  * Return the week number of `tm' (Monday being the first day of the
146  * week) as [01, 53].  Week number one is the one that has four or more
147  * days in that year.
148  */
149 
150 static int
151 week_number_mon4 (const struct tm *tm)
152 {
153     int wday  = (tm->tm_wday + 6) % 7;
154     int w1day = (wday - tm->tm_yday % 7 + 7) % 7;
155     int ret;
156 
157     ret = (tm->tm_yday + w1day) / 7;
158     if (w1day >= 4)
159 	--ret;
160     if (ret == -1)
161 	ret = 53;
162     else
163 	++ret;
164     return ret;
165 }
166 
167 /*
168  *
169  */
170 
171 ROKEN_LIB_FUNCTION size_t ROKEN_LIB_CALL
172 strftime (char *buf, size_t maxsize, const char *format,
173 	  const struct tm *tm)
174 {
175     size_t n = 0;
176     int ret;
177 
178     while (*format != '\0' && n < maxsize) {
179 	if (*format == '%') {
180 	    ++format;
181 	    if(*format == 'E' || *format == 'O')
182 		++format;
183 	    switch (*format) {
184 	    case 'a' :
185 		ret = snprintf (buf, maxsize - n,
186 				"%s", abb_weekdays[tm->tm_wday]);
187 		break;
188 	    case 'A' :
189 		ret = snprintf (buf, maxsize - n,
190 				"%s", full_weekdays[tm->tm_wday]);
191 		break;
192 	    case 'h' :
193 	    case 'b' :
194 		ret = snprintf (buf, maxsize - n,
195 				"%s", abb_month[tm->tm_mon]);
196 		break;
197 	    case 'B' :
198 		ret = snprintf (buf, maxsize - n,
199 				"%s", full_month[tm->tm_mon]);
200 		break;
201 	    case 'c' :
202 		ret = snprintf (buf, maxsize - n,
203 				"%d:%02d:%02d %02d:%02d:%02d",
204 				tm->tm_year,
205 				tm->tm_mon + 1,
206 				tm->tm_mday,
207 				tm->tm_hour,
208 				tm->tm_min,
209 				tm->tm_sec);
210 		break;
211 	    case 'C' :
212 		ret = snprintf (buf, maxsize - n,
213 				"%02d", (tm->tm_year + 1900) / 100);
214 		break;
215 	    case 'd' :
216 		ret = snprintf (buf, maxsize - n,
217 				"%02d", tm->tm_mday);
218 		break;
219 	    case 'D' :
220 		ret = snprintf (buf, maxsize - n,
221 				"%02d/%02d/%02d",
222 				tm->tm_mon + 1,
223 				tm->tm_mday,
224 				(tm->tm_year + 1900) % 100);
225 		break;
226 	    case 'e' :
227 		ret = snprintf (buf, maxsize - n,
228 				"%2d", tm->tm_mday);
229 		break;
230 	    case 'F':
231 		ret = snprintf (buf, maxsize - n,
232 				"%04d-%02d-%02d", tm->tm_year + 1900,
233 				tm->tm_mon + 1, tm->tm_mday);
234 		break;
235 	    case 'g':
236 		/* last two digits of week-based year */
237 		abort();
238 	    case 'G':
239 		/* week-based year */
240 		abort();
241 	    case 'H' :
242 		ret = snprintf (buf, maxsize - n,
243 				"%02d", tm->tm_hour);
244 		break;
245 	    case 'I' :
246 		ret = snprintf (buf, maxsize - n,
247 				"%02d",
248 				hour_24to12 (tm->tm_hour));
249 		break;
250 	    case 'j' :
251 		ret = snprintf (buf, maxsize - n,
252 				"%03d", tm->tm_yday + 1);
253 		break;
254 	    case 'k' :
255 		ret = snprintf (buf, maxsize - n,
256 				"%2d", tm->tm_hour);
257 		break;
258 	    case 'l' :
259 		ret = snprintf (buf, maxsize - n,
260 				"%2d",
261 				hour_24to12 (tm->tm_hour));
262 		break;
263 	    case 'm' :
264 		ret = snprintf (buf, maxsize - n,
265 				"%02d", tm->tm_mon + 1);
266 		break;
267 	    case 'M' :
268 		ret = snprintf (buf, maxsize - n,
269 				"%02d", tm->tm_min);
270 		break;
271 	    case 'n' :
272 		ret = snprintf (buf, maxsize - n, "\n");
273 		break;
274 	    case 'p' :
275 		ret = snprintf (buf, maxsize - n, "%s",
276 				hour_to_ampm (tm->tm_hour));
277 		break;
278 	    case 'r' :
279 		ret = snprintf (buf, maxsize - n,
280 				"%02d:%02d:%02d %s",
281 				hour_24to12 (tm->tm_hour),
282 				tm->tm_min,
283 				tm->tm_sec,
284 				hour_to_ampm (tm->tm_hour));
285 		break;
286 	    case 'R' :
287 		ret = snprintf (buf, maxsize - n,
288 				"%02d:%02d",
289 				tm->tm_hour,
290 				tm->tm_min);
291 
292 	    case 's' :
293 		ret = snprintf (buf, maxsize - n,
294 				"%d", (int)mktime(rk_UNCONST(tm)));
295 		break;
296 	    case 'S' :
297 		ret = snprintf (buf, maxsize - n,
298 				"%02d", tm->tm_sec);
299 		break;
300 	    case 't' :
301 		ret = snprintf (buf, maxsize - n, "\t");
302 		break;
303 	    case 'T' :
304 	    case 'X' :
305 		ret = snprintf (buf, maxsize - n,
306 				"%02d:%02d:%02d",
307 				tm->tm_hour,
308 				tm->tm_min,
309 				tm->tm_sec);
310 		break;
311 	    case 'u' :
312 		ret = snprintf (buf, maxsize - n,
313 				"%d", (tm->tm_wday == 0) ? 7 : tm->tm_wday);
314 		break;
315 	    case 'U' :
316 		ret = snprintf (buf, maxsize - n,
317 				"%02d", week_number_sun (tm));
318 		break;
319 	    case 'V' :
320 		ret = snprintf (buf, maxsize - n,
321 				"%02d", week_number_mon4 (tm));
322 		break;
323 	    case 'w' :
324 		ret = snprintf (buf, maxsize - n,
325 				"%d", tm->tm_wday);
326 		break;
327 	    case 'W' :
328 		ret = snprintf (buf, maxsize - n,
329 				"%02d", week_number_mon (tm));
330 		break;
331 	    case 'x' :
332 		ret = snprintf (buf, maxsize - n,
333 				"%d:%02d:%02d",
334 				tm->tm_year,
335 				tm->tm_mon + 1,
336 				tm->tm_mday);
337 		break;
338 	    case 'y' :
339 		ret = snprintf (buf, maxsize - n,
340 				"%02d", (tm->tm_year + 1900) % 100);
341 		break;
342 	    case 'Y' :
343 		ret = snprintf (buf, maxsize - n,
344 				"%d", tm->tm_year + 1900);
345 		break;
346 	    case 'z':
347 		ret = snprintf (buf, maxsize - n,
348 				"%ld",
349 #if defined(HAVE_STRUCT_TM_TM_GMTOFF)
350 				(long)tm->tm_gmtoff
351 #elif defined(HAVE_TIMEZONE)
352 #ifdef HAVE_ALTZONE
353 				tm->tm_isdst ?
354 				(long)altzone :
355 #endif
356 				(long)timezone
357 #else
358 #error Where in timezone chaos are you?
359 #endif
360 				);
361 		break;
362 	    case 'Z' :
363 		ret = snprintf (buf, maxsize - n,
364 				"%s",
365 
366 #if defined(HAVE_STRUCT_TM_TM_ZONE)
367 				tm->tm_zone
368 #elif defined(HAVE_TIMEZONE)
369 				tzname[tm->tm_isdst]
370 #else
371 #error what?
372 #endif
373 		    );
374 		break;
375 	    case '\0' :
376 		--format;
377 		/* FALLTHROUGH */
378 	    case '%' :
379 		ret = snprintf (buf, maxsize - n,
380 				"%%");
381 		break;
382 	    default :
383 		ret = snprintf (buf, maxsize - n,
384 				"%%%c", *format);
385 		break;
386 	    }
387 	    if (ret < 0 || ret >= (int)(maxsize - n))
388 		return 0;
389 	    n   += ret;
390 	    buf += ret;
391 	    ++format;
392 	} else {
393 	    *buf++ = *format++;
394 	    ++n;
395 	}
396     }
397     *buf++ = '\0';
398     return n;
399 }
400