use client-amel
[heimdal.git] / lib / roken / strftime.c
blobd95b88c1bb0f7006467faad2d40ca6607f09fe65
1 /*
2 * Copyright (c) 1999 - 2002 Kungliga Tekniska Högskolan
3 * (Royal Institute of Technology, Stockholm, Sweden).
4 * All rights reserved.
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
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.
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.
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. */
33 #include <config.h>
34 #include "roken.h"
35 #ifdef TEST_STRPFTIME
36 #include "strpftime-test.h"
37 #endif
39 static const char *abb_weekdays[] = {
40 "Sun",
41 "Mon",
42 "Tue",
43 "Wed",
44 "Thu",
45 "Fri",
46 "Sat",
49 static const char *full_weekdays[] = {
50 "Sunday",
51 "Monday",
52 "Tuesday",
53 "Wednesday",
54 "Thursday",
55 "Friday",
56 "Saturday",
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"
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"
89 static const char *ampm[] = {
90 "AM",
91 "PM"
95 * Convert hour in [0, 24] to [12 1 - 11 12 1 - 11 12]
98 static int
99 hour_24to12 (int hour)
101 int ret = hour % 12;
103 if (ret == 0)
104 ret = 12;
105 return ret;
109 * Return AM or PM for `hour'
112 static const char *
113 hour_to_ampm (int hour)
115 return ampm[hour / 12];
119 * Return the week number of `tm' (Sunday being the first day of the week)
120 * as [0, 53]
123 static int
124 week_number_sun (const struct tm *tm)
126 return (tm->tm_yday + 7 - (tm->tm_yday % 7 - tm->tm_wday + 7) % 7) / 7;
130 * Return the week number of `tm' (Monday being the first day of the week)
131 * as [0, 53]
134 static int
135 week_number_mon (const struct tm *tm)
137 int wday = (tm->tm_wday + 6) % 7;
139 return (tm->tm_yday + 7 - (tm->tm_yday % 7 - wday + 7) % 7) / 7;
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.
148 static int
149 week_number_mon4 (const struct tm *tm)
151 int wday = (tm->tm_wday + 6) % 7;
152 int w1day = (wday - tm->tm_yday % 7 + 7) % 7;
153 int ret;
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;
169 ROKEN_LIB_FUNCTION size_t ROKEN_LIB_CALL
170 strftime (char *buf, size_t maxsize, const char *format,
171 const struct tm *tm)
173 size_t n = 0;
174 int ret;
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);
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
359 break;
360 case 'Z' :
361 ret = snprintf (buf, maxsize - n,
362 "%s",
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
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;
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;
395 *buf++ = '\0';
396 return n;