Update.
[glibc.git] / time / strptime.c
blob36f42c9355dc74609ac1f565ead729ca1f6fcac7
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 /* The "Year 2000 :The Millennium Rollover" paper suggests that
547 values in the range 69-99 refer to the twentieth century. */
548 tm->tm_year = val >= 69 ? val : val + 100;
549 break;
550 case 'Y':
551 /* Match year including century number. */
552 get_number (0, 9999);
553 tm->tm_year = val - 1900;
554 break;
555 case 'Z':
556 /* XXX How to handle this? */
557 break;
558 case 'E':
559 #ifdef _NL_CURRENT
560 switch (*fmt++)
562 case 'c':
563 /* Match locale's alternate date and time format. */
564 if (*decided != raw)
566 const char *fmt = _NL_CURRENT (LC_TIME, ERA_D_T_FMT);
568 if (*fmt == '\0')
569 fmt = _NL_CURRENT (LC_TIME, D_T_FMT);
571 if (!recursive (fmt))
573 if (*decided == loc)
574 return NULL;
576 else
578 if (strcmp (fmt, HERE_D_T_FMT))
579 *decided = loc;
580 break;
582 *decided = raw;
584 /* The C locale has no era information, so use the
585 normal representation. */
586 if (!recursive (HERE_D_T_FMT))
587 return NULL;
588 break;
589 case 'C':
590 case 'y':
591 case 'Y':
592 /* Match name of base year in locale's alternate
593 representation. */
594 /* XXX This is currently not implemented. It should
595 use the value _NL_CURRENT (LC_TIME, ERA). */
596 break;
597 case 'x':
598 if (*decided != raw)
600 const char *fmt = _NL_CURRENT (LC_TIME, ERA_D_FMT);
602 if (*fmt == '\0')
603 fmt = _NL_CURRENT (LC_TIME, D_FMT);
605 if (!recursive (fmt))
607 if (*decided == loc)
608 return NULL;
610 else
612 if (strcmp (fmt, HERE_D_FMT))
613 *decided = loc;
614 break;
616 *decided = raw;
618 if (!recursive (HERE_D_FMT))
619 return NULL;
620 break;
621 case 'X':
622 if (*decided != raw)
624 const char *fmt = _NL_CURRENT (LC_TIME, ERA_T_FMT);
626 if (*fmt == '\0')
627 fmt = _NL_CURRENT (LC_TIME, T_FMT);
629 if (!recursive (fmt))
631 if (*decided == loc)
632 return NULL;
634 else
636 if (strcmp (fmt, HERE_T_FMT))
637 *decided = loc;
638 break;
640 *decided = raw;
642 if (!recursive (HERE_T_FMT))
643 return NULL;
644 break;
645 default:
646 return NULL;
648 break;
649 #else
650 /* We have no information about the era format. Just use
651 the normal format. */
652 if (*fmt != 'c' && *fmt != 'C' && *fmt != 'y' && *fmt != 'Y'
653 && *fmt != 'x' && *fmt != 'X')
654 /* This is an illegal format. */
655 return NULL;
657 goto start_over;
658 #endif
659 case 'O':
660 switch (*fmt++)
662 case 'd':
663 case 'e':
664 /* Match day of month using alternate numeric symbols. */
665 get_alt_number (1, 31);
666 tm->tm_mday = val;
667 break;
668 case 'H':
669 /* Match hour in 24-hour clock using alternate numeric
670 symbols. */
671 get_alt_number (0, 23);
672 tm->tm_hour = val;
673 have_I = 0;
674 break;
675 case 'I':
676 /* Match hour in 12-hour clock using alternate numeric
677 symbols. */
678 get_alt_number (1, 12);
679 tm->tm_hour = val - 1;
680 have_I = 1;
681 break;
682 case 'm':
683 /* Match month using alternate numeric symbols. */
684 get_alt_number (1, 12);
685 tm->tm_mon = val - 1;
686 break;
687 case 'M':
688 /* Match minutes using alternate numeric symbols. */
689 get_alt_number (0, 59);
690 tm->tm_min = val;
691 break;
692 case 'S':
693 /* Match seconds using alternate numeric symbols. */
694 get_alt_number (0, 61);
695 tm->tm_sec = val;
696 break;
697 case 'U':
698 case 'V':
699 case 'W':
700 get_alt_number (0, 53);
701 /* XXX This cannot determine any field in TM without
702 further information. */
703 break;
704 case 'w':
705 /* Match number of weekday using alternate numeric symbols. */
706 get_alt_number (0, 6);
707 tm->tm_wday = val;
708 break;
709 case 'y':
710 /* Match year within century using alternate numeric symbols. */
711 get_alt_number (0, 99);
712 tm->tm_year = val >= 69 ? val : val + 100;
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);