Update.
[glibc.git] / time / strptime.c
blob90b88a1ba33746c627506c5e674b2fe9e478bf75
1 /* Convert a string representation of time to a time value.
2 Copyright (C) 1996, 1997 Free Software Foundation, Inc.
3 This file is part of the GNU C Library.
4 Contributed by Ulrich Drepper <drepper@cygnus.com>, 1996.
6 The GNU C Library is free software; you can redistribute it and/or
7 modify it under the terms of the GNU Library General Public License as
8 published by the Free Software Foundation; either version 2 of the
9 License, or (at your option) any later version.
11 The GNU C Library is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Library General Public License for more details.
16 You should have received a copy of the GNU Library General Public
17 License along with the GNU C Library; see the file COPYING.LIB. If not,
18 write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19 Boston, MA 02111-1307, USA. */
21 /* XXX This version of the implementation is not really complete.
22 Some of the fields cannot add information alone. But if seeing
23 some of them in the same format (such as year, week and weekday)
24 this is enough information for determining the date. */
26 #ifdef HAVE_CONFIG_H
27 # include <config.h>
28 #endif
30 #include <ctype.h>
31 #include <langinfo.h>
32 #include <limits.h>
33 #include <string.h>
34 #include <time.h>
36 #ifdef _LIBC
37 # include "../locale/localeinfo.h"
38 #endif
41 #ifndef __P
42 # if defined (__GNUC__) || (defined (__STDC__) && __STDC__)
43 # define __P(args) args
44 # else
45 # define __P(args) ()
46 # endif /* GCC. */
47 #endif /* Not __P. */
49 #if ! HAVE_LOCALTIME_R && ! defined (localtime_r)
50 #ifdef _LIBC
51 #define localtime_r __localtime_r
52 #else
53 /* Approximate localtime_r as best we can in its absence. */
54 #define localtime_r my_localtime_r
55 static struct tm *localtime_r __P ((const time_t *, struct tm *));
56 static struct tm *
57 localtime_r (t, tp)
58 const time_t *t;
59 struct tm *tp;
61 struct tm *l = localtime (t);
62 if (! l)
63 return 0;
64 *tp = *l;
65 return tp;
67 #endif /* ! _LIBC */
68 #endif /* ! HAVE_LOCALTIME_R && ! defined (localtime_r) */
71 #define match_char(ch1, ch2) if (ch1 != ch2) return NULL
72 #if defined __GNUC__ && __GNUC__ >= 2
73 # define match_string(cs1, s2) \
74 ({ size_t len = strlen (cs1); \
75 int result = strncasecmp ((cs1), (s2), len) == 0; \
76 if (result) (s2) += len; \
77 result; })
78 #else
79 /* Oh come on. Get a reasonable compiler. */
80 # define match_string(cs1, s2) \
81 (strncasecmp ((cs1), (s2), strlen (cs1)) ? 0 : ((s2) += strlen (cs1), 1))
82 #endif
83 /* We intentionally do not use isdigit() for testing because this will
84 lead to problems with the wide character version. */
85 #define get_number(from, to) \
86 do { \
87 val = 0; \
88 if (*rp < '0' || *rp > '9') \
89 return NULL; \
90 do { \
91 val *= 10; \
92 val += *rp++ - '0'; \
93 } while (val * 10 <= to && *rp >= '0' && *rp <= '9'); \
94 if (val < from || val > to) \
95 return NULL; \
96 } while (0)
97 #ifdef _NL_CURRENT
98 # define get_alt_number(from, to) \
99 do { \
100 if (*decided != raw) \
102 const char *alts = _NL_CURRENT (LC_TIME, ALT_DIGITS); \
103 val = 0; \
104 while (*alts != '\0') \
106 size_t len = strlen (alts); \
107 if (strncasecmp (alts, rp, len) == 0) \
108 break; \
109 alts = strchr (alts, '\0') + 1; \
110 ++val; \
112 if (*alts == '\0') \
114 if (*decided == loc && val != 0) \
115 return NULL; \
117 else \
119 *decided = loc; \
120 break; \
123 get_number (from, to); \
124 } while (0)
125 #else
126 # define get_alt_number(from, to) \
127 /* We don't have the alternate representation. */ \
128 get_number(from, to)
129 #endif
130 #define recursive(new_fmt) \
131 (*(new_fmt) != '\0' \
132 && (rp = strptime_internal (rp, (new_fmt), tm, decided)) != NULL)
135 #ifdef _LIBC
136 /* This is defined in locale/C-time.c in the GNU libc. */
137 extern const struct locale_data _nl_C_LC_TIME;
139 # define weekday_name (&_nl_C_LC_TIME.values[_NL_ITEM_INDEX (DAY_1)].string)
140 # define ab_weekday_name \
141 (&_nl_C_LC_TIME.values[_NL_ITEM_INDEX (ABDAY_1)].string)
142 # define month_name (&_nl_C_LC_TIME.values[_NL_ITEM_INDEX (MON_1)].string)
143 # define ab_month_name (&_nl_C_LC_TIME.values[_NL_ITEM_INDEX (ABMON_1)].string)
144 # define HERE_D_T_FMT (_nl_C_LC_TIME.values[_NL_ITEM_INDEX (D_T_FMT)].string)
145 # define HERE_D_FMT (_nl_C_LC_TIME.values[_NL_ITEM_INDEX (D_T_FMT)].string)
146 # define HERE_AM_STR (_nl_C_LC_TIME.values[_NL_ITEM_INDEX (AM_STR)].string)
147 # define HERE_PM_STR (_nl_C_LC_TIME.values[_NL_ITEM_INDEX (PM_STR)].string)
148 # define HERE_T_FMT_AMPM \
149 (_nl_C_LC_TIME.values[_NL_ITEM_INDEX (T_FMT_AMPM)].string)
150 # define HERE_T_FMT (_nl_C_LC_TIME.values[_NL_ITEM_INDEX (T_FMT)].string)
151 #else
152 static char const weekday_name[][10] =
154 "Sunday", "Monday", "Tuesday", "Wednesday",
155 "Thursday", "Friday", "Saturday"
157 static char const ab_weekday_name[][4] =
159 "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
161 static char const month_name[][10] =
163 "January", "February", "March", "April", "May", "June",
164 "July", "August", "September", "October", "November", "December"
166 static char const ab_month_name[][4] =
168 "Jan", "Feb", "Mar", "Apr", "May", "Jun",
169 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
171 # define HERE_D_T_FMT "%a %b %e %H:%M:%S %Y"
172 # define HERE_D_FMT "%m/%d/%y"
173 # define HERE_AM_STR "AM"
174 # define HERE_PM_STR "PM"
175 # define HERE_T_FMT_AMPM "%I:%M:%S %p"
176 # define HERE_T_FMT "%H:%M:%S"
177 #endif
179 /* Status of lookup: do we use the locale data or the raw data? */
180 enum locale_status { not, loc, raw };
182 static char *
183 strptime_internal __P ((const char *buf, const char *format, struct tm *tm,
184 enum locale_status *decided));
186 static char *
187 strptime_internal (buf, format, tm, decided)
188 const char *buf;
189 const char *format;
190 struct tm *tm;
191 enum locale_status *decided;
193 const char *rp;
194 const char *fmt;
195 int cnt;
196 size_t val;
197 int have_I, is_pm;
199 rp = buf;
200 fmt = format;
201 have_I = is_pm = 0;
203 while (*fmt != '\0')
205 /* A white space in the format string matches 0 more or white
206 space in the input string. */
207 if (isspace (*fmt))
209 while (isspace (*rp))
210 ++rp;
211 ++fmt;
212 continue;
215 /* Any character but `%' must be matched by the same character
216 in the iput string. */
217 if (*fmt != '%')
219 match_char (*fmt++, *rp++);
220 continue;
223 ++fmt;
224 #ifndef _NL_CURRENT
225 /* We need this for handling the `E' modifier. */
226 start_over:
227 #endif
228 switch (*fmt++)
230 case '%':
231 /* Match the `%' character itself. */
232 match_char ('%', *rp++);
233 break;
234 case 'a':
235 case 'A':
236 /* Match day of week. */
237 for (cnt = 0; cnt < 7; ++cnt)
239 #ifdef _NL_CURRENT
240 if (*decided !=raw)
242 if (match_string (_NL_CURRENT (LC_TIME, DAY_1 + cnt), rp))
244 if (*decided == not
245 && strcmp (_NL_CURRENT (LC_TIME, DAY_1 + cnt),
246 weekday_name[cnt]))
247 *decided = loc;
248 break;
250 if (match_string (_NL_CURRENT (LC_TIME, ABDAY_1 + cnt), rp))
252 if (*decided == not
253 && strcmp (_NL_CURRENT (LC_TIME, ABDAY_1 + cnt),
254 ab_weekday_name[cnt]))
255 *decided = loc;
256 break;
259 #endif
260 if (*decided != loc
261 && (match_string (weekday_name[cnt], rp)
262 || match_string (ab_weekday_name[cnt], rp)))
264 *decided = raw;
265 break;
268 if (cnt == 7)
269 /* Does not match a weekday name. */
270 return NULL;
271 tm->tm_wday = cnt;
272 break;
273 case 'b':
274 case 'B':
275 case 'h':
276 /* Match month name. */
277 for (cnt = 0; cnt < 12; ++cnt)
279 #ifdef _NL_CURRENT
280 if (*decided !=raw)
282 if (match_string (_NL_CURRENT (LC_TIME, MON_1 + cnt), rp))
284 if (*decided == not
285 && strcmp (_NL_CURRENT (LC_TIME, MON_1 + cnt),
286 month_name[cnt]))
287 *decided = loc;
288 break;
290 if (match_string (_NL_CURRENT (LC_TIME, ABMON_1 + cnt), rp))
292 if (*decided == not
293 && strcmp (_NL_CURRENT (LC_TIME, ABMON_1 + cnt),
294 ab_month_name[cnt]))
295 *decided = loc;
296 break;
299 #endif
300 if (match_string (month_name[cnt], rp)
301 || match_string (ab_month_name[cnt], rp))
303 *decided = raw;
304 break;
307 if (cnt == 12)
308 /* Does not match a month name. */
309 return NULL;
310 tm->tm_mon = cnt;
311 break;
312 case 'c':
313 /* Match locale's date and time format. */
314 #ifdef _NL_CURRENT
315 if (*decided != raw)
317 if (!recursive (_NL_CURRENT (LC_TIME, D_T_FMT)))
319 if (*decided == loc)
320 return NULL;
322 else
324 if (*decided == not &&
325 strcmp (_NL_CURRENT (LC_TIME, D_T_FMT), HERE_D_T_FMT))
326 *decided = loc;
327 break;
329 *decided = raw;
331 #endif
332 if (!recursive (HERE_D_T_FMT))
333 return NULL;
334 break;
335 case 'C':
336 /* Match century number. */
337 get_number (0, 99);
338 /* We don't need the number. */
339 break;
340 case 'd':
341 case 'e':
342 /* Match day of month. */
343 get_number (1, 31);
344 tm->tm_mday = val;
345 break;
346 case 'x':
347 #ifdef _NL_CURRENT
348 if (*decided != raw)
350 if (!recursive (_NL_CURRENT (LC_TIME, D_FMT)))
352 if (*decided == loc)
353 return NULL;
355 else
357 if (decided == not
358 && strcmp (_NL_CURRENT (LC_TIME, D_FMT), HERE_D_FMT))
359 *decided = loc;
360 break;
362 *decided = raw;
364 #endif
365 /* Fall through. */
366 case 'D':
367 /* Match standard day format. */
368 if (!recursive (HERE_D_FMT))
369 return NULL;
370 break;
371 case 'H':
372 /* Match hour in 24-hour clock. */
373 get_number (0, 23);
374 tm->tm_hour = val;
375 have_I = 0;
376 break;
377 case 'I':
378 /* Match hour in 12-hour clock. */
379 get_number (1, 12);
380 tm->tm_hour = val % 12;
381 have_I = 1;
382 break;
383 case 'j':
384 /* Match day number of year. */
385 get_number (1, 366);
386 tm->tm_yday = val - 1;
387 break;
388 case 'm':
389 /* Match number of month. */
390 get_number (1, 12);
391 tm->tm_mon = val - 1;
392 break;
393 case 'M':
394 /* Match minute. */
395 get_number (0, 59);
396 tm->tm_min = val;
397 break;
398 case 'n':
399 case 't':
400 /* Match any white space. */
401 while (isspace (*rp))
402 ++rp;
403 break;
404 case 'p':
405 /* Match locale's equivalent of AM/PM. */
406 #ifdef _NL_CURRENT
407 if (*decided != raw)
409 if (match_string (_NL_CURRENT (LC_TIME, AM_STR), rp))
411 if (strcmp (_NL_CURRENT (LC_TIME, AM_STR), HERE_AM_STR))
412 *decided = loc;
413 break;
415 if (match_string (_NL_CURRENT (LC_TIME, PM_STR), rp))
417 if (strcmp (_NL_CURRENT (LC_TIME, PM_STR), HERE_PM_STR))
418 *decided = loc;
419 is_pm = 1;
420 break;
422 *decided = raw;
424 #endif
425 if (!match_string (HERE_AM_STR, rp))
426 if (match_string (HERE_PM_STR, rp))
427 is_pm = 1;
428 else
429 return NULL;
430 break;
431 case 'r':
432 #ifdef _NL_CURRENT
433 if (*decided != raw)
435 if (!recursive (_NL_CURRENT (LC_TIME, T_FMT_AMPM)))
437 if (*decided == loc)
438 return NULL;
440 else
442 if (*decided == not &&
443 strcmp (_NL_CURRENT (LC_TIME, T_FMT_AMPM),
444 HERE_T_FMT_AMPM))
445 *decided = loc;
446 break;
448 *decided = raw;
450 #endif
451 if (!recursive (HERE_T_FMT_AMPM))
452 return NULL;
453 break;
454 case 'R':
455 if (!recursive ("%H:%M"))
456 return NULL;
457 break;
458 case 's':
460 /* The number of seconds may be very high so we cannot use
461 the `get_number' macro. Instead read the number
462 character for character and construct the result while
463 doing this. */
464 time_t secs;
465 if (*rp < '0' || *rp > '9')
466 /* We need at least one digit. */
467 return NULL;
471 secs *= 10;
472 secs += *rp++ - '0';
474 while (*rp >= '0' && *rp <= '9');
476 if (localtime_r (&secs, tm) == NULL)
477 /* Error in function. */
478 return NULL;
480 break;
481 case 'S':
482 get_number (0, 61);
483 tm->tm_sec = val;
484 break;
485 case 'X':
486 #ifdef _NL_CURRENT
487 if (*decided != raw)
489 if (!recursive (_NL_CURRENT (LC_TIME, T_FMT)))
491 if (*decided == loc)
492 return NULL;
494 else
496 if (strcmp (_NL_CURRENT (LC_TIME, T_FMT), HERE_T_FMT))
497 *decided = loc;
498 break;
500 *decided = raw;
502 #endif
503 /* Fall through. */
504 case 'T':
505 if (!recursive (HERE_T_FMT))
506 return NULL;
507 break;
508 case 'u':
509 get_number (1, 7);
510 tm->tm_wday = val % 7;
511 break;
512 case 'g':
513 get_number (0, 99);
514 /* XXX This cannot determine any field in TM. */
515 break;
516 case 'G':
517 if (*rp < '0' || *rp > '9')
518 return NULL;
519 /* XXX Ignore the number since we would need some more
520 information to compute a real date. */
522 ++rp;
523 while (*rp >= '0' && *rp <= '9');
524 break;
525 case 'U':
526 case 'V':
527 case 'W':
528 get_number (0, 53);
529 /* XXX This cannot determine any field in TM without some
530 information. */
531 break;
532 case 'w':
533 /* Match number of weekday. */
534 get_number (0, 6);
535 tm->tm_wday = val;
536 break;
537 case 'y':
538 /* Match year within century. */
539 get_number (0, 99);
540 tm->tm_year = val >= 50 ? val : val + 100;
541 break;
542 case 'Y':
543 /* Match year including century number. */
544 get_number (0, INT_MAX);
545 tm->tm_year = val - 1900;
546 break;
547 case 'Z':
548 /* XXX How to handle this? */
549 break;
550 case 'E':
551 #ifdef _NL_CURRENT
552 switch (*fmt++)
554 case 'c':
555 /* Match locale's alternate date and time format. */
556 if (*decided != raw)
558 const char *fmt = _NL_CURRENT (LC_TIME, ERA_D_T_FMT);
560 if (*fmt == '\0')
561 fmt = _NL_CURRENT (LC_TIME, D_T_FMT);
563 if (!recursive (fmt))
565 if (*decided == loc)
566 return NULL;
568 else
570 if (strcmp (fmt, HERE_D_T_FMT))
571 *decided = loc;
572 break;
574 *decided = raw;
576 /* The C locale has no era information, so use the
577 normal representation. */
578 if (!recursive (HERE_D_T_FMT))
579 return NULL;
580 break;
581 case 'C':
582 case 'y':
583 case 'Y':
584 /* Match name of base year in locale's alternate
585 representation. */
586 /* XXX This is currently not implemented. It should
587 use the value _NL_CURRENT (LC_TIME, ERA). */
588 break;
589 case 'x':
590 if (*decided != raw)
592 const char *fmt = _NL_CURRENT (LC_TIME, ERA_D_FMT);
594 if (*fmt == '\0')
595 fmt = _NL_CURRENT (LC_TIME, D_FMT);
597 if (!recursive (fmt))
599 if (*decided == loc)
600 return NULL;
602 else
604 if (strcmp (fmt, HERE_D_FMT))
605 *decided = loc;
606 break;
608 *decided = raw;
610 if (!recursive (HERE_D_FMT))
611 return NULL;
612 break;
613 case 'X':
614 if (*decided != raw)
616 const char *fmt = _NL_CURRENT (LC_TIME, ERA_T_FMT);
618 if (*fmt == '\0')
619 fmt = _NL_CURRENT (LC_TIME, T_FMT);
621 if (!recursive (fmt))
623 if (*decided == loc)
624 return NULL;
626 else
628 if (strcmp (fmt, HERE_T_FMT))
629 *decided = loc;
630 break;
632 *decided = raw;
634 if (!recursive (HERE_T_FMT))
635 return NULL;
636 break;
637 default:
638 return NULL;
640 break;
641 #else
642 /* We have no information about the era format. Just use
643 the normal format. */
644 if (*fmt != 'c' && *fmt != 'C' && *fmt != 'y' && *fmt != 'Y'
645 && *fmt != 'x' && *fmt != 'X')
646 /* This is an illegal format. */
647 return NULL;
649 goto start_over;
650 #endif
651 case 'O':
652 switch (*fmt++)
654 case 'd':
655 case 'e':
656 /* Match day of month using alternate numeric symbols. */
657 get_alt_number (1, 31);
658 tm->tm_mday = val;
659 break;
660 case 'H':
661 /* Match hour in 24-hour clock using alternate numeric
662 symbols. */
663 get_alt_number (0, 23);
664 tm->tm_hour = val;
665 have_I = 0;
666 break;
667 case 'I':
668 /* Match hour in 12-hour clock using alternate numeric
669 symbols. */
670 get_alt_number (1, 12);
671 tm->tm_hour = val - 1;
672 have_I = 1;
673 break;
674 case 'm':
675 /* Match month using alternate numeric symbols. */
676 get_alt_number (1, 12);
677 tm->tm_mon = val - 1;
678 break;
679 case 'M':
680 /* Match minutes using alternate numeric symbols. */
681 get_alt_number (0, 59);
682 tm->tm_min = val;
683 break;
684 case 'S':
685 /* Match seconds using alternate numeric symbols. */
686 get_alt_number (0, 61);
687 tm->tm_sec = val;
688 break;
689 case 'U':
690 case 'V':
691 case 'W':
692 get_alt_number (0, 53);
693 /* XXX This cannot determine any field in TM without
694 further information. */
695 break;
696 case 'w':
697 /* Match number of weekday using alternate numeric symbols. */
698 get_alt_number (0, 6);
699 tm->tm_wday = val;
700 break;
701 case 'y':
702 /* Match year within century using alternate numeric symbols. */
703 get_alt_number (0, 99);
704 break;
705 default:
706 return NULL;
708 break;
709 default:
710 return NULL;
714 if (have_I && is_pm)
715 tm->tm_hour += 12;
717 return (char *) rp;
721 char *
722 strptime (buf, format, tm)
723 const char *buf;
724 const char *format;
725 struct tm *tm;
727 enum locale_status decided;
728 #ifdef _NL_CURRENT
729 decided = not;
730 #else
731 decided = raw;
732 #endif
733 return strptime_internal (buf, format, tm, &decided);