2003-05-04 Roland McGrath <roland@redhat.com>
[glibc.git] / time / strptime.c
blob1edd4ac257ff06ad383029580f0e3266278841e5
1 /* Convert a string representation of time to a time value.
2 Copyright (C) 1996-2000, 2001, 2002 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 Lesser General Public
8 License as published by the Free Software Foundation; either
9 version 2.1 of the 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 Lesser General Public License for more details.
16 You should have received a copy of the GNU Lesser General Public
17 License along with the GNU C Library; if not, write to the Free
18 Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
19 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. */
50 #if ! HAVE_LOCALTIME_R && ! defined localtime_r
51 # ifdef _LIBC
52 # define localtime_r __localtime_r
53 # else
54 /* Approximate localtime_r as best we can in its absence. */
55 # define localtime_r my_localtime_r
56 static struct tm *localtime_r __P ((const time_t *, struct tm *));
57 static struct tm *
58 localtime_r (t, tp)
59 const time_t *t;
60 struct tm *tp;
62 struct tm *l = localtime (t);
63 if (! l)
64 return 0;
65 *tp = *l;
66 return tp;
68 # endif /* ! _LIBC */
69 #endif /* ! HAVE_LOCALTIME_R && ! defined (localtime_r) */
72 #define match_char(ch1, ch2) if (ch1 != ch2) return NULL
73 #if defined __GNUC__ && __GNUC__ >= 2
74 # ifdef USE_IN_EXTENDED_LOCALE_MODEL
75 # define match_string(cs1, s2) \
76 ({ size_t len = strlen (cs1); \
77 int result = __strncasecmp_l ((cs1), (s2), len, locale) == 0; \
78 if (result) (s2) += len; \
79 result; })
80 # else
81 # define match_string(cs1, s2) \
82 ({ size_t len = strlen (cs1); \
83 int result = strncasecmp ((cs1), (s2), len) == 0; \
84 if (result) (s2) += len; \
85 result; })
86 # endif
87 #else
88 /* Oh come on. Get a reasonable compiler. */
89 # define match_string(cs1, s2) \
90 (strncasecmp ((cs1), (s2), strlen (cs1)) ? 0 : ((s2) += strlen (cs1), 1))
91 #endif
92 /* We intentionally do not use isdigit() for testing because this will
93 lead to problems with the wide character version. */
94 #define get_number(from, to, n) \
95 do { \
96 int __n = n; \
97 val = 0; \
98 while (*rp == ' ') \
99 ++rp; \
100 if (*rp < '0' || *rp > '9') \
101 return NULL; \
102 do { \
103 val *= 10; \
104 val += *rp++ - '0'; \
105 } while (--__n > 0 && val * 10 <= to && *rp >= '0' && *rp <= '9'); \
106 if (val < from || val > to) \
107 return NULL; \
108 } while (0)
109 #ifdef _NL_CURRENT
110 # define get_alt_number(from, to, n) \
111 ({ \
112 __label__ do_normal; \
114 if (*decided != raw) \
116 val = _nl_parse_alt_digit (&rp HELPER_LOCALE_ARG); \
117 if (val == -1 && *decided != loc) \
119 *decided = loc; \
120 goto do_normal; \
122 if (val < from || val > to) \
123 return NULL; \
125 else \
127 do_normal: \
128 get_number (from, to, n); \
130 0; \
132 #else
133 # define get_alt_number(from, to, n) \
134 /* We don't have the alternate representation. */ \
135 get_number(from, to, n)
136 #endif
137 #define recursive(new_fmt) \
138 (*(new_fmt) != '\0' \
139 && (rp = strptime_internal (rp, (new_fmt), tm, \
140 decided, era_cnt LOCALE_ARG)) != NULL)
143 #ifdef _LIBC
144 /* This is defined in locale/C-time.c in the GNU libc. */
145 extern const struct locale_data _nl_C_LC_TIME attribute_hidden;
147 # define weekday_name (&_nl_C_LC_TIME.values[_NL_ITEM_INDEX (DAY_1)].string)
148 # define ab_weekday_name \
149 (&_nl_C_LC_TIME.values[_NL_ITEM_INDEX (ABDAY_1)].string)
150 # define month_name (&_nl_C_LC_TIME.values[_NL_ITEM_INDEX (MON_1)].string)
151 # define ab_month_name (&_nl_C_LC_TIME.values[_NL_ITEM_INDEX (ABMON_1)].string)
152 # define HERE_D_T_FMT (_nl_C_LC_TIME.values[_NL_ITEM_INDEX (D_T_FMT)].string)
153 # define HERE_D_FMT (_nl_C_LC_TIME.values[_NL_ITEM_INDEX (D_FMT)].string)
154 # define HERE_AM_STR (_nl_C_LC_TIME.values[_NL_ITEM_INDEX (AM_STR)].string)
155 # define HERE_PM_STR (_nl_C_LC_TIME.values[_NL_ITEM_INDEX (PM_STR)].string)
156 # define HERE_T_FMT_AMPM \
157 (_nl_C_LC_TIME.values[_NL_ITEM_INDEX (T_FMT_AMPM)].string)
158 # define HERE_T_FMT (_nl_C_LC_TIME.values[_NL_ITEM_INDEX (T_FMT)].string)
160 # define strncasecmp(s1, s2, n) __strncasecmp (s1, s2, n)
161 #else
162 static char const weekday_name[][10] =
164 "Sunday", "Monday", "Tuesday", "Wednesday",
165 "Thursday", "Friday", "Saturday"
167 static char const ab_weekday_name[][4] =
169 "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
171 static char const month_name[][10] =
173 "January", "February", "March", "April", "May", "June",
174 "July", "August", "September", "October", "November", "December"
176 static char const ab_month_name[][4] =
178 "Jan", "Feb", "Mar", "Apr", "May", "Jun",
179 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
181 # define HERE_D_T_FMT "%a %b %e %H:%M:%S %Y"
182 # define HERE_D_FMT "%m/%d/%y"
183 # define HERE_AM_STR "AM"
184 # define HERE_PM_STR "PM"
185 # define HERE_T_FMT_AMPM "%I:%M:%S %p"
186 # define HERE_T_FMT "%H:%M:%S"
188 static const unsigned short int __mon_yday[2][13] =
190 /* Normal years. */
191 { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 },
192 /* Leap years. */
193 { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 }
195 #endif
197 #if defined _LIBC && defined USE_IN_EXTENDED_LOCALE_MODEL
198 /* We use this code also for the extended locale handling where the
199 function gets as an additional argument the locale which has to be
200 used. To access the values we have to redefine the _NL_CURRENT
201 macro. */
202 # define strptime __strptime_l
203 # undef _NL_CURRENT
204 # define _NL_CURRENT(category, item) \
205 (current->values[_NL_ITEM_INDEX (item)].string)
206 # undef _NL_CURRENT_WORD
207 # define _NL_CURRENT_WORD(category, item) \
208 (current->values[_NL_ITEM_INDEX (item)].word)
209 # define LOCALE_PARAM , locale
210 # define LOCALE_ARG , locale
211 # define LOCALE_PARAM_PROTO , __locale_t locale
212 # define LOCALE_PARAM_DECL __locale_t locale;
213 # define HELPER_LOCALE_ARG , current
214 # define ISSPACE(Ch) __isspace_l (Ch, locale)
215 #else
216 # define LOCALE_PARAM
217 # define LOCALE_ARG
218 # define LOCALE_PARAM_DECL
219 # define LOCALE_PARAM_PROTO
220 # ifdef _LIBC
221 # define HELPER_LOCALE_ARG , _NL_CURRENT_DATA (LC_TIME)
222 # else
223 # define HELPER_LOCALE_ARG
224 # endif
225 # define ISSPACE(Ch) isspace (Ch)
226 #endif
229 /* Status of lookup: do we use the locale data or the raw data? */
230 enum locale_status { not, loc, raw };
233 #ifndef __isleap
234 /* Nonzero if YEAR is a leap year (every 4 years,
235 except every 100th isn't, and every 400th is). */
236 # define __isleap(year) \
237 ((year) % 4 == 0 && ((year) % 100 != 0 || (year) % 400 == 0))
238 #endif
240 /* Compute the day of the week. */
241 static void
242 day_of_the_week (struct tm *tm)
244 /* We know that January 1st 1970 was a Thursday (= 4). Compute the
245 the difference between this data in the one on TM and so determine
246 the weekday. */
247 int corr_year = 1900 + tm->tm_year - (tm->tm_mon < 2);
248 int wday = (-473
249 + (365 * (tm->tm_year - 70))
250 + (corr_year / 4)
251 - ((corr_year / 4) / 25) + ((corr_year / 4) % 25 < 0)
252 + (((corr_year / 4) / 25) / 4)
253 + __mon_yday[0][tm->tm_mon]
254 + tm->tm_mday - 1);
255 tm->tm_wday = ((wday % 7) + 7) % 7;
258 /* Compute the day of the year. */
259 static void
260 day_of_the_year (struct tm *tm)
262 tm->tm_yday = (__mon_yday[__isleap (1900 + tm->tm_year)][tm->tm_mon]
263 + (tm->tm_mday - 1));
267 static char *
268 #ifdef _LIBC
269 internal_function
270 #endif
271 strptime_internal __P ((const char *rp, const char *fmt, struct tm *tm,
272 enum locale_status *decided, int era_cnt
273 LOCALE_PARAM_PROTO));
275 static char *
276 #ifdef _LIBC
277 internal_function
278 #endif
279 strptime_internal (rp, fmt, tm, decided, era_cnt LOCALE_PARAM)
280 const char *rp;
281 const char *fmt;
282 struct tm *tm;
283 enum locale_status *decided;
284 int era_cnt;
285 LOCALE_PARAM_DECL
287 #if defined _LIBC && defined USE_IN_EXTENDED_LOCALE_MODEL
288 struct locale_data *const current = locale->__locales[LC_TIME];
289 #endif
291 const char *rp_backup;
292 int cnt;
293 size_t val;
294 int have_I, is_pm;
295 int century, want_century;
296 int want_era;
297 int have_wday, want_xday;
298 int have_yday;
299 int have_mon, have_mday;
300 int have_uweek, have_wweek;
301 int week_no;
302 size_t num_eras;
303 struct era_entry *era;
305 have_I = is_pm = 0;
306 century = -1;
307 want_century = 0;
308 want_era = 0;
309 era = NULL;
310 week_no = 0;
312 have_wday = want_xday = have_yday = have_mon = have_mday = have_uweek = 0;
313 have_wweek = 0;
315 while (*fmt != '\0')
317 /* A white space in the format string matches 0 more or white
318 space in the input string. */
319 if (ISSPACE (*fmt))
321 while (ISSPACE (*rp))
322 ++rp;
323 ++fmt;
324 continue;
327 /* Any character but `%' must be matched by the same character
328 in the iput string. */
329 if (*fmt != '%')
331 match_char (*fmt++, *rp++);
332 continue;
335 ++fmt;
336 #ifndef _NL_CURRENT
337 /* We need this for handling the `E' modifier. */
338 start_over:
339 #endif
341 /* Make back up of current processing pointer. */
342 rp_backup = rp;
344 switch (*fmt++)
346 case '%':
347 /* Match the `%' character itself. */
348 match_char ('%', *rp++);
349 break;
350 case 'a':
351 case 'A':
352 /* Match day of week. */
353 for (cnt = 0; cnt < 7; ++cnt)
355 #ifdef _NL_CURRENT
356 if (*decided !=raw)
358 if (match_string (_NL_CURRENT (LC_TIME, DAY_1 + cnt), rp))
360 if (*decided == not
361 && strcmp (_NL_CURRENT (LC_TIME, DAY_1 + cnt),
362 weekday_name[cnt]))
363 *decided = loc;
364 break;
366 if (match_string (_NL_CURRENT (LC_TIME, ABDAY_1 + cnt), rp))
368 if (*decided == not
369 && strcmp (_NL_CURRENT (LC_TIME, ABDAY_1 + cnt),
370 ab_weekday_name[cnt]))
371 *decided = loc;
372 break;
375 #endif
376 if (*decided != loc
377 && (match_string (weekday_name[cnt], rp)
378 || match_string (ab_weekday_name[cnt], rp)))
380 *decided = raw;
381 break;
384 if (cnt == 7)
385 /* Does not match a weekday name. */
386 return NULL;
387 tm->tm_wday = cnt;
388 have_wday = 1;
389 break;
390 case 'b':
391 case 'B':
392 case 'h':
393 /* Match month name. */
394 for (cnt = 0; cnt < 12; ++cnt)
396 #ifdef _NL_CURRENT
397 if (*decided !=raw)
399 if (match_string (_NL_CURRENT (LC_TIME, MON_1 + cnt), rp))
401 if (*decided == not
402 && strcmp (_NL_CURRENT (LC_TIME, MON_1 + cnt),
403 month_name[cnt]))
404 *decided = loc;
405 break;
407 if (match_string (_NL_CURRENT (LC_TIME, ABMON_1 + cnt), rp))
409 if (*decided == not
410 && strcmp (_NL_CURRENT (LC_TIME, ABMON_1 + cnt),
411 ab_month_name[cnt]))
412 *decided = loc;
413 break;
416 #endif
417 if (match_string (month_name[cnt], rp)
418 || match_string (ab_month_name[cnt], rp))
420 *decided = raw;
421 break;
424 if (cnt == 12)
425 /* Does not match a month name. */
426 return NULL;
427 tm->tm_mon = cnt;
428 want_xday = 1;
429 break;
430 case 'c':
431 /* Match locale's date and time format. */
432 #ifdef _NL_CURRENT
433 if (*decided != raw)
435 if (!recursive (_NL_CURRENT (LC_TIME, D_T_FMT)))
437 if (*decided == loc)
438 return NULL;
439 else
440 rp = rp_backup;
442 else
444 if (*decided == not &&
445 strcmp (_NL_CURRENT (LC_TIME, D_T_FMT), HERE_D_T_FMT))
446 *decided = loc;
447 want_xday = 1;
448 break;
450 *decided = raw;
452 #endif
453 if (!recursive (HERE_D_T_FMT))
454 return NULL;
455 want_xday = 1;
456 break;
457 case 'C':
458 /* Match century number. */
459 match_century:
460 get_number (0, 99, 2);
461 century = val;
462 want_xday = 1;
463 break;
464 case 'd':
465 case 'e':
466 /* Match day of month. */
467 get_number (1, 31, 2);
468 tm->tm_mday = val;
469 have_mday = 1;
470 want_xday = 1;
471 break;
472 case 'F':
473 if (!recursive ("%Y-%m-%d"))
474 return NULL;
475 want_xday = 1;
476 break;
477 case 'x':
478 #ifdef _NL_CURRENT
479 if (*decided != raw)
481 if (!recursive (_NL_CURRENT (LC_TIME, D_FMT)))
483 if (*decided == loc)
484 return NULL;
485 else
486 rp = rp_backup;
488 else
490 if (*decided == not
491 && strcmp (_NL_CURRENT (LC_TIME, D_FMT), HERE_D_FMT))
492 *decided = loc;
493 want_xday = 1;
494 break;
496 *decided = raw;
498 #endif
499 /* Fall through. */
500 case 'D':
501 /* Match standard day format. */
502 if (!recursive (HERE_D_FMT))
503 return NULL;
504 want_xday = 1;
505 break;
506 case 'k':
507 case 'H':
508 /* Match hour in 24-hour clock. */
509 get_number (0, 23, 2);
510 tm->tm_hour = val;
511 have_I = 0;
512 break;
513 case 'l':
514 /* Match hour in 12-hour clock. GNU extension. */
515 case 'I':
516 /* Match hour in 12-hour clock. */
517 get_number (1, 12, 2);
518 tm->tm_hour = val % 12;
519 have_I = 1;
520 break;
521 case 'j':
522 /* Match day number of year. */
523 get_number (1, 366, 3);
524 tm->tm_yday = val - 1;
525 have_yday = 1;
526 break;
527 case 'm':
528 /* Match number of month. */
529 get_number (1, 12, 2);
530 tm->tm_mon = val - 1;
531 have_mon = 1;
532 want_xday = 1;
533 break;
534 case 'M':
535 /* Match minute. */
536 get_number (0, 59, 2);
537 tm->tm_min = val;
538 break;
539 case 'n':
540 case 't':
541 /* Match any white space. */
542 while (ISSPACE (*rp))
543 ++rp;
544 break;
545 case 'p':
546 /* Match locale's equivalent of AM/PM. */
547 #ifdef _NL_CURRENT
548 if (*decided != raw)
550 if (match_string (_NL_CURRENT (LC_TIME, AM_STR), rp))
552 if (strcmp (_NL_CURRENT (LC_TIME, AM_STR), HERE_AM_STR))
553 *decided = loc;
554 break;
556 if (match_string (_NL_CURRENT (LC_TIME, PM_STR), rp))
558 if (strcmp (_NL_CURRENT (LC_TIME, PM_STR), HERE_PM_STR))
559 *decided = loc;
560 is_pm = 1;
561 break;
563 *decided = raw;
565 #endif
566 if (!match_string (HERE_AM_STR, rp))
567 if (match_string (HERE_PM_STR, rp))
568 is_pm = 1;
569 else
570 return NULL;
571 break;
572 case 'r':
573 #ifdef _NL_CURRENT
574 if (*decided != raw)
576 if (!recursive (_NL_CURRENT (LC_TIME, T_FMT_AMPM)))
578 if (*decided == loc)
579 return NULL;
580 else
581 rp = rp_backup;
583 else
585 if (*decided == not &&
586 strcmp (_NL_CURRENT (LC_TIME, T_FMT_AMPM),
587 HERE_T_FMT_AMPM))
588 *decided = loc;
589 break;
591 *decided = raw;
593 #endif
594 if (!recursive (HERE_T_FMT_AMPM))
595 return NULL;
596 break;
597 case 'R':
598 if (!recursive ("%H:%M"))
599 return NULL;
600 break;
601 case 's':
603 /* The number of seconds may be very high so we cannot use
604 the `get_number' macro. Instead read the number
605 character for character and construct the result while
606 doing this. */
607 time_t secs = 0;
608 if (*rp < '0' || *rp > '9')
609 /* We need at least one digit. */
610 return NULL;
614 secs *= 10;
615 secs += *rp++ - '0';
617 while (*rp >= '0' && *rp <= '9');
619 if (localtime_r (&secs, tm) == NULL)
620 /* Error in function. */
621 return NULL;
623 break;
624 case 'S':
625 get_number (0, 61, 2);
626 tm->tm_sec = val;
627 break;
628 case 'X':
629 #ifdef _NL_CURRENT
630 if (*decided != raw)
632 if (!recursive (_NL_CURRENT (LC_TIME, T_FMT)))
634 if (*decided == loc)
635 return NULL;
636 else
637 rp = rp_backup;
639 else
641 if (strcmp (_NL_CURRENT (LC_TIME, T_FMT), HERE_T_FMT))
642 *decided = loc;
643 break;
645 *decided = raw;
647 #endif
648 /* Fall through. */
649 case 'T':
650 if (!recursive (HERE_T_FMT))
651 return NULL;
652 break;
653 case 'u':
654 get_number (1, 7, 1);
655 tm->tm_wday = val % 7;
656 have_wday = 1;
657 break;
658 case 'g':
659 get_number (0, 99, 2);
660 /* XXX This cannot determine any field in TM. */
661 break;
662 case 'G':
663 if (*rp < '0' || *rp > '9')
664 return NULL;
665 /* XXX Ignore the number since we would need some more
666 information to compute a real date. */
668 ++rp;
669 while (*rp >= '0' && *rp <= '9');
670 break;
671 case 'U':
672 get_number (0, 53, 2);
673 week_no = val;
674 have_uweek = 1;
675 break;
676 case 'W':
677 get_number (0, 53, 2);
678 week_no = val;
679 have_wweek = 1;
680 break;
681 case 'V':
682 get_number (0, 53, 2);
683 /* XXX This cannot determine any field in TM without some
684 information. */
685 break;
686 case 'w':
687 /* Match number of weekday. */
688 get_number (0, 6, 1);
689 tm->tm_wday = val;
690 have_wday = 1;
691 break;
692 case 'y':
693 match_year_in_century:
694 /* Match year within century. */
695 get_number (0, 99, 2);
696 /* The "Year 2000: The Millennium Rollover" paper suggests that
697 values in the range 69-99 refer to the twentieth century. */
698 tm->tm_year = val >= 69 ? val : val + 100;
699 /* Indicate that we want to use the century, if specified. */
700 want_century = 1;
701 want_xday = 1;
702 break;
703 case 'Y':
704 /* Match year including century number. */
705 get_number (0, 9999, 4);
706 tm->tm_year = val - 1900;
707 want_century = 0;
708 want_xday = 1;
709 break;
710 case 'Z':
711 /* XXX How to handle this? */
712 break;
713 case 'E':
714 #ifdef _NL_CURRENT
715 switch (*fmt++)
717 case 'c':
718 /* Match locale's alternate date and time format. */
719 if (*decided != raw)
721 const char *fmt = _NL_CURRENT (LC_TIME, ERA_D_T_FMT);
723 if (*fmt == '\0')
724 fmt = _NL_CURRENT (LC_TIME, D_T_FMT);
726 if (!recursive (fmt))
728 if (*decided == loc)
729 return NULL;
730 else
731 rp = rp_backup;
733 else
735 if (strcmp (fmt, HERE_D_T_FMT))
736 *decided = loc;
737 want_xday = 1;
738 break;
740 *decided = raw;
742 /* The C locale has no era information, so use the
743 normal representation. */
744 if (!recursive (HERE_D_T_FMT))
745 return NULL;
746 want_xday = 1;
747 break;
748 case 'C':
749 if (*decided != raw)
751 if (era_cnt >= 0)
753 era = _nl_select_era_entry (era_cnt HELPER_LOCALE_ARG);
754 if (era != NULL && match_string (era->era_name, rp))
756 *decided = loc;
757 break;
759 else
760 return NULL;
762 else
764 num_eras = _NL_CURRENT_WORD (LC_TIME,
765 _NL_TIME_ERA_NUM_ENTRIES);
766 for (era_cnt = 0; era_cnt < (int) num_eras;
767 ++era_cnt, rp = rp_backup)
769 era = _nl_select_era_entry (era_cnt
770 HELPER_LOCALE_ARG);
771 if (era != NULL && match_string (era->era_name, rp))
773 *decided = loc;
774 break;
777 if (era_cnt == (int) num_eras)
779 era_cnt = -1;
780 if (*decided == loc)
781 return NULL;
783 else
784 break;
787 *decided = raw;
789 /* The C locale has no era information, so use the
790 normal representation. */
791 goto match_century;
792 case 'y':
793 if (*decided == raw)
794 goto match_year_in_century;
796 get_number(0, 9999, 4);
797 tm->tm_year = val;
798 want_era = 1;
799 want_xday = 1;
800 want_century = 1;
801 break;
802 case 'Y':
803 if (*decided != raw)
805 num_eras = _NL_CURRENT_WORD (LC_TIME,
806 _NL_TIME_ERA_NUM_ENTRIES);
807 for (era_cnt = 0; era_cnt < (int) num_eras;
808 ++era_cnt, rp = rp_backup)
810 era = _nl_select_era_entry (era_cnt HELPER_LOCALE_ARG);
811 if (era != NULL && recursive (era->era_format))
812 break;
814 if (era_cnt == (int) num_eras)
816 era_cnt = -1;
817 if (*decided == loc)
818 return NULL;
819 else
820 rp = rp_backup;
822 else
824 *decided = loc;
825 era_cnt = -1;
826 break;
829 *decided = raw;
831 get_number (0, 9999, 4);
832 tm->tm_year = val - 1900;
833 want_century = 0;
834 want_xday = 1;
835 break;
836 case 'x':
837 if (*decided != raw)
839 const char *fmt = _NL_CURRENT (LC_TIME, ERA_D_FMT);
841 if (*fmt == '\0')
842 fmt = _NL_CURRENT (LC_TIME, D_FMT);
844 if (!recursive (fmt))
846 if (*decided == loc)
847 return NULL;
848 else
849 rp = rp_backup;
851 else
853 if (strcmp (fmt, HERE_D_FMT))
854 *decided = loc;
855 break;
857 *decided = raw;
859 if (!recursive (HERE_D_FMT))
860 return NULL;
861 break;
862 case 'X':
863 if (*decided != raw)
865 const char *fmt = _NL_CURRENT (LC_TIME, ERA_T_FMT);
867 if (*fmt == '\0')
868 fmt = _NL_CURRENT (LC_TIME, T_FMT);
870 if (!recursive (fmt))
872 if (*decided == loc)
873 return NULL;
874 else
875 rp = rp_backup;
877 else
879 if (strcmp (fmt, HERE_T_FMT))
880 *decided = loc;
881 break;
883 *decided = raw;
885 if (!recursive (HERE_T_FMT))
886 return NULL;
887 break;
888 default:
889 return NULL;
891 break;
892 #else
893 /* We have no information about the era format. Just use
894 the normal format. */
895 if (*fmt != 'c' && *fmt != 'C' && *fmt != 'y' && *fmt != 'Y'
896 && *fmt != 'x' && *fmt != 'X')
897 /* This is an illegal format. */
898 return NULL;
900 goto start_over;
901 #endif
902 case 'O':
903 switch (*fmt++)
905 case 'd':
906 case 'e':
907 /* Match day of month using alternate numeric symbols. */
908 get_alt_number (1, 31, 2);
909 tm->tm_mday = val;
910 have_mday = 1;
911 want_xday = 1;
912 break;
913 case 'H':
914 /* Match hour in 24-hour clock using alternate numeric
915 symbols. */
916 get_alt_number (0, 23, 2);
917 tm->tm_hour = val;
918 have_I = 0;
919 break;
920 case 'I':
921 /* Match hour in 12-hour clock using alternate numeric
922 symbols. */
923 get_alt_number (1, 12, 2);
924 tm->tm_hour = val % 12;
925 have_I = 1;
926 break;
927 case 'm':
928 /* Match month using alternate numeric symbols. */
929 get_alt_number (1, 12, 2);
930 tm->tm_mon = val - 1;
931 have_mon = 1;
932 want_xday = 1;
933 break;
934 case 'M':
935 /* Match minutes using alternate numeric symbols. */
936 get_alt_number (0, 59, 2);
937 tm->tm_min = val;
938 break;
939 case 'S':
940 /* Match seconds using alternate numeric symbols. */
941 get_alt_number (0, 61, 2);
942 tm->tm_sec = val;
943 break;
944 case 'U':
945 get_alt_number (0, 53, 2);
946 week_no = val;
947 have_uweek = 1;
948 break;
949 case 'W':
950 get_alt_number (0, 53, 2);
951 week_no = val;
952 have_wweek = 1;
953 break;
954 case 'V':
955 get_alt_number (0, 53, 2);
956 /* XXX This cannot determine any field in TM without
957 further information. */
958 break;
959 case 'w':
960 /* Match number of weekday using alternate numeric symbols. */
961 get_alt_number (0, 6, 1);
962 tm->tm_wday = val;
963 have_wday = 1;
964 break;
965 case 'y':
966 /* Match year within century using alternate numeric symbols. */
967 get_alt_number (0, 99, 2);
968 tm->tm_year = val >= 69 ? val : val + 100;
969 want_xday = 1;
970 break;
971 default:
972 return NULL;
974 break;
975 default:
976 return NULL;
980 if (have_I && is_pm)
981 tm->tm_hour += 12;
983 if (century != -1)
985 if (want_century)
986 tm->tm_year = tm->tm_year % 100 + (century - 19) * 100;
987 else
988 /* Only the century, but not the year. Strange, but so be it. */
989 tm->tm_year = (century - 19) * 100;
992 if (era_cnt != -1)
994 era = _nl_select_era_entry (era_cnt HELPER_LOCALE_ARG);
995 if (era == NULL)
996 return NULL;
997 if (want_era)
998 tm->tm_year = (era->start_date[0]
999 + ((tm->tm_year - era->offset)
1000 * era->absolute_direction));
1001 else
1002 /* Era start year assumed. */
1003 tm->tm_year = era->start_date[0];
1005 else
1006 if (want_era)
1008 /* No era found but we have seen an E modifier. Rectify some
1009 values. */
1010 if (want_century && century == -1 && tm->tm_year < 69)
1011 tm->tm_year += 100;
1014 if (want_xday && !have_wday)
1016 if ( !(have_mon && have_mday) && have_yday)
1018 /* We don't have tm_mon and/or tm_mday, compute them. */
1019 int t_mon = 0;
1020 while (__mon_yday[__isleap(1900 + tm->tm_year)][t_mon] <= tm->tm_yday)
1021 t_mon++;
1022 if (!have_mon)
1023 tm->tm_mon = t_mon - 1;
1024 if (!have_mday)
1025 tm->tm_mday =
1026 (tm->tm_yday
1027 - __mon_yday[__isleap(1900 + tm->tm_year)][t_mon - 1] + 1);
1029 day_of_the_week (tm);
1032 if (want_xday && !have_yday)
1033 day_of_the_year (tm);
1035 if ((have_uweek || have_wweek) && have_wday)
1037 int save_wday = tm->tm_wday;
1038 int save_mday = tm->tm_mday;
1039 int save_mon = tm->tm_mon;
1040 int w_offset = have_uweek ? 0 : 1;
1042 tm->tm_mday = 1;
1043 tm->tm_mon = 0;
1044 day_of_the_week (tm);
1045 if (have_mday)
1046 tm->tm_mday = save_mday;
1047 if (have_mon)
1048 tm->tm_mon = save_mon;
1050 if (!have_yday)
1051 tm->tm_yday = ((7 - (tm->tm_wday - w_offset)) % 7
1052 + (week_no - 1) *7
1053 + save_wday - w_offset);
1055 if (!have_mday || !have_mon)
1057 int t_mon = 0;
1058 while (__mon_yday[__isleap(1900 + tm->tm_year)][t_mon]
1059 <= tm->tm_yday)
1060 t_mon++;
1061 if (!have_mon)
1062 tm->tm_mon = t_mon - 1;
1063 if (!have_mday)
1064 tm->tm_mday =
1065 (tm->tm_yday
1066 - __mon_yday[__isleap(1900 + tm->tm_year)][t_mon - 1] + 1);
1069 tm->tm_wday = save_wday;
1072 return (char *) rp;
1076 char *
1077 strptime (buf, format, tm LOCALE_PARAM)
1078 const char *buf;
1079 const char *format;
1080 struct tm *tm;
1081 LOCALE_PARAM_DECL
1083 enum locale_status decided;
1085 #ifdef _NL_CURRENT
1086 decided = not;
1087 #else
1088 decided = raw;
1089 #endif
1090 return strptime_internal (buf, format, tm, &decided, -1 LOCALE_ARG);
1092 #if defined _LIBC && !defined USE_IN_EXTENDED_LOCALE_MODEL
1093 libc_hidden_def (strptime)
1094 #endif