Update.
[glibc.git] / time / strptime.c
blob4e91a719aca68a4dc8d249df02cbcecc9329b1df
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 #ifdef _LIBC
184 internal_function
185 #endif
186 strptime_internal __P ((const char *buf, const char *format, struct tm *tm,
187 enum locale_status *decided));
189 static char *
190 #ifdef _LIBC
191 internal_function
192 #endif
193 strptime_internal (buf, format, tm, decided)
194 const char *buf;
195 const char *format;
196 struct tm *tm;
197 enum locale_status *decided;
199 const char *rp;
200 const char *fmt;
201 int cnt;
202 size_t val;
203 int have_I, is_pm;
205 rp = buf;
206 fmt = format;
207 have_I = is_pm = 0;
209 while (*fmt != '\0')
211 /* A white space in the format string matches 0 more or white
212 space in the input string. */
213 if (isspace (*fmt))
215 while (isspace (*rp))
216 ++rp;
217 ++fmt;
218 continue;
221 /* Any character but `%' must be matched by the same character
222 in the iput string. */
223 if (*fmt != '%')
225 match_char (*fmt++, *rp++);
226 continue;
229 ++fmt;
230 #ifndef _NL_CURRENT
231 /* We need this for handling the `E' modifier. */
232 start_over:
233 #endif
234 switch (*fmt++)
236 case '%':
237 /* Match the `%' character itself. */
238 match_char ('%', *rp++);
239 break;
240 case 'a':
241 case 'A':
242 /* Match day of week. */
243 for (cnt = 0; cnt < 7; ++cnt)
245 #ifdef _NL_CURRENT
246 if (*decided !=raw)
248 if (match_string (_NL_CURRENT (LC_TIME, DAY_1 + cnt), rp))
250 if (*decided == not
251 && strcmp (_NL_CURRENT (LC_TIME, DAY_1 + cnt),
252 weekday_name[cnt]))
253 *decided = loc;
254 break;
256 if (match_string (_NL_CURRENT (LC_TIME, ABDAY_1 + cnt), rp))
258 if (*decided == not
259 && strcmp (_NL_CURRENT (LC_TIME, ABDAY_1 + cnt),
260 ab_weekday_name[cnt]))
261 *decided = loc;
262 break;
265 #endif
266 if (*decided != loc
267 && (match_string (weekday_name[cnt], rp)
268 || match_string (ab_weekday_name[cnt], rp)))
270 *decided = raw;
271 break;
274 if (cnt == 7)
275 /* Does not match a weekday name. */
276 return NULL;
277 tm->tm_wday = cnt;
278 break;
279 case 'b':
280 case 'B':
281 case 'h':
282 /* Match month name. */
283 for (cnt = 0; cnt < 12; ++cnt)
285 #ifdef _NL_CURRENT
286 if (*decided !=raw)
288 if (match_string (_NL_CURRENT (LC_TIME, MON_1 + cnt), rp))
290 if (*decided == not
291 && strcmp (_NL_CURRENT (LC_TIME, MON_1 + cnt),
292 month_name[cnt]))
293 *decided = loc;
294 break;
296 if (match_string (_NL_CURRENT (LC_TIME, ABMON_1 + cnt), rp))
298 if (*decided == not
299 && strcmp (_NL_CURRENT (LC_TIME, ABMON_1 + cnt),
300 ab_month_name[cnt]))
301 *decided = loc;
302 break;
305 #endif
306 if (match_string (month_name[cnt], rp)
307 || match_string (ab_month_name[cnt], rp))
309 *decided = raw;
310 break;
313 if (cnt == 12)
314 /* Does not match a month name. */
315 return NULL;
316 tm->tm_mon = cnt;
317 break;
318 case 'c':
319 /* Match locale's date and time format. */
320 #ifdef _NL_CURRENT
321 if (*decided != raw)
323 if (!recursive (_NL_CURRENT (LC_TIME, D_T_FMT)))
325 if (*decided == loc)
326 return NULL;
328 else
330 if (*decided == not &&
331 strcmp (_NL_CURRENT (LC_TIME, D_T_FMT), HERE_D_T_FMT))
332 *decided = loc;
333 break;
335 *decided = raw;
337 #endif
338 if (!recursive (HERE_D_T_FMT))
339 return NULL;
340 break;
341 case 'C':
342 /* Match century number. */
343 get_number (0, 99);
344 /* We don't need the number. */
345 break;
346 case 'd':
347 case 'e':
348 /* Match day of month. */
349 get_number (1, 31);
350 tm->tm_mday = val;
351 break;
352 case 'x':
353 #ifdef _NL_CURRENT
354 if (*decided != raw)
356 if (!recursive (_NL_CURRENT (LC_TIME, D_FMT)))
358 if (*decided == loc)
359 return NULL;
361 else
363 if (decided == not
364 && strcmp (_NL_CURRENT (LC_TIME, D_FMT), HERE_D_FMT))
365 *decided = loc;
366 break;
368 *decided = raw;
370 #endif
371 /* Fall through. */
372 case 'D':
373 /* Match standard day format. */
374 if (!recursive (HERE_D_FMT))
375 return NULL;
376 break;
377 case 'H':
378 /* Match hour in 24-hour clock. */
379 get_number (0, 23);
380 tm->tm_hour = val;
381 have_I = 0;
382 break;
383 case 'I':
384 /* Match hour in 12-hour clock. */
385 get_number (1, 12);
386 tm->tm_hour = val % 12;
387 have_I = 1;
388 break;
389 case 'j':
390 /* Match day number of year. */
391 get_number (1, 366);
392 tm->tm_yday = val - 1;
393 break;
394 case 'm':
395 /* Match number of month. */
396 get_number (1, 12);
397 tm->tm_mon = val - 1;
398 break;
399 case 'M':
400 /* Match minute. */
401 get_number (0, 59);
402 tm->tm_min = val;
403 break;
404 case 'n':
405 case 't':
406 /* Match any white space. */
407 while (isspace (*rp))
408 ++rp;
409 break;
410 case 'p':
411 /* Match locale's equivalent of AM/PM. */
412 #ifdef _NL_CURRENT
413 if (*decided != raw)
415 if (match_string (_NL_CURRENT (LC_TIME, AM_STR), rp))
417 if (strcmp (_NL_CURRENT (LC_TIME, AM_STR), HERE_AM_STR))
418 *decided = loc;
419 break;
421 if (match_string (_NL_CURRENT (LC_TIME, PM_STR), rp))
423 if (strcmp (_NL_CURRENT (LC_TIME, PM_STR), HERE_PM_STR))
424 *decided = loc;
425 is_pm = 1;
426 break;
428 *decided = raw;
430 #endif
431 if (!match_string (HERE_AM_STR, rp))
432 if (match_string (HERE_PM_STR, rp))
433 is_pm = 1;
434 else
435 return NULL;
436 break;
437 case 'r':
438 #ifdef _NL_CURRENT
439 if (*decided != raw)
441 if (!recursive (_NL_CURRENT (LC_TIME, T_FMT_AMPM)))
443 if (*decided == loc)
444 return NULL;
446 else
448 if (*decided == not &&
449 strcmp (_NL_CURRENT (LC_TIME, T_FMT_AMPM),
450 HERE_T_FMT_AMPM))
451 *decided = loc;
452 break;
454 *decided = raw;
456 #endif
457 if (!recursive (HERE_T_FMT_AMPM))
458 return NULL;
459 break;
460 case 'R':
461 if (!recursive ("%H:%M"))
462 return NULL;
463 break;
464 case 's':
466 /* The number of seconds may be very high so we cannot use
467 the `get_number' macro. Instead read the number
468 character for character and construct the result while
469 doing this. */
470 time_t secs;
471 if (*rp < '0' || *rp > '9')
472 /* We need at least one digit. */
473 return NULL;
477 secs *= 10;
478 secs += *rp++ - '0';
480 while (*rp >= '0' && *rp <= '9');
482 if (localtime_r (&secs, tm) == NULL)
483 /* Error in function. */
484 return NULL;
486 break;
487 case 'S':
488 get_number (0, 61);
489 tm->tm_sec = val;
490 break;
491 case 'X':
492 #ifdef _NL_CURRENT
493 if (*decided != raw)
495 if (!recursive (_NL_CURRENT (LC_TIME, T_FMT)))
497 if (*decided == loc)
498 return NULL;
500 else
502 if (strcmp (_NL_CURRENT (LC_TIME, T_FMT), HERE_T_FMT))
503 *decided = loc;
504 break;
506 *decided = raw;
508 #endif
509 /* Fall through. */
510 case 'T':
511 if (!recursive (HERE_T_FMT))
512 return NULL;
513 break;
514 case 'u':
515 get_number (1, 7);
516 tm->tm_wday = val % 7;
517 break;
518 case 'g':
519 get_number (0, 99);
520 /* XXX This cannot determine any field in TM. */
521 break;
522 case 'G':
523 if (*rp < '0' || *rp > '9')
524 return NULL;
525 /* XXX Ignore the number since we would need some more
526 information to compute a real date. */
528 ++rp;
529 while (*rp >= '0' && *rp <= '9');
530 break;
531 case 'U':
532 case 'V':
533 case 'W':
534 get_number (0, 53);
535 /* XXX This cannot determine any field in TM without some
536 information. */
537 break;
538 case 'w':
539 /* Match number of weekday. */
540 get_number (0, 6);
541 tm->tm_wday = val;
542 break;
543 case 'y':
544 /* Match year within century. */
545 get_number (0, 99);
546 tm->tm_year = val >= 50 ? val : val + 100;
547 break;
548 case 'Y':
549 /* Match year including century number. */
550 if (sizeof (time_t) > 4)
551 get_number (0, 9999);
552 else
553 get_number (0, 2036);
554 tm->tm_year = val - 1900;
555 break;
556 case 'Z':
557 /* XXX How to handle this? */
558 break;
559 case 'E':
560 #ifdef _NL_CURRENT
561 switch (*fmt++)
563 case 'c':
564 /* Match locale's alternate date and time format. */
565 if (*decided != raw)
567 const char *fmt = _NL_CURRENT (LC_TIME, ERA_D_T_FMT);
569 if (*fmt == '\0')
570 fmt = _NL_CURRENT (LC_TIME, D_T_FMT);
572 if (!recursive (fmt))
574 if (*decided == loc)
575 return NULL;
577 else
579 if (strcmp (fmt, HERE_D_T_FMT))
580 *decided = loc;
581 break;
583 *decided = raw;
585 /* The C locale has no era information, so use the
586 normal representation. */
587 if (!recursive (HERE_D_T_FMT))
588 return NULL;
589 break;
590 case 'C':
591 case 'y':
592 case 'Y':
593 /* Match name of base year in locale's alternate
594 representation. */
595 /* XXX This is currently not implemented. It should
596 use the value _NL_CURRENT (LC_TIME, ERA). */
597 break;
598 case 'x':
599 if (*decided != raw)
601 const char *fmt = _NL_CURRENT (LC_TIME, ERA_D_FMT);
603 if (*fmt == '\0')
604 fmt = _NL_CURRENT (LC_TIME, D_FMT);
606 if (!recursive (fmt))
608 if (*decided == loc)
609 return NULL;
611 else
613 if (strcmp (fmt, HERE_D_FMT))
614 *decided = loc;
615 break;
617 *decided = raw;
619 if (!recursive (HERE_D_FMT))
620 return NULL;
621 break;
622 case 'X':
623 if (*decided != raw)
625 const char *fmt = _NL_CURRENT (LC_TIME, ERA_T_FMT);
627 if (*fmt == '\0')
628 fmt = _NL_CURRENT (LC_TIME, T_FMT);
630 if (!recursive (fmt))
632 if (*decided == loc)
633 return NULL;
635 else
637 if (strcmp (fmt, HERE_T_FMT))
638 *decided = loc;
639 break;
641 *decided = raw;
643 if (!recursive (HERE_T_FMT))
644 return NULL;
645 break;
646 default:
647 return NULL;
649 break;
650 #else
651 /* We have no information about the era format. Just use
652 the normal format. */
653 if (*fmt != 'c' && *fmt != 'C' && *fmt != 'y' && *fmt != 'Y'
654 && *fmt != 'x' && *fmt != 'X')
655 /* This is an illegal format. */
656 return NULL;
658 goto start_over;
659 #endif
660 case 'O':
661 switch (*fmt++)
663 case 'd':
664 case 'e':
665 /* Match day of month using alternate numeric symbols. */
666 get_alt_number (1, 31);
667 tm->tm_mday = val;
668 break;
669 case 'H':
670 /* Match hour in 24-hour clock using alternate numeric
671 symbols. */
672 get_alt_number (0, 23);
673 tm->tm_hour = val;
674 have_I = 0;
675 break;
676 case 'I':
677 /* Match hour in 12-hour clock using alternate numeric
678 symbols. */
679 get_alt_number (1, 12);
680 tm->tm_hour = val - 1;
681 have_I = 1;
682 break;
683 case 'm':
684 /* Match month using alternate numeric symbols. */
685 get_alt_number (1, 12);
686 tm->tm_mon = val - 1;
687 break;
688 case 'M':
689 /* Match minutes using alternate numeric symbols. */
690 get_alt_number (0, 59);
691 tm->tm_min = val;
692 break;
693 case 'S':
694 /* Match seconds using alternate numeric symbols. */
695 get_alt_number (0, 61);
696 tm->tm_sec = val;
697 break;
698 case 'U':
699 case 'V':
700 case 'W':
701 get_alt_number (0, 53);
702 /* XXX This cannot determine any field in TM without
703 further information. */
704 break;
705 case 'w':
706 /* Match number of weekday using alternate numeric symbols. */
707 get_alt_number (0, 6);
708 tm->tm_wday = val;
709 break;
710 case 'y':
711 /* Match year within century using alternate numeric symbols. */
712 get_alt_number (0, 99);
713 break;
714 default:
715 return NULL;
717 break;
718 default:
719 return NULL;
723 if (have_I && is_pm)
724 tm->tm_hour += 12;
726 return (char *) rp;
730 char *
731 strptime (buf, format, tm)
732 const char *buf;
733 const char *format;
734 struct tm *tm;
736 enum locale_status decided;
737 #ifdef _NL_CURRENT
738 decided = not;
739 #else
740 decided = raw;
741 #endif
742 return strptime_internal (buf, format, tm, &decided);