2005-03-15 Jakub Jelinek <jakub@redhat.com>
[glibc/history.git] / time / strptime_l.c
blobccb838a229f1d050e3008d15b3ad309ffe188dce
1 /* Copyright (C) 2002, 2004 Free Software Foundation, Inc.
2 This file is part of the GNU C Library.
4 The GNU C Library is free software; you can redistribute it and/or
5 modify it under the terms of the GNU Lesser General Public
6 License as published by the Free Software Foundation; either
7 version 2.1 of the License, or (at your option) any later version.
9 The GNU C Library is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 Lesser General Public License for more details.
14 You should have received a copy of the GNU Lesser General Public
15 License along with the GNU C Library; if not, write to the Free
16 Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
17 02111-1307 USA. */
19 #ifdef HAVE_CONFIG_H
20 # include <config.h>
21 #endif
23 #include <assert.h>
24 #include <ctype.h>
25 #include <langinfo.h>
26 #include <limits.h>
27 #include <string.h>
28 #include <time.h>
29 #include <stdbool.h>
31 #ifdef _LIBC
32 # include "../locale/localeinfo.h"
33 #endif
36 #ifndef __P
37 # if defined __GNUC__ || (defined __STDC__ && __STDC__)
38 # define __P(args) args
39 # else
40 # define __P(args) ()
41 # endif /* GCC. */
42 #endif /* Not __P. */
45 #if ! HAVE_LOCALTIME_R && ! defined localtime_r
46 # ifdef _LIBC
47 # define localtime_r __localtime_r
48 # else
49 /* Approximate localtime_r as best we can in its absence. */
50 # define localtime_r my_localtime_r
51 static struct tm *localtime_r __P ((const time_t *, struct tm *));
52 static struct tm *
53 localtime_r (t, tp)
54 const time_t *t;
55 struct tm *tp;
57 struct tm *l = localtime (t);
58 if (! l)
59 return 0;
60 *tp = *l;
61 return tp;
63 # endif /* ! _LIBC */
64 #endif /* ! HAVE_LOCALTIME_R && ! defined (localtime_r) */
67 #define match_char(ch1, ch2) if (ch1 != ch2) return NULL
68 #if defined __GNUC__ && __GNUC__ >= 2
69 # define match_string(cs1, s2) \
70 ({ size_t len = strlen (cs1); \
71 int result = __strncasecmp_l ((cs1), (s2), len, locale) == 0; \
72 if (result) (s2) += len; \
73 result; })
74 #else
75 /* Oh come on. Get a reasonable compiler. */
76 # define match_string(cs1, s2) \
77 (strncasecmp ((cs1), (s2), strlen (cs1)) ? 0 : ((s2) += strlen (cs1), 1))
78 #endif
79 /* We intentionally do not use isdigit() for testing because this will
80 lead to problems with the wide character version. */
81 #define get_number(from, to, n) \
82 do { \
83 int __n = n; \
84 val = 0; \
85 while (*rp == ' ') \
86 ++rp; \
87 if (*rp < '0' || *rp > '9') \
88 return NULL; \
89 do { \
90 val *= 10; \
91 val += *rp++ - '0'; \
92 } while (--__n > 0 && val * 10 <= to && *rp >= '0' && *rp <= '9'); \
93 if (val < from || val > to) \
94 return NULL; \
95 } while (0)
96 #ifdef _NL_CURRENT
97 # define get_alt_number(from, to, n) \
98 ({ \
99 __label__ do_normal; \
101 if (*decided != raw) \
103 val = _nl_parse_alt_digit (&rp HELPER_LOCALE_ARG); \
104 if (val == -1 && *decided != loc) \
106 *decided = loc; \
107 goto do_normal; \
109 if (val < from || val > to) \
110 return NULL; \
112 else \
114 do_normal: \
115 get_number (from, to, n); \
117 0; \
119 #else
120 # define get_alt_number(from, to, n) \
121 /* We don't have the alternate representation. */ \
122 get_number(from, to, n)
123 #endif
124 #define recursive(new_fmt) \
125 (*(new_fmt) != '\0' \
126 && (rp = __strptime_internal (rp, (new_fmt), tm, \
127 decided, era_cnt LOCALE_ARG)) != NULL)
130 #ifdef _LIBC
131 /* This is defined in locale/C-time.c in the GNU libc. */
132 extern const struct locale_data _nl_C_LC_TIME attribute_hidden;
134 # define weekday_name (&_nl_C_LC_TIME.values[_NL_ITEM_INDEX (DAY_1)].string)
135 # define ab_weekday_name \
136 (&_nl_C_LC_TIME.values[_NL_ITEM_INDEX (ABDAY_1)].string)
137 # define month_name (&_nl_C_LC_TIME.values[_NL_ITEM_INDEX (MON_1)].string)
138 # define ab_month_name (&_nl_C_LC_TIME.values[_NL_ITEM_INDEX (ABMON_1)].string)
139 # define HERE_D_T_FMT (_nl_C_LC_TIME.values[_NL_ITEM_INDEX (D_T_FMT)].string)
140 # define HERE_D_FMT (_nl_C_LC_TIME.values[_NL_ITEM_INDEX (D_FMT)].string)
141 # define HERE_AM_STR (_nl_C_LC_TIME.values[_NL_ITEM_INDEX (AM_STR)].string)
142 # define HERE_PM_STR (_nl_C_LC_TIME.values[_NL_ITEM_INDEX (PM_STR)].string)
143 # define HERE_T_FMT_AMPM \
144 (_nl_C_LC_TIME.values[_NL_ITEM_INDEX (T_FMT_AMPM)].string)
145 # define HERE_T_FMT (_nl_C_LC_TIME.values[_NL_ITEM_INDEX (T_FMT)].string)
147 # define strncasecmp(s1, s2, n) __strncasecmp (s1, s2, n)
148 #else
149 static char const weekday_name[][10] =
151 "Sunday", "Monday", "Tuesday", "Wednesday",
152 "Thursday", "Friday", "Saturday"
154 static char const ab_weekday_name[][4] =
156 "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
158 static char const month_name[][10] =
160 "January", "February", "March", "April", "May", "June",
161 "July", "August", "September", "October", "November", "December"
163 static char const ab_month_name[][4] =
165 "Jan", "Feb", "Mar", "Apr", "May", "Jun",
166 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
168 # define HERE_D_T_FMT "%a %b %e %H:%M:%S %Y"
169 # define HERE_D_FMT "%m/%d/%y"
170 # define HERE_AM_STR "AM"
171 # define HERE_PM_STR "PM"
172 # define HERE_T_FMT_AMPM "%I:%M:%S %p"
173 # define HERE_T_FMT "%H:%M:%S"
175 static const unsigned short int __mon_yday[2][13] =
177 /* Normal years. */
178 { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 },
179 /* Leap years. */
180 { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 }
182 #endif
184 #if defined _LIBC
185 /* We use this code also for the extended locale handling where the
186 function gets as an additional argument the locale which has to be
187 used. To access the values we have to redefine the _NL_CURRENT
188 macro. */
189 # define strptime __strptime_l
190 # undef _NL_CURRENT
191 # define _NL_CURRENT(category, item) \
192 (current->values[_NL_ITEM_INDEX (item)].string)
193 # undef _NL_CURRENT_WORD
194 # define _NL_CURRENT_WORD(category, item) \
195 (current->values[_NL_ITEM_INDEX (item)].word)
196 # define LOCALE_PARAM , locale
197 # define LOCALE_ARG , locale
198 # define LOCALE_PARAM_PROTO , __locale_t locale
199 # define LOCALE_PARAM_DECL __locale_t locale;
200 # define HELPER_LOCALE_ARG , current
201 # define ISSPACE(Ch) __isspace_l (Ch, locale)
202 #else
203 # define LOCALE_PARAM
204 # define LOCALE_ARG
205 # define LOCALE_PARAM_DECL
206 # define LOCALE_PARAM_PROTO
207 # define HELPER_LOCALE_ARG
208 # define ISSPACE(Ch) isspace (Ch)
209 #endif
214 #ifndef __isleap
215 /* Nonzero if YEAR is a leap year (every 4 years,
216 except every 100th isn't, and every 400th is). */
217 # define __isleap(year) \
218 ((year) % 4 == 0 && ((year) % 100 != 0 || (year) % 400 == 0))
219 #endif
221 /* Compute the day of the week. */
222 static void
223 day_of_the_week (struct tm *tm)
225 /* We know that January 1st 1970 was a Thursday (= 4). Compute the
226 the difference between this data in the one on TM and so determine
227 the weekday. */
228 int corr_year = 1900 + tm->tm_year - (tm->tm_mon < 2);
229 int wday = (-473
230 + (365 * (tm->tm_year - 70))
231 + (corr_year / 4)
232 - ((corr_year / 4) / 25) + ((corr_year / 4) % 25 < 0)
233 + (((corr_year / 4) / 25) / 4)
234 + __mon_yday[0][tm->tm_mon]
235 + tm->tm_mday - 1);
236 tm->tm_wday = ((wday % 7) + 7) % 7;
239 /* Compute the day of the year. */
240 static void
241 day_of_the_year (struct tm *tm)
243 tm->tm_yday = (__mon_yday[__isleap (1900 + tm->tm_year)][tm->tm_mon]
244 + (tm->tm_mday - 1));
248 #ifdef _LIBC
249 char *
250 internal_function
251 #else
252 static char *
253 #endif
254 __strptime_internal (rp, fmt, tm, decided, era_cnt LOCALE_PARAM)
255 const char *rp;
256 const char *fmt;
257 struct tm *tm;
258 enum ptime_locale_status *decided;
259 int era_cnt;
260 LOCALE_PARAM_DECL
262 #ifdef _LIBC
263 struct locale_data *const current = locale->__locales[LC_TIME];
264 #endif
266 const char *rp_backup;
267 int cnt;
268 size_t val;
269 int have_I, is_pm;
270 int century, want_century;
271 int want_era;
272 int have_wday, want_xday;
273 int have_yday;
274 int have_mon, have_mday;
275 int have_uweek, have_wweek;
276 int week_no;
277 size_t num_eras;
278 struct era_entry *era;
280 have_I = is_pm = 0;
281 century = -1;
282 want_century = 0;
283 want_era = 0;
284 era = NULL;
285 week_no = 0;
287 have_wday = want_xday = have_yday = have_mon = have_mday = have_uweek = 0;
288 have_wweek = 0;
290 while (*fmt != '\0')
292 /* A white space in the format string matches 0 more or white
293 space in the input string. */
294 if (ISSPACE (*fmt))
296 while (ISSPACE (*rp))
297 ++rp;
298 ++fmt;
299 continue;
302 /* Any character but `%' must be matched by the same character
303 in the iput string. */
304 if (*fmt != '%')
306 match_char (*fmt++, *rp++);
307 continue;
310 ++fmt;
311 #ifndef _NL_CURRENT
312 /* We need this for handling the `E' modifier. */
313 start_over:
314 #endif
316 /* Make back up of current processing pointer. */
317 rp_backup = rp;
319 switch (*fmt++)
321 case '%':
322 /* Match the `%' character itself. */
323 match_char ('%', *rp++);
324 break;
325 case 'a':
326 case 'A':
327 /* Match day of week. */
328 for (cnt = 0; cnt < 7; ++cnt)
330 #ifdef _NL_CURRENT
331 if (*decided !=raw)
333 if (match_string (_NL_CURRENT (LC_TIME, DAY_1 + cnt), rp))
335 if (*decided == not
336 && strcmp (_NL_CURRENT (LC_TIME, DAY_1 + cnt),
337 weekday_name[cnt]))
338 *decided = loc;
339 break;
341 if (match_string (_NL_CURRENT (LC_TIME, ABDAY_1 + cnt), rp))
343 if (*decided == not
344 && strcmp (_NL_CURRENT (LC_TIME, ABDAY_1 + cnt),
345 ab_weekday_name[cnt]))
346 *decided = loc;
347 break;
350 #endif
351 if (*decided != loc
352 && (match_string (weekday_name[cnt], rp)
353 || match_string (ab_weekday_name[cnt], rp)))
355 *decided = raw;
356 break;
359 if (cnt == 7)
360 /* Does not match a weekday name. */
361 return NULL;
362 tm->tm_wday = cnt;
363 have_wday = 1;
364 break;
365 case 'b':
366 case 'B':
367 case 'h':
368 /* Match month name. */
369 for (cnt = 0; cnt < 12; ++cnt)
371 #ifdef _NL_CURRENT
372 if (*decided !=raw)
374 if (match_string (_NL_CURRENT (LC_TIME, MON_1 + cnt), rp))
376 if (*decided == not
377 && strcmp (_NL_CURRENT (LC_TIME, MON_1 + cnt),
378 month_name[cnt]))
379 *decided = loc;
380 break;
382 if (match_string (_NL_CURRENT (LC_TIME, ABMON_1 + cnt), rp))
384 if (*decided == not
385 && strcmp (_NL_CURRENT (LC_TIME, ABMON_1 + cnt),
386 ab_month_name[cnt]))
387 *decided = loc;
388 break;
391 #endif
392 if (match_string (month_name[cnt], rp)
393 || match_string (ab_month_name[cnt], rp))
395 *decided = raw;
396 break;
399 if (cnt == 12)
400 /* Does not match a month name. */
401 return NULL;
402 tm->tm_mon = cnt;
403 want_xday = 1;
404 break;
405 case 'c':
406 /* Match locale's date and time format. */
407 #ifdef _NL_CURRENT
408 if (*decided != raw)
410 if (!recursive (_NL_CURRENT (LC_TIME, D_T_FMT)))
412 if (*decided == loc)
413 return NULL;
414 else
415 rp = rp_backup;
417 else
419 if (*decided == not &&
420 strcmp (_NL_CURRENT (LC_TIME, D_T_FMT), HERE_D_T_FMT))
421 *decided = loc;
422 want_xday = 1;
423 break;
425 *decided = raw;
427 #endif
428 if (!recursive (HERE_D_T_FMT))
429 return NULL;
430 want_xday = 1;
431 break;
432 case 'C':
433 /* Match century number. */
434 match_century:
435 get_number (0, 99, 2);
436 century = val;
437 want_xday = 1;
438 break;
439 case 'd':
440 case 'e':
441 /* Match day of month. */
442 get_number (1, 31, 2);
443 tm->tm_mday = val;
444 have_mday = 1;
445 want_xday = 1;
446 break;
447 case 'F':
448 if (!recursive ("%Y-%m-%d"))
449 return NULL;
450 want_xday = 1;
451 break;
452 case 'x':
453 #ifdef _NL_CURRENT
454 if (*decided != raw)
456 if (!recursive (_NL_CURRENT (LC_TIME, D_FMT)))
458 if (*decided == loc)
459 return NULL;
460 else
461 rp = rp_backup;
463 else
465 if (*decided == not
466 && strcmp (_NL_CURRENT (LC_TIME, D_FMT), HERE_D_FMT))
467 *decided = loc;
468 want_xday = 1;
469 break;
471 *decided = raw;
473 #endif
474 /* Fall through. */
475 case 'D':
476 /* Match standard day format. */
477 if (!recursive (HERE_D_FMT))
478 return NULL;
479 want_xday = 1;
480 break;
481 case 'k':
482 case 'H':
483 /* Match hour in 24-hour clock. */
484 get_number (0, 23, 2);
485 tm->tm_hour = val;
486 have_I = 0;
487 break;
488 case 'l':
489 /* Match hour in 12-hour clock. GNU extension. */
490 case 'I':
491 /* Match hour in 12-hour clock. */
492 get_number (1, 12, 2);
493 tm->tm_hour = val % 12;
494 have_I = 1;
495 break;
496 case 'j':
497 /* Match day number of year. */
498 get_number (1, 366, 3);
499 tm->tm_yday = val - 1;
500 have_yday = 1;
501 break;
502 case 'm':
503 /* Match number of month. */
504 get_number (1, 12, 2);
505 tm->tm_mon = val - 1;
506 have_mon = 1;
507 want_xday = 1;
508 break;
509 case 'M':
510 /* Match minute. */
511 get_number (0, 59, 2);
512 tm->tm_min = val;
513 break;
514 case 'n':
515 case 't':
516 /* Match any white space. */
517 while (ISSPACE (*rp))
518 ++rp;
519 break;
520 case 'p':
521 /* Match locale's equivalent of AM/PM. */
522 #ifdef _NL_CURRENT
523 if (*decided != raw)
525 if (match_string (_NL_CURRENT (LC_TIME, AM_STR), rp))
527 if (strcmp (_NL_CURRENT (LC_TIME, AM_STR), HERE_AM_STR))
528 *decided = loc;
529 break;
531 if (match_string (_NL_CURRENT (LC_TIME, PM_STR), rp))
533 if (strcmp (_NL_CURRENT (LC_TIME, PM_STR), HERE_PM_STR))
534 *decided = loc;
535 is_pm = 1;
536 break;
538 *decided = raw;
540 #endif
541 if (!match_string (HERE_AM_STR, rp))
543 if (match_string (HERE_PM_STR, rp))
544 is_pm = 1;
545 else
546 return NULL;
548 break;
549 case 'r':
550 #ifdef _NL_CURRENT
551 if (*decided != raw)
553 if (!recursive (_NL_CURRENT (LC_TIME, T_FMT_AMPM)))
555 if (*decided == loc)
556 return NULL;
557 else
558 rp = rp_backup;
560 else
562 if (*decided == not &&
563 strcmp (_NL_CURRENT (LC_TIME, T_FMT_AMPM),
564 HERE_T_FMT_AMPM))
565 *decided = loc;
566 break;
568 *decided = raw;
570 #endif
571 if (!recursive (HERE_T_FMT_AMPM))
572 return NULL;
573 break;
574 case 'R':
575 if (!recursive ("%H:%M"))
576 return NULL;
577 break;
578 case 's':
580 /* The number of seconds may be very high so we cannot use
581 the `get_number' macro. Instead read the number
582 character for character and construct the result while
583 doing this. */
584 time_t secs = 0;
585 if (*rp < '0' || *rp > '9')
586 /* We need at least one digit. */
587 return NULL;
591 secs *= 10;
592 secs += *rp++ - '0';
594 while (*rp >= '0' && *rp <= '9');
596 if (localtime_r (&secs, tm) == NULL)
597 /* Error in function. */
598 return NULL;
600 break;
601 case 'S':
602 get_number (0, 61, 2);
603 tm->tm_sec = val;
604 break;
605 case 'X':
606 #ifdef _NL_CURRENT
607 if (*decided != raw)
609 if (!recursive (_NL_CURRENT (LC_TIME, T_FMT)))
611 if (*decided == loc)
612 return NULL;
613 else
614 rp = rp_backup;
616 else
618 if (strcmp (_NL_CURRENT (LC_TIME, T_FMT), HERE_T_FMT))
619 *decided = loc;
620 break;
622 *decided = raw;
624 #endif
625 /* Fall through. */
626 case 'T':
627 if (!recursive (HERE_T_FMT))
628 return NULL;
629 break;
630 case 'u':
631 get_number (1, 7, 1);
632 tm->tm_wday = val % 7;
633 have_wday = 1;
634 break;
635 case 'g':
636 get_number (0, 99, 2);
637 /* XXX This cannot determine any field in TM. */
638 break;
639 case 'G':
640 if (*rp < '0' || *rp > '9')
641 return NULL;
642 /* XXX Ignore the number since we would need some more
643 information to compute a real date. */
645 ++rp;
646 while (*rp >= '0' && *rp <= '9');
647 break;
648 case 'U':
649 get_number (0, 53, 2);
650 week_no = val;
651 have_uweek = 1;
652 break;
653 case 'W':
654 get_number (0, 53, 2);
655 week_no = val;
656 have_wweek = 1;
657 break;
658 case 'V':
659 get_number (0, 53, 2);
660 /* XXX This cannot determine any field in TM without some
661 information. */
662 break;
663 case 'w':
664 /* Match number of weekday. */
665 get_number (0, 6, 1);
666 tm->tm_wday = val;
667 have_wday = 1;
668 break;
669 case 'y':
670 match_year_in_century:
671 /* Match year within century. */
672 get_number (0, 99, 2);
673 /* The "Year 2000: The Millennium Rollover" paper suggests that
674 values in the range 69-99 refer to the twentieth century. */
675 tm->tm_year = val >= 69 ? val : val + 100;
676 /* Indicate that we want to use the century, if specified. */
677 want_century = 1;
678 want_xday = 1;
679 break;
680 case 'Y':
681 /* Match year including century number. */
682 get_number (0, 9999, 4);
683 tm->tm_year = val - 1900;
684 want_century = 0;
685 want_xday = 1;
686 break;
687 case 'Z':
688 /* XXX How to handle this? */
689 break;
690 case 'z':
691 /* We recognize two formats: if two digits are given, these
692 specify hours. If fours digits are used, minutes are
693 also specified. */
695 val = 0;
696 while (*rp == ' ')
697 ++rp;
698 if (*rp != '+' && *rp != '-')
699 return NULL;
700 bool neg = *rp++ == '-';
701 int n = 0;
702 while (n < 4 && *rp >= '0' && *rp <= '9')
704 val = val * 10 + *rp++ - '0';
705 ++n;
707 if (n == 2)
708 val *= 100;
709 else if (n != 4)
710 /* Only two or four digits recognized. */
711 return NULL;
712 else
714 /* We have to convert the minutes into decimal. */
715 if (val % 100 >= 60)
716 return NULL;
717 val = (val / 100) * 100 + ((val % 100) * 50) / 30;
719 if (val > 1200)
720 return NULL;
721 tm->tm_gmtoff = (val * 3600) / 100;
722 if (neg)
723 tm->tm_gmtoff = -tm->tm_gmtoff;
725 break;
726 case 'E':
727 #ifdef _NL_CURRENT
728 switch (*fmt++)
730 case 'c':
731 /* Match locale's alternate date and time format. */
732 if (*decided != raw)
734 const char *fmt = _NL_CURRENT (LC_TIME, ERA_D_T_FMT);
736 if (*fmt == '\0')
737 fmt = _NL_CURRENT (LC_TIME, D_T_FMT);
739 if (!recursive (fmt))
741 if (*decided == loc)
742 return NULL;
743 else
744 rp = rp_backup;
746 else
748 if (strcmp (fmt, HERE_D_T_FMT))
749 *decided = loc;
750 want_xday = 1;
751 break;
753 *decided = raw;
755 /* The C locale has no era information, so use the
756 normal representation. */
757 if (!recursive (HERE_D_T_FMT))
758 return NULL;
759 want_xday = 1;
760 break;
761 case 'C':
762 if (*decided != raw)
764 if (era_cnt >= 0)
766 era = _nl_select_era_entry (era_cnt HELPER_LOCALE_ARG);
767 if (era != NULL && match_string (era->era_name, rp))
769 *decided = loc;
770 break;
772 else
773 return NULL;
776 num_eras = _NL_CURRENT_WORD (LC_TIME,
777 _NL_TIME_ERA_NUM_ENTRIES);
778 for (era_cnt = 0; era_cnt < (int) num_eras;
779 ++era_cnt, rp = rp_backup)
781 era = _nl_select_era_entry (era_cnt
782 HELPER_LOCALE_ARG);
783 if (era != NULL && match_string (era->era_name, rp))
785 *decided = loc;
786 break;
789 if (era_cnt != (int) num_eras)
790 break;
792 era_cnt = -1;
793 if (*decided == loc)
794 return NULL;
796 *decided = raw;
798 /* The C locale has no era information, so use the
799 normal representation. */
800 goto match_century;
801 case 'y':
802 if (*decided != raw)
804 get_number(0, 9999, 4);
805 tm->tm_year = val;
806 want_era = 1;
807 want_xday = 1;
808 want_century = 1;
810 if (era_cnt >= 0)
812 assert (*decided == loc);
814 era = _nl_select_era_entry (era_cnt HELPER_LOCALE_ARG);
815 bool match = false;
816 if (era != NULL)
818 int delta = ((tm->tm_year - era->offset)
819 * era->absolute_direction);
820 match = (delta >= 0
821 && delta < (((int64_t) era->stop_date[0]
822 - (int64_t) era->start_date[0])
823 * era->absolute_direction));
825 if (! match)
826 return NULL;
828 break;
831 num_eras = _NL_CURRENT_WORD (LC_TIME,
832 _NL_TIME_ERA_NUM_ENTRIES);
833 for (era_cnt = 0; era_cnt < (int) num_eras; ++era_cnt)
835 era = _nl_select_era_entry (era_cnt
836 HELPER_LOCALE_ARG);
837 if (era != NULL)
839 int delta = ((tm->tm_year - era->offset)
840 * era->absolute_direction);
841 if (delta >= 0
842 && delta < (((int64_t) era->stop_date[0]
843 - (int64_t) era->start_date[0])
844 * era->absolute_direction))
846 *decided = loc;
847 break;
851 if (era_cnt != (int) num_eras)
852 break;
854 era_cnt = -1;
855 if (*decided == loc)
856 return NULL;
858 *decided = raw;
861 goto match_year_in_century;
862 case 'Y':
863 if (*decided != raw)
865 num_eras = _NL_CURRENT_WORD (LC_TIME,
866 _NL_TIME_ERA_NUM_ENTRIES);
867 for (era_cnt = 0; era_cnt < (int) num_eras;
868 ++era_cnt, rp = rp_backup)
870 era = _nl_select_era_entry (era_cnt HELPER_LOCALE_ARG);
871 if (era != NULL && recursive (era->era_format))
872 break;
874 if (era_cnt == (int) num_eras)
876 era_cnt = -1;
877 if (*decided == loc)
878 return NULL;
879 else
880 rp = rp_backup;
882 else
884 *decided = loc;
885 era_cnt = -1;
886 break;
889 *decided = raw;
891 get_number (0, 9999, 4);
892 tm->tm_year = val - 1900;
893 want_century = 0;
894 want_xday = 1;
895 break;
896 case 'x':
897 if (*decided != raw)
899 const char *fmt = _NL_CURRENT (LC_TIME, ERA_D_FMT);
901 if (*fmt == '\0')
902 fmt = _NL_CURRENT (LC_TIME, D_FMT);
904 if (!recursive (fmt))
906 if (*decided == loc)
907 return NULL;
908 else
909 rp = rp_backup;
911 else
913 if (strcmp (fmt, HERE_D_FMT))
914 *decided = loc;
915 break;
917 *decided = raw;
919 if (!recursive (HERE_D_FMT))
920 return NULL;
921 break;
922 case 'X':
923 if (*decided != raw)
925 const char *fmt = _NL_CURRENT (LC_TIME, ERA_T_FMT);
927 if (*fmt == '\0')
928 fmt = _NL_CURRENT (LC_TIME, T_FMT);
930 if (!recursive (fmt))
932 if (*decided == loc)
933 return NULL;
934 else
935 rp = rp_backup;
937 else
939 if (strcmp (fmt, HERE_T_FMT))
940 *decided = loc;
941 break;
943 *decided = raw;
945 if (!recursive (HERE_T_FMT))
946 return NULL;
947 break;
948 default:
949 return NULL;
951 break;
952 #else
953 /* We have no information about the era format. Just use
954 the normal format. */
955 if (*fmt != 'c' && *fmt != 'C' && *fmt != 'y' && *fmt != 'Y'
956 && *fmt != 'x' && *fmt != 'X')
957 /* This is an illegal format. */
958 return NULL;
960 goto start_over;
961 #endif
962 case 'O':
963 switch (*fmt++)
965 case 'd':
966 case 'e':
967 /* Match day of month using alternate numeric symbols. */
968 get_alt_number (1, 31, 2);
969 tm->tm_mday = val;
970 have_mday = 1;
971 want_xday = 1;
972 break;
973 case 'H':
974 /* Match hour in 24-hour clock using alternate numeric
975 symbols. */
976 get_alt_number (0, 23, 2);
977 tm->tm_hour = val;
978 have_I = 0;
979 break;
980 case 'I':
981 /* Match hour in 12-hour clock using alternate numeric
982 symbols. */
983 get_alt_number (1, 12, 2);
984 tm->tm_hour = val % 12;
985 have_I = 1;
986 break;
987 case 'm':
988 /* Match month using alternate numeric symbols. */
989 get_alt_number (1, 12, 2);
990 tm->tm_mon = val - 1;
991 have_mon = 1;
992 want_xday = 1;
993 break;
994 case 'M':
995 /* Match minutes using alternate numeric symbols. */
996 get_alt_number (0, 59, 2);
997 tm->tm_min = val;
998 break;
999 case 'S':
1000 /* Match seconds using alternate numeric symbols. */
1001 get_alt_number (0, 61, 2);
1002 tm->tm_sec = val;
1003 break;
1004 case 'U':
1005 get_alt_number (0, 53, 2);
1006 week_no = val;
1007 have_uweek = 1;
1008 break;
1009 case 'W':
1010 get_alt_number (0, 53, 2);
1011 week_no = val;
1012 have_wweek = 1;
1013 break;
1014 case 'V':
1015 get_alt_number (0, 53, 2);
1016 /* XXX This cannot determine any field in TM without
1017 further information. */
1018 break;
1019 case 'w':
1020 /* Match number of weekday using alternate numeric symbols. */
1021 get_alt_number (0, 6, 1);
1022 tm->tm_wday = val;
1023 have_wday = 1;
1024 break;
1025 case 'y':
1026 /* Match year within century using alternate numeric symbols. */
1027 get_alt_number (0, 99, 2);
1028 tm->tm_year = val >= 69 ? val : val + 100;
1029 want_xday = 1;
1030 break;
1031 default:
1032 return NULL;
1034 break;
1035 default:
1036 return NULL;
1040 if (have_I && is_pm)
1041 tm->tm_hour += 12;
1043 if (century != -1)
1045 if (want_century)
1046 tm->tm_year = tm->tm_year % 100 + (century - 19) * 100;
1047 else
1048 /* Only the century, but not the year. Strange, but so be it. */
1049 tm->tm_year = (century - 19) * 100;
1052 if (era_cnt != -1)
1054 era = _nl_select_era_entry (era_cnt HELPER_LOCALE_ARG);
1055 if (era == NULL)
1056 return NULL;
1057 if (want_era)
1058 tm->tm_year = (era->start_date[0]
1059 + ((tm->tm_year - era->offset)
1060 * era->absolute_direction));
1061 else
1062 /* Era start year assumed. */
1063 tm->tm_year = era->start_date[0];
1065 else
1066 if (want_era)
1068 /* No era found but we have seen an E modifier. Rectify some
1069 values. */
1070 if (want_century && century == -1 && tm->tm_year < 69)
1071 tm->tm_year += 100;
1074 if (want_xday && !have_wday)
1076 if ( !(have_mon && have_mday) && have_yday)
1078 /* We don't have tm_mon and/or tm_mday, compute them. */
1079 int t_mon = 0;
1080 while (__mon_yday[__isleap(1900 + tm->tm_year)][t_mon] <= tm->tm_yday)
1081 t_mon++;
1082 if (!have_mon)
1083 tm->tm_mon = t_mon - 1;
1084 if (!have_mday)
1085 tm->tm_mday =
1086 (tm->tm_yday
1087 - __mon_yday[__isleap(1900 + tm->tm_year)][t_mon - 1] + 1);
1089 day_of_the_week (tm);
1092 if (want_xday && !have_yday)
1093 day_of_the_year (tm);
1095 if ((have_uweek || have_wweek) && have_wday)
1097 int save_wday = tm->tm_wday;
1098 int save_mday = tm->tm_mday;
1099 int save_mon = tm->tm_mon;
1100 int w_offset = have_uweek ? 0 : 1;
1102 tm->tm_mday = 1;
1103 tm->tm_mon = 0;
1104 day_of_the_week (tm);
1105 if (have_mday)
1106 tm->tm_mday = save_mday;
1107 if (have_mon)
1108 tm->tm_mon = save_mon;
1110 if (!have_yday)
1111 tm->tm_yday = ((7 - (tm->tm_wday - w_offset)) % 7
1112 + (week_no - 1) *7
1113 + save_wday - w_offset);
1115 if (!have_mday || !have_mon)
1117 int t_mon = 0;
1118 while (__mon_yday[__isleap(1900 + tm->tm_year)][t_mon]
1119 <= tm->tm_yday)
1120 t_mon++;
1121 if (!have_mon)
1122 tm->tm_mon = t_mon - 1;
1123 if (!have_mday)
1124 tm->tm_mday =
1125 (tm->tm_yday
1126 - __mon_yday[__isleap(1900 + tm->tm_year)][t_mon - 1] + 1);
1129 tm->tm_wday = save_wday;
1132 return (char *) rp;
1136 char *
1137 strptime (buf, format, tm LOCALE_PARAM)
1138 const char *buf;
1139 const char *format;
1140 struct tm *tm;
1141 LOCALE_PARAM_DECL
1143 enum ptime_locale_status decided;
1145 #ifdef _NL_CURRENT
1146 decided = not;
1147 #else
1148 decided = raw;
1149 #endif
1150 return __strptime_internal (buf, format, tm, &decided, -1 LOCALE_ARG);
1153 #ifdef _LIBC
1154 weak_alias (__strptime_l, strptime_l)
1155 #endif