[BZ #4773]
[glibc.git] / time / strptime_l.c
blob27b6f9ba868799ee9e103552ca74e71c15792620
1 /* Copyright (C) 2002, 2004, 2005, 2007 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 const char *rp_longest;
268 int cnt;
269 int cnt_longest;
270 size_t val;
271 int have_I;
272 int is_pm;
273 int century;
274 int want_century;
275 int want_era;
276 int have_wday;
277 int want_xday;
278 int have_yday;
279 int have_mon;
280 int have_mday;
281 int have_uweek;
282 int have_wweek;
283 int week_no;
284 size_t num_eras;
285 struct era_entry *era;
286 enum ptime_locale_status decided_longest;
288 have_I = is_pm = 0;
289 century = -1;
290 want_century = 0;
291 want_era = 0;
292 era = NULL;
293 week_no = 0;
295 have_wday = want_xday = have_yday = have_mon = have_mday = have_uweek = 0;
296 have_wweek = 0;
298 while (*fmt != '\0')
300 /* A white space in the format string matches 0 more or white
301 space in the input string. */
302 if (ISSPACE (*fmt))
304 while (ISSPACE (*rp))
305 ++rp;
306 ++fmt;
307 continue;
310 /* Any character but `%' must be matched by the same character
311 in the iput string. */
312 if (*fmt != '%')
314 match_char (*fmt++, *rp++);
315 continue;
318 ++fmt;
319 #ifndef _NL_CURRENT
320 /* We need this for handling the `E' modifier. */
321 start_over:
322 #endif
324 /* Make back up of current processing pointer. */
325 rp_backup = rp;
327 switch (*fmt++)
329 case '%':
330 /* Match the `%' character itself. */
331 match_char ('%', *rp++);
332 break;
333 case 'a':
334 case 'A':
335 /* Match day of week. */
336 rp_longest = NULL;
337 decided_longest = *decided;
338 cnt_longest = -1;
339 for (cnt = 0; cnt < 7; ++cnt)
341 const char *trp;
342 #ifdef _NL_CURRENT
343 if (*decided !=raw)
345 trp = rp;
346 if (match_string (_NL_CURRENT (LC_TIME, DAY_1 + cnt), trp)
347 && trp > rp_longest)
349 rp_longest = trp;
350 cnt_longest = cnt;
351 if (*decided == not
352 && strcmp (_NL_CURRENT (LC_TIME, DAY_1 + cnt),
353 weekday_name[cnt]))
354 decided_longest = loc;
356 trp = rp;
357 if (match_string (_NL_CURRENT (LC_TIME, ABDAY_1 + cnt), trp)
358 && trp > rp_longest)
360 rp_longest = trp;
361 cnt_longest = cnt;
362 if (*decided == not
363 && strcmp (_NL_CURRENT (LC_TIME, ABDAY_1 + cnt),
364 ab_weekday_name[cnt]))
365 decided_longest = loc;
368 #endif
369 if (*decided != loc
370 && (((trp = rp, match_string (weekday_name[cnt], trp))
371 && trp > rp_longest)
372 || ((trp = rp, match_string (ab_weekday_name[cnt], rp))
373 && trp > rp_longest)))
375 rp_longest = trp;
376 cnt_longest = cnt;
377 decided_longest = raw;
380 if (rp_longest == NULL)
381 /* Does not match a weekday name. */
382 return NULL;
383 rp = rp_longest;
384 *decided = decided_longest;
385 tm->tm_wday = cnt_longest;
386 have_wday = 1;
387 break;
388 case 'b':
389 case 'B':
390 case 'h':
391 /* Match month name. */
392 rp_longest = NULL;
393 decided_longest = *decided;
394 cnt_longest = -1;
395 for (cnt = 0; cnt < 12; ++cnt)
397 const char *trp;
398 #ifdef _NL_CURRENT
399 if (*decided !=raw)
401 trp = rp;
402 if (match_string (_NL_CURRENT (LC_TIME, MON_1 + cnt), trp)
403 && trp > rp_longest)
405 rp_longest = trp;
406 cnt_longest = cnt;
407 if (*decided == not
408 && strcmp (_NL_CURRENT (LC_TIME, MON_1 + cnt),
409 month_name[cnt]))
410 decided_longest = loc;
412 trp = rp;
413 if (match_string (_NL_CURRENT (LC_TIME, ABMON_1 + cnt), trp)
414 && trp > rp_longest)
416 rp_longest = trp;
417 cnt_longest = cnt;
418 if (*decided == not
419 && strcmp (_NL_CURRENT (LC_TIME, ABMON_1 + cnt),
420 ab_month_name[cnt]))
421 decided_longest = loc;
424 #endif
425 if (*decided != loc
426 && (((trp = rp, match_string (month_name[cnt], trp))
427 && trp > rp_longest)
428 || ((trp = rp, match_string (ab_month_name[cnt], trp))
429 && trp > rp_longest)))
431 rp_longest = trp;
432 cnt_longest = cnt;
433 decided_longest = raw;
436 if (rp_longest == NULL)
437 /* Does not match a month name. */
438 return NULL;
439 rp = rp_longest;
440 *decided = decided_longest;
441 tm->tm_mon = cnt_longest;
442 have_mon = 1;
443 want_xday = 1;
444 break;
445 case 'c':
446 /* Match locale's date and time format. */
447 #ifdef _NL_CURRENT
448 if (*decided != raw)
450 if (!recursive (_NL_CURRENT (LC_TIME, D_T_FMT)))
452 if (*decided == loc)
453 return NULL;
454 else
455 rp = rp_backup;
457 else
459 if (*decided == not &&
460 strcmp (_NL_CURRENT (LC_TIME, D_T_FMT), HERE_D_T_FMT))
461 *decided = loc;
462 want_xday = 1;
463 break;
465 *decided = raw;
467 #endif
468 if (!recursive (HERE_D_T_FMT))
469 return NULL;
470 want_xday = 1;
471 break;
472 case 'C':
473 /* Match century number. */
474 match_century:
475 get_number (0, 99, 2);
476 century = val;
477 want_xday = 1;
478 break;
479 case 'd':
480 case 'e':
481 /* Match day of month. */
482 get_number (1, 31, 2);
483 tm->tm_mday = val;
484 have_mday = 1;
485 want_xday = 1;
486 break;
487 case 'F':
488 if (!recursive ("%Y-%m-%d"))
489 return NULL;
490 want_xday = 1;
491 break;
492 case 'x':
493 #ifdef _NL_CURRENT
494 if (*decided != raw)
496 if (!recursive (_NL_CURRENT (LC_TIME, D_FMT)))
498 if (*decided == loc)
499 return NULL;
500 else
501 rp = rp_backup;
503 else
505 if (*decided == not
506 && strcmp (_NL_CURRENT (LC_TIME, D_FMT), HERE_D_FMT))
507 *decided = loc;
508 want_xday = 1;
509 break;
511 *decided = raw;
513 #endif
514 /* Fall through. */
515 case 'D':
516 /* Match standard day format. */
517 if (!recursive (HERE_D_FMT))
518 return NULL;
519 want_xday = 1;
520 break;
521 case 'k':
522 case 'H':
523 /* Match hour in 24-hour clock. */
524 get_number (0, 23, 2);
525 tm->tm_hour = val;
526 have_I = 0;
527 break;
528 case 'l':
529 /* Match hour in 12-hour clock. GNU extension. */
530 case 'I':
531 /* Match hour in 12-hour clock. */
532 get_number (1, 12, 2);
533 tm->tm_hour = val % 12;
534 have_I = 1;
535 break;
536 case 'j':
537 /* Match day number of year. */
538 get_number (1, 366, 3);
539 tm->tm_yday = val - 1;
540 have_yday = 1;
541 break;
542 case 'm':
543 /* Match number of month. */
544 get_number (1, 12, 2);
545 tm->tm_mon = val - 1;
546 have_mon = 1;
547 want_xday = 1;
548 break;
549 case 'M':
550 /* Match minute. */
551 get_number (0, 59, 2);
552 tm->tm_min = val;
553 break;
554 case 'n':
555 case 't':
556 /* Match any white space. */
557 while (ISSPACE (*rp))
558 ++rp;
559 break;
560 case 'p':
561 /* Match locale's equivalent of AM/PM. */
562 #ifdef _NL_CURRENT
563 if (*decided != raw)
565 if (match_string (_NL_CURRENT (LC_TIME, AM_STR), rp))
567 if (strcmp (_NL_CURRENT (LC_TIME, AM_STR), HERE_AM_STR))
568 *decided = loc;
569 break;
571 if (match_string (_NL_CURRENT (LC_TIME, PM_STR), rp))
573 if (strcmp (_NL_CURRENT (LC_TIME, PM_STR), HERE_PM_STR))
574 *decided = loc;
575 is_pm = 1;
576 break;
578 *decided = raw;
580 #endif
581 if (!match_string (HERE_AM_STR, rp))
583 if (match_string (HERE_PM_STR, rp))
584 is_pm = 1;
585 else
586 return NULL;
588 break;
589 case 'r':
590 #ifdef _NL_CURRENT
591 if (*decided != raw)
593 if (!recursive (_NL_CURRENT (LC_TIME, T_FMT_AMPM)))
595 if (*decided == loc)
596 return NULL;
597 else
598 rp = rp_backup;
600 else
602 if (*decided == not &&
603 strcmp (_NL_CURRENT (LC_TIME, T_FMT_AMPM),
604 HERE_T_FMT_AMPM))
605 *decided = loc;
606 break;
608 *decided = raw;
610 #endif
611 if (!recursive (HERE_T_FMT_AMPM))
612 return NULL;
613 break;
614 case 'R':
615 if (!recursive ("%H:%M"))
616 return NULL;
617 break;
618 case 's':
620 /* The number of seconds may be very high so we cannot use
621 the `get_number' macro. Instead read the number
622 character for character and construct the result while
623 doing this. */
624 time_t secs = 0;
625 if (*rp < '0' || *rp > '9')
626 /* We need at least one digit. */
627 return NULL;
631 secs *= 10;
632 secs += *rp++ - '0';
634 while (*rp >= '0' && *rp <= '9');
636 if (localtime_r (&secs, tm) == NULL)
637 /* Error in function. */
638 return NULL;
640 break;
641 case 'S':
642 get_number (0, 61, 2);
643 tm->tm_sec = val;
644 break;
645 case 'X':
646 #ifdef _NL_CURRENT
647 if (*decided != raw)
649 if (!recursive (_NL_CURRENT (LC_TIME, T_FMT)))
651 if (*decided == loc)
652 return NULL;
653 else
654 rp = rp_backup;
656 else
658 if (strcmp (_NL_CURRENT (LC_TIME, T_FMT), HERE_T_FMT))
659 *decided = loc;
660 break;
662 *decided = raw;
664 #endif
665 /* Fall through. */
666 case 'T':
667 if (!recursive (HERE_T_FMT))
668 return NULL;
669 break;
670 case 'u':
671 get_number (1, 7, 1);
672 tm->tm_wday = val % 7;
673 have_wday = 1;
674 break;
675 case 'g':
676 get_number (0, 99, 2);
677 /* XXX This cannot determine any field in TM. */
678 break;
679 case 'G':
680 if (*rp < '0' || *rp > '9')
681 return NULL;
682 /* XXX Ignore the number since we would need some more
683 information to compute a real date. */
685 ++rp;
686 while (*rp >= '0' && *rp <= '9');
687 break;
688 case 'U':
689 get_number (0, 53, 2);
690 week_no = val;
691 have_uweek = 1;
692 break;
693 case 'W':
694 get_number (0, 53, 2);
695 week_no = val;
696 have_wweek = 1;
697 break;
698 case 'V':
699 get_number (0, 53, 2);
700 /* XXX This cannot determine any field in TM without some
701 information. */
702 break;
703 case 'w':
704 /* Match number of weekday. */
705 get_number (0, 6, 1);
706 tm->tm_wday = val;
707 have_wday = 1;
708 break;
709 case 'y':
710 match_year_in_century:
711 /* Match year within century. */
712 get_number (0, 99, 2);
713 /* The "Year 2000: The Millennium Rollover" paper suggests that
714 values in the range 69-99 refer to the twentieth century. */
715 tm->tm_year = val >= 69 ? val : val + 100;
716 /* Indicate that we want to use the century, if specified. */
717 want_century = 1;
718 want_xday = 1;
719 break;
720 case 'Y':
721 /* Match year including century number. */
722 get_number (0, 9999, 4);
723 tm->tm_year = val - 1900;
724 want_century = 0;
725 want_xday = 1;
726 break;
727 case 'Z':
728 /* XXX How to handle this? */
729 break;
730 case 'z':
731 /* We recognize two formats: if two digits are given, these
732 specify hours. If fours digits are used, minutes are
733 also specified. */
735 val = 0;
736 while (*rp == ' ')
737 ++rp;
738 if (*rp != '+' && *rp != '-')
739 return NULL;
740 bool neg = *rp++ == '-';
741 int n = 0;
742 while (n < 4 && *rp >= '0' && *rp <= '9')
744 val = val * 10 + *rp++ - '0';
745 ++n;
747 if (n == 2)
748 val *= 100;
749 else if (n != 4)
750 /* Only two or four digits recognized. */
751 return NULL;
752 else
754 /* We have to convert the minutes into decimal. */
755 if (val % 100 >= 60)
756 return NULL;
757 val = (val / 100) * 100 + ((val % 100) * 50) / 30;
759 if (val > 1200)
760 return NULL;
761 tm->tm_gmtoff = (val * 3600) / 100;
762 if (neg)
763 tm->tm_gmtoff = -tm->tm_gmtoff;
765 break;
766 case 'E':
767 #ifdef _NL_CURRENT
768 switch (*fmt++)
770 case 'c':
771 /* Match locale's alternate date and time format. */
772 if (*decided != raw)
774 const char *fmt = _NL_CURRENT (LC_TIME, ERA_D_T_FMT);
776 if (*fmt == '\0')
777 fmt = _NL_CURRENT (LC_TIME, D_T_FMT);
779 if (!recursive (fmt))
781 if (*decided == loc)
782 return NULL;
783 else
784 rp = rp_backup;
786 else
788 if (strcmp (fmt, HERE_D_T_FMT))
789 *decided = loc;
790 want_xday = 1;
791 break;
793 *decided = raw;
795 /* The C locale has no era information, so use the
796 normal representation. */
797 if (!recursive (HERE_D_T_FMT))
798 return NULL;
799 want_xday = 1;
800 break;
801 case 'C':
802 if (*decided != raw)
804 if (era_cnt >= 0)
806 era = _nl_select_era_entry (era_cnt HELPER_LOCALE_ARG);
807 if (era != NULL && match_string (era->era_name, rp))
809 *decided = loc;
810 break;
812 else
813 return NULL;
816 num_eras = _NL_CURRENT_WORD (LC_TIME,
817 _NL_TIME_ERA_NUM_ENTRIES);
818 for (era_cnt = 0; era_cnt < (int) num_eras;
819 ++era_cnt, rp = rp_backup)
821 era = _nl_select_era_entry (era_cnt
822 HELPER_LOCALE_ARG);
823 if (era != NULL && match_string (era->era_name, rp))
825 *decided = loc;
826 break;
829 if (era_cnt != (int) num_eras)
830 break;
832 era_cnt = -1;
833 if (*decided == loc)
834 return NULL;
836 *decided = raw;
838 /* The C locale has no era information, so use the
839 normal representation. */
840 goto match_century;
841 case 'y':
842 if (*decided != raw)
844 get_number(0, 9999, 4);
845 tm->tm_year = val;
846 want_era = 1;
847 want_xday = 1;
848 want_century = 1;
850 if (era_cnt >= 0)
852 assert (*decided == loc);
854 era = _nl_select_era_entry (era_cnt HELPER_LOCALE_ARG);
855 bool match = false;
856 if (era != NULL)
858 int delta = ((tm->tm_year - era->offset)
859 * era->absolute_direction);
860 match = (delta >= 0
861 && delta < (((int64_t) era->stop_date[0]
862 - (int64_t) era->start_date[0])
863 * era->absolute_direction));
865 if (! match)
866 return NULL;
868 break;
871 num_eras = _NL_CURRENT_WORD (LC_TIME,
872 _NL_TIME_ERA_NUM_ENTRIES);
873 for (era_cnt = 0; era_cnt < (int) num_eras; ++era_cnt)
875 era = _nl_select_era_entry (era_cnt
876 HELPER_LOCALE_ARG);
877 if (era != NULL)
879 int delta = ((tm->tm_year - era->offset)
880 * era->absolute_direction);
881 if (delta >= 0
882 && delta < (((int64_t) era->stop_date[0]
883 - (int64_t) era->start_date[0])
884 * era->absolute_direction))
886 *decided = loc;
887 break;
891 if (era_cnt != (int) num_eras)
892 break;
894 era_cnt = -1;
895 if (*decided == loc)
896 return NULL;
898 *decided = raw;
901 goto match_year_in_century;
902 case 'Y':
903 if (*decided != raw)
905 num_eras = _NL_CURRENT_WORD (LC_TIME,
906 _NL_TIME_ERA_NUM_ENTRIES);
907 for (era_cnt = 0; era_cnt < (int) num_eras;
908 ++era_cnt, rp = rp_backup)
910 era = _nl_select_era_entry (era_cnt HELPER_LOCALE_ARG);
911 if (era != NULL && recursive (era->era_format))
912 break;
914 if (era_cnt == (int) num_eras)
916 era_cnt = -1;
917 if (*decided == loc)
918 return NULL;
919 else
920 rp = rp_backup;
922 else
924 *decided = loc;
925 era_cnt = -1;
926 break;
929 *decided = raw;
931 get_number (0, 9999, 4);
932 tm->tm_year = val - 1900;
933 want_century = 0;
934 want_xday = 1;
935 break;
936 case 'x':
937 if (*decided != raw)
939 const char *fmt = _NL_CURRENT (LC_TIME, ERA_D_FMT);
941 if (*fmt == '\0')
942 fmt = _NL_CURRENT (LC_TIME, D_FMT);
944 if (!recursive (fmt))
946 if (*decided == loc)
947 return NULL;
948 else
949 rp = rp_backup;
951 else
953 if (strcmp (fmt, HERE_D_FMT))
954 *decided = loc;
955 break;
957 *decided = raw;
959 if (!recursive (HERE_D_FMT))
960 return NULL;
961 break;
962 case 'X':
963 if (*decided != raw)
965 const char *fmt = _NL_CURRENT (LC_TIME, ERA_T_FMT);
967 if (*fmt == '\0')
968 fmt = _NL_CURRENT (LC_TIME, T_FMT);
970 if (!recursive (fmt))
972 if (*decided == loc)
973 return NULL;
974 else
975 rp = rp_backup;
977 else
979 if (strcmp (fmt, HERE_T_FMT))
980 *decided = loc;
981 break;
983 *decided = raw;
985 if (!recursive (HERE_T_FMT))
986 return NULL;
987 break;
988 default:
989 return NULL;
991 break;
992 #else
993 /* We have no information about the era format. Just use
994 the normal format. */
995 if (*fmt != 'c' && *fmt != 'C' && *fmt != 'y' && *fmt != 'Y'
996 && *fmt != 'x' && *fmt != 'X')
997 /* This is an illegal format. */
998 return NULL;
1000 goto start_over;
1001 #endif
1002 case 'O':
1003 switch (*fmt++)
1005 case 'd':
1006 case 'e':
1007 /* Match day of month using alternate numeric symbols. */
1008 get_alt_number (1, 31, 2);
1009 tm->tm_mday = val;
1010 have_mday = 1;
1011 want_xday = 1;
1012 break;
1013 case 'H':
1014 /* Match hour in 24-hour clock using alternate numeric
1015 symbols. */
1016 get_alt_number (0, 23, 2);
1017 tm->tm_hour = val;
1018 have_I = 0;
1019 break;
1020 case 'I':
1021 /* Match hour in 12-hour clock using alternate numeric
1022 symbols. */
1023 get_alt_number (1, 12, 2);
1024 tm->tm_hour = val % 12;
1025 have_I = 1;
1026 break;
1027 case 'm':
1028 /* Match month using alternate numeric symbols. */
1029 get_alt_number (1, 12, 2);
1030 tm->tm_mon = val - 1;
1031 have_mon = 1;
1032 want_xday = 1;
1033 break;
1034 case 'M':
1035 /* Match minutes using alternate numeric symbols. */
1036 get_alt_number (0, 59, 2);
1037 tm->tm_min = val;
1038 break;
1039 case 'S':
1040 /* Match seconds using alternate numeric symbols. */
1041 get_alt_number (0, 61, 2);
1042 tm->tm_sec = val;
1043 break;
1044 case 'U':
1045 get_alt_number (0, 53, 2);
1046 week_no = val;
1047 have_uweek = 1;
1048 break;
1049 case 'W':
1050 get_alt_number (0, 53, 2);
1051 week_no = val;
1052 have_wweek = 1;
1053 break;
1054 case 'V':
1055 get_alt_number (0, 53, 2);
1056 /* XXX This cannot determine any field in TM without
1057 further information. */
1058 break;
1059 case 'w':
1060 /* Match number of weekday using alternate numeric symbols. */
1061 get_alt_number (0, 6, 1);
1062 tm->tm_wday = val;
1063 have_wday = 1;
1064 break;
1065 case 'y':
1066 /* Match year within century using alternate numeric symbols. */
1067 get_alt_number (0, 99, 2);
1068 tm->tm_year = val >= 69 ? val : val + 100;
1069 want_xday = 1;
1070 break;
1071 default:
1072 return NULL;
1074 break;
1075 default:
1076 return NULL;
1080 if (have_I && is_pm)
1081 tm->tm_hour += 12;
1083 if (century != -1)
1085 if (want_century)
1086 tm->tm_year = tm->tm_year % 100 + (century - 19) * 100;
1087 else
1088 /* Only the century, but not the year. Strange, but so be it. */
1089 tm->tm_year = (century - 19) * 100;
1092 if (era_cnt != -1)
1094 era = _nl_select_era_entry (era_cnt HELPER_LOCALE_ARG);
1095 if (era == NULL)
1096 return NULL;
1097 if (want_era)
1098 tm->tm_year = (era->start_date[0]
1099 + ((tm->tm_year - era->offset)
1100 * era->absolute_direction));
1101 else
1102 /* Era start year assumed. */
1103 tm->tm_year = era->start_date[0];
1105 else
1106 if (want_era)
1108 /* No era found but we have seen an E modifier. Rectify some
1109 values. */
1110 if (want_century && century == -1 && tm->tm_year < 69)
1111 tm->tm_year += 100;
1114 if (want_xday && !have_wday)
1116 if ( !(have_mon && have_mday) && have_yday)
1118 /* We don't have tm_mon and/or tm_mday, compute them. */
1119 int t_mon = 0;
1120 while (__mon_yday[__isleap(1900 + tm->tm_year)][t_mon] <= tm->tm_yday)
1121 t_mon++;
1122 if (!have_mon)
1123 tm->tm_mon = t_mon - 1;
1124 if (!have_mday)
1125 tm->tm_mday =
1126 (tm->tm_yday
1127 - __mon_yday[__isleap(1900 + tm->tm_year)][t_mon - 1] + 1);
1128 have_mon = 1;
1129 have_mday = 1;
1131 /* Don't crash in day_of_the_week if tm_mon is uninitialized. */
1132 if (have_mon || (unsigned) tm->tm_mon <= 11)
1133 day_of_the_week (tm);
1136 if (want_xday && !have_yday && (have_mon || (unsigned) tm->tm_mon <= 11))
1137 day_of_the_year (tm);
1139 if ((have_uweek || have_wweek) && have_wday)
1141 int save_wday = tm->tm_wday;
1142 int save_mday = tm->tm_mday;
1143 int save_mon = tm->tm_mon;
1144 int w_offset = have_uweek ? 0 : 1;
1146 tm->tm_mday = 1;
1147 tm->tm_mon = 0;
1148 day_of_the_week (tm);
1149 if (have_mday)
1150 tm->tm_mday = save_mday;
1151 if (have_mon)
1152 tm->tm_mon = save_mon;
1154 if (!have_yday)
1155 tm->tm_yday = ((7 - (tm->tm_wday - w_offset)) % 7
1156 + (week_no - 1) *7
1157 + save_wday - w_offset);
1159 if (!have_mday || !have_mon)
1161 int t_mon = 0;
1162 while (__mon_yday[__isleap(1900 + tm->tm_year)][t_mon]
1163 <= tm->tm_yday)
1164 t_mon++;
1165 if (!have_mon)
1166 tm->tm_mon = t_mon - 1;
1167 if (!have_mday)
1168 tm->tm_mday =
1169 (tm->tm_yday
1170 - __mon_yday[__isleap(1900 + tm->tm_year)][t_mon - 1] + 1);
1173 tm->tm_wday = save_wday;
1176 return (char *) rp;
1180 char *
1181 strptime (buf, format, tm LOCALE_PARAM)
1182 const char *buf;
1183 const char *format;
1184 struct tm *tm;
1185 LOCALE_PARAM_DECL
1187 enum ptime_locale_status decided;
1189 #ifdef _NL_CURRENT
1190 decided = not;
1191 #else
1192 decided = raw;
1193 #endif
1194 return __strptime_internal (buf, format, tm, &decided, -1 LOCALE_ARG);
1197 #ifdef _LIBC
1198 weak_alias (__strptime_l, strptime_l)
1199 #endif