This commit was manufactured by cvs2svn to create tag
[heimdal.git] / lib / roken / strptime.c
blob8262a302276ca3aaaadf67b8d4f192af2c7d5c5a
1 /*
2 * Copyright (c) 1999 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 #ifdef HAVE_CONFIG_H
34 #include <config.h>
35 #endif
36 #include <ctype.h>
37 #include "roken.h"
39 RCSID("$Id$");
41 static const char *abb_weekdays[] = {
42 "Sun",
43 "Mon",
44 "Tue",
45 "Wed",
46 "Thu",
47 "Fri",
48 "Sat",
49 NULL
52 static const char *full_weekdays[] = {
53 "Sunday",
54 "Monday",
55 "Tuesday",
56 "Wednesday",
57 "Thursday",
58 "Friday",
59 "Saturday",
60 NULL
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 NULL
79 static const char *full_month[] = {
80 "January",
81 "February",
82 "Mars",
83 "April",
84 "May",
85 "June",
86 "July",
87 "August",
88 "September",
89 "October",
90 "November",
91 "December",
92 NULL,
95 static const char *ampm[] = {
96 "am",
97 "pm",
98 NULL
102 * Try to match `*buf' to one of the strings in `strs'. Return the
103 * index of the matching string (or -1 if none). Also advance buf.
106 static int
107 match_string (const char **buf, const char **strs)
109 int i = 0;
111 for (i = 0; strs[i] != NULL; ++i) {
112 int len = strlen (strs[i]);
114 if (strncasecmp (*buf, strs[i], len) == 0) {
115 *buf += len;
116 return i;
119 return -1;
123 * tm_year is relative this year */
125 const int tm_year_base = 1900;
128 * Return TRUE iff `year' was a leap year.
131 static int
132 is_leap_year (int year)
134 return (year % 4) == 0 && ((year % 100) != 0 || (year % 400) == 0);
138 * Return the weekday [0,6] (0 = Sunday) of the first day of `year'
141 static int
142 first_day (int year)
144 int ret = 4;
146 for (; year > 1970; --year)
147 ret = (ret + 365 + is_leap_year (year) ? 1 : 0) % 7;
148 return ret;
152 * Set `timeptr' given `wnum' (week number [0, 53])
155 static void
156 set_week_number_sun (struct tm *timeptr, int wnum)
158 int fday = first_day (timeptr->tm_year + tm_year_base);
160 timeptr->tm_yday = wnum * 7 + timeptr->tm_wday - fday;
161 if (timeptr->tm_yday < 0) {
162 timeptr->tm_wday = fday;
163 timeptr->tm_yday = 0;
168 * Set `timeptr' given `wnum' (week number [0, 53])
171 static void
172 set_week_number_mon (struct tm *timeptr, int wnum)
174 int fday = (first_day (timeptr->tm_year + tm_year_base) + 6) % 7;
176 timeptr->tm_yday = wnum * 7 + (timeptr->tm_wday + 6) % 7 - fday;
177 if (timeptr->tm_yday < 0) {
178 timeptr->tm_wday = (fday + 1) % 7;
179 timeptr->tm_yday = 0;
184 * Set `timeptr' given `wnum' (week number [0, 53])
187 static void
188 set_week_number_mon4 (struct tm *timeptr, int wnum)
190 int fday = (first_day (timeptr->tm_year + tm_year_base) + 6) % 7;
191 int offset = 0;
193 if (fday < 4)
194 offset += 7;
196 timeptr->tm_yday = offset + (wnum - 1) * 7 + timeptr->tm_wday - fday;
197 if (timeptr->tm_yday < 0) {
198 timeptr->tm_wday = fday;
199 timeptr->tm_yday = 0;
207 char *
208 strptime (const char *buf, const char *format, struct tm *timeptr)
210 char c;
212 for (; (c = *format) != '\0'; ++format) {
213 char *s;
214 int ret;
216 if (isspace (c)) {
217 while (isspace (*buf))
218 ++buf;
219 } else if (c == '%' && format[1] != '\0') {
220 c = *++format;
221 if (c == 'E' || c == 'O')
222 c = *++format;
223 switch (c) {
224 case 'A' :
225 ret = match_string (&buf, full_weekdays);
226 if (ret < 0)
227 return NULL;
228 timeptr->tm_wday = ret;
229 break;
230 case 'a' :
231 ret = match_string (&buf, abb_weekdays);
232 if (ret < 0)
233 return NULL;
234 timeptr->tm_wday = ret;
235 break;
236 case 'B' :
237 ret = match_string (&buf, full_month);
238 if (ret < 0)
239 return NULL;
240 timeptr->tm_mon = ret;
241 break;
242 case 'b' :
243 case 'h' :
244 ret = match_string (&buf, abb_month);
245 if (ret < 0)
246 return NULL;
247 timeptr->tm_mon = ret;
248 break;
249 case 'C' :
250 ret = strtol (buf, &s, 10);
251 if (s == buf)
252 return NULL;
253 timeptr->tm_year = (ret * 100) - tm_year_base;
254 buf = s;
255 break;
256 case 'c' :
257 abort ();
258 case 'D' : /* %m/%d/%y */
259 s = strptime (buf, "%m/%d/%y", timeptr);
260 if (s == NULL)
261 return NULL;
262 buf = s;
263 break;
264 case 'd' :
265 case 'e' :
266 ret = strtol (buf, &s, 10);
267 if (s == buf)
268 return NULL;
269 timeptr->tm_mday = ret;
270 buf = s;
271 break;
272 case 'H' :
273 case 'k' :
274 ret = strtol (buf, &s, 10);
275 if (s == buf)
276 return NULL;
277 timeptr->tm_hour = ret;
278 buf = s;
279 break;
280 case 'I' :
281 case 'l' :
282 ret = strtol (buf, &s, 10);
283 if (s == buf)
284 return NULL;
285 if (ret == 12)
286 timeptr->tm_hour = 0;
287 else
288 timeptr->tm_hour = ret;
289 buf = s;
290 break;
291 case 'j' :
292 ret = strtol (buf, &s, 10);
293 if (s == buf)
294 return NULL;
295 timeptr->tm_yday = ret - 1;
296 buf = s;
297 break;
298 case 'm' :
299 ret = strtol (buf, &s, 10);
300 if (s == buf)
301 return NULL;
302 timeptr->tm_mon = ret - 1;
303 buf = s;
304 break;
305 case 'M' :
306 ret = strtol (buf, &s, 10);
307 if (s == buf)
308 return NULL;
309 timeptr->tm_min = ret;
310 buf = s;
311 break;
312 case 'n' :
313 if (*buf == '\n')
314 ++buf;
315 else
316 return NULL;
317 break;
318 case 'p' :
319 ret = match_string (&buf, ampm);
320 if (ret < 0)
321 return NULL;
322 if (timeptr->tm_hour == 0) {
323 if (ret == 1)
324 timeptr->tm_hour = 12;
325 } else
326 timeptr->tm_hour += 12;
327 break;
328 case 'r' : /* %I:%M:%S %p */
329 s = strptime (buf, "%I:%M:%S %p", timeptr);
330 if (s == NULL)
331 return NULL;
332 buf = s;
333 break;
334 case 'R' : /* %H:%M */
335 s = strptime (buf, "%H:%M", timeptr);
336 if (s == NULL)
337 return NULL;
338 buf = s;
339 break;
340 case 'S' :
341 ret = strtol (buf, &s, 10);
342 if (s == buf)
343 return NULL;
344 timeptr->tm_sec = ret;
345 buf = s;
346 break;
347 case 't' :
348 if (*buf == '\t')
349 ++buf;
350 else
351 return NULL;
352 break;
353 case 'T' : /* %H:%M:%S */
354 case 'X' :
355 s = strptime (buf, "%H:%M:%S", timeptr);
356 if (s == NULL)
357 return NULL;
358 buf = s;
359 break;
360 case 'u' :
361 ret = strtol (buf, &s, 10);
362 if (s == buf)
363 return NULL;
364 timeptr->tm_wday = ret - 1;
365 buf = s;
366 break;
367 case 'w' :
368 ret = strtol (buf, &s, 10);
369 if (s == buf)
370 return NULL;
371 timeptr->tm_wday = ret;
372 buf = s;
373 break;
374 case 'U' :
375 ret = strtol (buf, &s, 10);
376 if (s == buf)
377 return NULL;
378 set_week_number_sun (timeptr, ret);
379 buf = s;
380 break;
381 case 'V' :
382 ret = strtol (buf, &s, 10);
383 if (s == buf)
384 return NULL;
385 set_week_number_mon4 (timeptr, ret);
386 buf = s;
387 break;
388 case 'W' :
389 ret = strtol (buf, &s, 10);
390 if (s == buf)
391 return NULL;
392 set_week_number_mon (timeptr, ret);
393 buf = s;
394 break;
395 case 'x' :
396 s = strptime (buf, "%Y:%m:%d", timeptr);
397 if (s == NULL)
398 return NULL;
399 buf = s;
400 break;
401 case 'y' :
402 ret = strtol (buf, &s, 10);
403 if (s == buf)
404 return NULL;
405 if (ret < 70)
406 timeptr->tm_year = 100 + ret;
407 else
408 timeptr->tm_year = ret;
409 buf = s;
410 break;
411 case 'Y' :
412 ret = strtol (buf, &s, 10);
413 if (s == buf)
414 return NULL;
415 timeptr->tm_year = ret - tm_year_base;
416 buf = s;
417 break;
418 case 'Z' :
419 abort ();
420 case '\0' :
421 --format;
422 /* FALLTHROUGH */
423 case '%' :
424 if (*buf == '%')
425 ++buf;
426 else
427 return NULL;
428 break;
429 default :
430 if (*buf == '%' || *++buf == c)
431 ++buf;
432 else
433 return NULL;
434 break;
436 } else {
437 if (*buf == c)
438 ++buf;
439 else
440 return NULL;
443 return (char *)buf;