[BZ #5040]
[glibc.git] / time / strptime_l.c
blob59a557c22b216b9819efde97633097f3fbbb2495
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 (s.decided != raw) \
103 val = _nl_parse_alt_digit (&rp HELPER_LOCALE_ARG); \
104 if (val == -1 && s.decided != loc) \
106 s.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, &s LOCALE_ARG)) != NULL)
129 #ifdef _LIBC
130 /* This is defined in locale/C-time.c in the GNU libc. */
131 extern const struct locale_data _nl_C_LC_TIME attribute_hidden;
133 # define weekday_name (&_nl_C_LC_TIME.values[_NL_ITEM_INDEX (DAY_1)].string)
134 # define ab_weekday_name \
135 (&_nl_C_LC_TIME.values[_NL_ITEM_INDEX (ABDAY_1)].string)
136 # define month_name (&_nl_C_LC_TIME.values[_NL_ITEM_INDEX (MON_1)].string)
137 # define ab_month_name (&_nl_C_LC_TIME.values[_NL_ITEM_INDEX (ABMON_1)].string)
138 # define HERE_D_T_FMT (_nl_C_LC_TIME.values[_NL_ITEM_INDEX (D_T_FMT)].string)
139 # define HERE_D_FMT (_nl_C_LC_TIME.values[_NL_ITEM_INDEX (D_FMT)].string)
140 # define HERE_AM_STR (_nl_C_LC_TIME.values[_NL_ITEM_INDEX (AM_STR)].string)
141 # define HERE_PM_STR (_nl_C_LC_TIME.values[_NL_ITEM_INDEX (PM_STR)].string)
142 # define HERE_T_FMT_AMPM \
143 (_nl_C_LC_TIME.values[_NL_ITEM_INDEX (T_FMT_AMPM)].string)
144 # define HERE_T_FMT (_nl_C_LC_TIME.values[_NL_ITEM_INDEX (T_FMT)].string)
146 # define strncasecmp(s1, s2, n) __strncasecmp (s1, s2, n)
147 #else
148 static char const weekday_name[][10] =
150 "Sunday", "Monday", "Tuesday", "Wednesday",
151 "Thursday", "Friday", "Saturday"
153 static char const ab_weekday_name[][4] =
155 "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
157 static char const month_name[][10] =
159 "January", "February", "March", "April", "May", "June",
160 "July", "August", "September", "October", "November", "December"
162 static char const ab_month_name[][4] =
164 "Jan", "Feb", "Mar", "Apr", "May", "Jun",
165 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
167 # define HERE_D_T_FMT "%a %b %e %H:%M:%S %Y"
168 # define HERE_D_FMT "%m/%d/%y"
169 # define HERE_AM_STR "AM"
170 # define HERE_PM_STR "PM"
171 # define HERE_T_FMT_AMPM "%I:%M:%S %p"
172 # define HERE_T_FMT "%H:%M:%S"
174 static const unsigned short int __mon_yday[2][13] =
176 /* Normal years. */
177 { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 },
178 /* Leap years. */
179 { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 }
181 #endif
183 #if defined _LIBC
184 /* We use this code also for the extended locale handling where the
185 function gets as an additional argument the locale which has to be
186 used. To access the values we have to redefine the _NL_CURRENT
187 macro. */
188 # define strptime __strptime_l
189 # undef _NL_CURRENT
190 # define _NL_CURRENT(category, item) \
191 (current->values[_NL_ITEM_INDEX (item)].string)
192 # undef _NL_CURRENT_WORD
193 # define _NL_CURRENT_WORD(category, item) \
194 (current->values[_NL_ITEM_INDEX (item)].word)
195 # define LOCALE_PARAM , locale
196 # define LOCALE_ARG , locale
197 # define LOCALE_PARAM_PROTO , __locale_t locale
198 # define LOCALE_PARAM_DECL __locale_t locale;
199 # define HELPER_LOCALE_ARG , current
200 # define ISSPACE(Ch) __isspace_l (Ch, locale)
201 #else
202 # define LOCALE_PARAM
203 # define LOCALE_ARG
204 # define LOCALE_PARAM_DECL
205 # define LOCALE_PARAM_PROTO
206 # define HELPER_LOCALE_ARG
207 # define ISSPACE(Ch) isspace (Ch)
208 #endif
213 #ifndef __isleap
214 /* Nonzero if YEAR is a leap year (every 4 years,
215 except every 100th isn't, and every 400th is). */
216 # define __isleap(year) \
217 ((year) % 4 == 0 && ((year) % 100 != 0 || (year) % 400 == 0))
218 #endif
220 /* Compute the day of the week. */
221 static void
222 day_of_the_week (struct tm *tm)
224 /* We know that January 1st 1970 was a Thursday (= 4). Compute the
225 the difference between this data in the one on TM and so determine
226 the weekday. */
227 int corr_year = 1900 + tm->tm_year - (tm->tm_mon < 2);
228 int wday = (-473
229 + (365 * (tm->tm_year - 70))
230 + (corr_year / 4)
231 - ((corr_year / 4) / 25) + ((corr_year / 4) % 25 < 0)
232 + (((corr_year / 4) / 25) / 4)
233 + __mon_yday[0][tm->tm_mon]
234 + tm->tm_mday - 1);
235 tm->tm_wday = ((wday % 7) + 7) % 7;
238 /* Compute the day of the year. */
239 static void
240 day_of_the_year (struct tm *tm)
242 tm->tm_yday = (__mon_yday[__isleap (1900 + tm->tm_year)][tm->tm_mon]
243 + (tm->tm_mday - 1));
247 #ifdef _LIBC
248 char *
249 internal_function
250 #else
251 static char *
252 #endif
253 __strptime_internal (rp, fmt, tmp, statep LOCALE_PARAM)
254 const char *rp;
255 const char *fmt;
256 struct tm *tmp;
257 void *statep;
258 LOCALE_PARAM_DECL
260 #ifdef _LIBC
261 struct locale_data *const current = locale->__locales[LC_TIME];
262 #endif
264 const char *rp_backup;
265 const char *rp_longest;
266 int cnt;
267 int cnt_longest;
268 size_t val;
269 size_t num_eras;
270 struct era_entry *era = NULL;
271 enum ptime_locale_status { not, loc, raw } decided_longest;
272 struct __strptime_state
274 unsigned int have_I : 1;
275 unsigned int have_wday : 1;
276 unsigned int have_yday : 1;
277 unsigned int have_mon : 1;
278 unsigned int have_mday : 1;
279 unsigned int have_uweek : 1;
280 unsigned int have_wweek : 1;
281 unsigned int is_pm : 1;
282 unsigned int want_century : 1;
283 unsigned int want_era : 1;
284 unsigned int want_xday : 1;
285 enum ptime_locale_status decided : 2;
286 signed char week_no;
287 signed char century;
288 int era_cnt;
289 } s;
290 struct tm tmb;
291 struct tm *tm;
293 if (statep == NULL)
295 memset (&s, 0, sizeof (s));
296 s.century = -1;
297 s.era_cnt = -1;
298 #ifdef _NL_CURRENT
299 s.decided = not;
300 #else
301 s.decided = raw;
302 #endif
303 tm = tmp;
305 else
307 s = *(struct __strptime_state *) statep;
308 tmb = *tmp;
309 tm = &tmb;
312 while (*fmt != '\0')
314 /* A white space in the format string matches 0 more or white
315 space in the input string. */
316 if (ISSPACE (*fmt))
318 while (ISSPACE (*rp))
319 ++rp;
320 ++fmt;
321 continue;
324 /* Any character but `%' must be matched by the same character
325 in the iput string. */
326 if (*fmt != '%')
328 match_char (*fmt++, *rp++);
329 continue;
332 ++fmt;
333 if (statep != NULL)
335 /* In recursive calls silently discard strftime modifiers. */
336 while (*fmt == '-' || *fmt == '_' || *fmt == '0'
337 || *fmt == '^' || *fmt == '#')
338 ++fmt;
340 /* And field width. */
341 while (*fmt >= '0' && *fmt <= '9')
342 ++fmt;
345 #ifndef _NL_CURRENT
346 /* We need this for handling the `E' modifier. */
347 start_over:
348 #endif
350 /* Make back up of current processing pointer. */
351 rp_backup = rp;
353 switch (*fmt++)
355 case '%':
356 /* Match the `%' character itself. */
357 match_char ('%', *rp++);
358 break;
359 case 'a':
360 case 'A':
361 /* Match day of week. */
362 rp_longest = NULL;
363 decided_longest = s.decided;
364 cnt_longest = -1;
365 for (cnt = 0; cnt < 7; ++cnt)
367 const char *trp;
368 #ifdef _NL_CURRENT
369 if (s.decided !=raw)
371 trp = rp;
372 if (match_string (_NL_CURRENT (LC_TIME, DAY_1 + cnt), trp)
373 && trp > rp_longest)
375 rp_longest = trp;
376 cnt_longest = cnt;
377 if (s.decided == not
378 && strcmp (_NL_CURRENT (LC_TIME, DAY_1 + cnt),
379 weekday_name[cnt]))
380 decided_longest = loc;
382 trp = rp;
383 if (match_string (_NL_CURRENT (LC_TIME, ABDAY_1 + cnt), trp)
384 && trp > rp_longest)
386 rp_longest = trp;
387 cnt_longest = cnt;
388 if (s.decided == not
389 && strcmp (_NL_CURRENT (LC_TIME, ABDAY_1 + cnt),
390 ab_weekday_name[cnt]))
391 decided_longest = loc;
394 #endif
395 if (s.decided != loc
396 && (((trp = rp, match_string (weekday_name[cnt], trp))
397 && trp > rp_longest)
398 || ((trp = rp, match_string (ab_weekday_name[cnt], rp))
399 && trp > rp_longest)))
401 rp_longest = trp;
402 cnt_longest = cnt;
403 decided_longest = raw;
406 if (rp_longest == NULL)
407 /* Does not match a weekday name. */
408 return NULL;
409 rp = rp_longest;
410 s.decided = decided_longest;
411 tm->tm_wday = cnt_longest;
412 s.have_wday = 1;
413 break;
414 case 'b':
415 case 'B':
416 case 'h':
417 /* Match month name. */
418 rp_longest = NULL;
419 decided_longest = s.decided;
420 cnt_longest = -1;
421 for (cnt = 0; cnt < 12; ++cnt)
423 const char *trp;
424 #ifdef _NL_CURRENT
425 if (s.decided !=raw)
427 trp = rp;
428 if (match_string (_NL_CURRENT (LC_TIME, MON_1 + cnt), trp)
429 && trp > rp_longest)
431 rp_longest = trp;
432 cnt_longest = cnt;
433 if (s.decided == not
434 && strcmp (_NL_CURRENT (LC_TIME, MON_1 + cnt),
435 month_name[cnt]))
436 decided_longest = loc;
438 trp = rp;
439 if (match_string (_NL_CURRENT (LC_TIME, ABMON_1 + cnt), trp)
440 && trp > rp_longest)
442 rp_longest = trp;
443 cnt_longest = cnt;
444 if (s.decided == not
445 && strcmp (_NL_CURRENT (LC_TIME, ABMON_1 + cnt),
446 ab_month_name[cnt]))
447 decided_longest = loc;
450 #endif
451 if (s.decided != loc
452 && (((trp = rp, match_string (month_name[cnt], trp))
453 && trp > rp_longest)
454 || ((trp = rp, match_string (ab_month_name[cnt], trp))
455 && trp > rp_longest)))
457 rp_longest = trp;
458 cnt_longest = cnt;
459 decided_longest = raw;
462 if (rp_longest == NULL)
463 /* Does not match a month name. */
464 return NULL;
465 rp = rp_longest;
466 s.decided = decided_longest;
467 tm->tm_mon = cnt_longest;
468 s.have_mon = 1;
469 s.want_xday = 1;
470 break;
471 case 'c':
472 /* Match locale's date and time format. */
473 #ifdef _NL_CURRENT
474 if (s.decided != raw)
476 if (!recursive (_NL_CURRENT (LC_TIME, D_T_FMT)))
478 if (s.decided == loc)
479 return NULL;
480 else
481 rp = rp_backup;
483 else
485 if (s.decided == not &&
486 strcmp (_NL_CURRENT (LC_TIME, D_T_FMT), HERE_D_T_FMT))
487 s.decided = loc;
488 s.want_xday = 1;
489 break;
491 s.decided = raw;
493 #endif
494 if (!recursive (HERE_D_T_FMT))
495 return NULL;
496 s.want_xday = 1;
497 break;
498 case 'C':
499 /* Match century number. */
500 match_century:
501 get_number (0, 99, 2);
502 s.century = val;
503 s.want_xday = 1;
504 break;
505 case 'd':
506 case 'e':
507 /* Match day of month. */
508 get_number (1, 31, 2);
509 tm->tm_mday = val;
510 s.have_mday = 1;
511 s.want_xday = 1;
512 break;
513 case 'F':
514 if (!recursive ("%Y-%m-%d"))
515 return NULL;
516 s.want_xday = 1;
517 break;
518 case 'x':
519 #ifdef _NL_CURRENT
520 if (s.decided != raw)
522 if (!recursive (_NL_CURRENT (LC_TIME, D_FMT)))
524 if (s.decided == loc)
525 return NULL;
526 else
527 rp = rp_backup;
529 else
531 if (s.decided == not
532 && strcmp (_NL_CURRENT (LC_TIME, D_FMT), HERE_D_FMT))
533 s.decided = loc;
534 s.want_xday = 1;
535 break;
537 s.decided = raw;
539 #endif
540 /* Fall through. */
541 case 'D':
542 /* Match standard day format. */
543 if (!recursive (HERE_D_FMT))
544 return NULL;
545 s.want_xday = 1;
546 break;
547 case 'k':
548 case 'H':
549 /* Match hour in 24-hour clock. */
550 get_number (0, 23, 2);
551 tm->tm_hour = val;
552 s.have_I = 0;
553 break;
554 case 'l':
555 /* Match hour in 12-hour clock. GNU extension. */
556 case 'I':
557 /* Match hour in 12-hour clock. */
558 get_number (1, 12, 2);
559 tm->tm_hour = val % 12;
560 s.have_I = 1;
561 break;
562 case 'j':
563 /* Match day number of year. */
564 get_number (1, 366, 3);
565 tm->tm_yday = val - 1;
566 s.have_yday = 1;
567 break;
568 case 'm':
569 /* Match number of month. */
570 get_number (1, 12, 2);
571 tm->tm_mon = val - 1;
572 s.have_mon = 1;
573 s.want_xday = 1;
574 break;
575 case 'M':
576 /* Match minute. */
577 get_number (0, 59, 2);
578 tm->tm_min = val;
579 break;
580 case 'n':
581 case 't':
582 /* Match any white space. */
583 while (ISSPACE (*rp))
584 ++rp;
585 break;
586 case 'p':
587 /* Match locale's equivalent of AM/PM. */
588 #ifdef _NL_CURRENT
589 if (s.decided != raw)
591 if (match_string (_NL_CURRENT (LC_TIME, AM_STR), rp))
593 if (strcmp (_NL_CURRENT (LC_TIME, AM_STR), HERE_AM_STR))
594 s.decided = loc;
595 s.is_pm = 0;
596 break;
598 if (match_string (_NL_CURRENT (LC_TIME, PM_STR), rp))
600 if (strcmp (_NL_CURRENT (LC_TIME, PM_STR), HERE_PM_STR))
601 s.decided = loc;
602 s.is_pm = 1;
603 break;
605 s.decided = raw;
607 #endif
608 if (!match_string (HERE_AM_STR, rp))
610 if (match_string (HERE_PM_STR, rp))
611 s.is_pm = 1;
612 else
613 return NULL;
615 else
616 s.is_pm = 0;
617 break;
618 case 'r':
619 #ifdef _NL_CURRENT
620 if (s.decided != raw)
622 if (!recursive (_NL_CURRENT (LC_TIME, T_FMT_AMPM)))
624 if (s.decided == loc)
625 return NULL;
626 else
627 rp = rp_backup;
629 else
631 if (s.decided == not &&
632 strcmp (_NL_CURRENT (LC_TIME, T_FMT_AMPM),
633 HERE_T_FMT_AMPM))
634 s.decided = loc;
635 break;
637 s.decided = raw;
639 #endif
640 if (!recursive (HERE_T_FMT_AMPM))
641 return NULL;
642 break;
643 case 'R':
644 if (!recursive ("%H:%M"))
645 return NULL;
646 break;
647 case 's':
649 /* The number of seconds may be very high so we cannot use
650 the `get_number' macro. Instead read the number
651 character for character and construct the result while
652 doing this. */
653 time_t secs = 0;
654 if (*rp < '0' || *rp > '9')
655 /* We need at least one digit. */
656 return NULL;
660 secs *= 10;
661 secs += *rp++ - '0';
663 while (*rp >= '0' && *rp <= '9');
665 if (localtime_r (&secs, tm) == NULL)
666 /* Error in function. */
667 return NULL;
669 break;
670 case 'S':
671 get_number (0, 61, 2);
672 tm->tm_sec = val;
673 break;
674 case 'X':
675 #ifdef _NL_CURRENT
676 if (s.decided != raw)
678 if (!recursive (_NL_CURRENT (LC_TIME, T_FMT)))
680 if (s.decided == loc)
681 return NULL;
682 else
683 rp = rp_backup;
685 else
687 if (strcmp (_NL_CURRENT (LC_TIME, T_FMT), HERE_T_FMT))
688 s.decided = loc;
689 break;
691 s.decided = raw;
693 #endif
694 /* Fall through. */
695 case 'T':
696 if (!recursive (HERE_T_FMT))
697 return NULL;
698 break;
699 case 'u':
700 get_number (1, 7, 1);
701 tm->tm_wday = val % 7;
702 s.have_wday = 1;
703 break;
704 case 'g':
705 get_number (0, 99, 2);
706 /* XXX This cannot determine any field in TM. */
707 break;
708 case 'G':
709 if (*rp < '0' || *rp > '9')
710 return NULL;
711 /* XXX Ignore the number since we would need some more
712 information to compute a real date. */
714 ++rp;
715 while (*rp >= '0' && *rp <= '9');
716 break;
717 case 'U':
718 get_number (0, 53, 2);
719 s.week_no = val;
720 s.have_uweek = 1;
721 break;
722 case 'W':
723 get_number (0, 53, 2);
724 s.week_no = val;
725 s.have_wweek = 1;
726 break;
727 case 'V':
728 get_number (0, 53, 2);
729 /* XXX This cannot determine any field in TM without some
730 information. */
731 break;
732 case 'w':
733 /* Match number of weekday. */
734 get_number (0, 6, 1);
735 tm->tm_wday = val;
736 s.have_wday = 1;
737 break;
738 case 'y':
739 match_year_in_century:
740 /* Match year within century. */
741 get_number (0, 99, 2);
742 /* The "Year 2000: The Millennium Rollover" paper suggests that
743 values in the range 69-99 refer to the twentieth century. */
744 tm->tm_year = val >= 69 ? val : val + 100;
745 /* Indicate that we want to use the century, if specified. */
746 s.want_century = 1;
747 s.want_xday = 1;
748 break;
749 case 'Y':
750 /* Match year including century number. */
751 get_number (0, 9999, 4);
752 tm->tm_year = val - 1900;
753 s.want_century = 0;
754 s.want_xday = 1;
755 break;
756 case 'Z':
757 /* XXX How to handle this? */
758 break;
759 case 'z':
760 /* We recognize two formats: if two digits are given, these
761 specify hours. If fours digits are used, minutes are
762 also specified. */
764 val = 0;
765 while (*rp == ' ')
766 ++rp;
767 if (*rp != '+' && *rp != '-')
768 return NULL;
769 bool neg = *rp++ == '-';
770 int n = 0;
771 while (n < 4 && *rp >= '0' && *rp <= '9')
773 val = val * 10 + *rp++ - '0';
774 ++n;
776 if (n == 2)
777 val *= 100;
778 else if (n != 4)
779 /* Only two or four digits recognized. */
780 return NULL;
781 else
783 /* We have to convert the minutes into decimal. */
784 if (val % 100 >= 60)
785 return NULL;
786 val = (val / 100) * 100 + ((val % 100) * 50) / 30;
788 if (val > 1200)
789 return NULL;
790 tm->tm_gmtoff = (val * 3600) / 100;
791 if (neg)
792 tm->tm_gmtoff = -tm->tm_gmtoff;
794 break;
795 case 'E':
796 #ifdef _NL_CURRENT
797 switch (*fmt++)
799 case 'c':
800 /* Match locale's alternate date and time format. */
801 if (s.decided != raw)
803 const char *fmt = _NL_CURRENT (LC_TIME, ERA_D_T_FMT);
805 if (*fmt == '\0')
806 fmt = _NL_CURRENT (LC_TIME, D_T_FMT);
808 if (!recursive (fmt))
810 if (s.decided == loc)
811 return NULL;
812 else
813 rp = rp_backup;
815 else
817 if (strcmp (fmt, HERE_D_T_FMT))
818 s.decided = loc;
819 s.want_xday = 1;
820 break;
822 s.decided = raw;
824 /* The C locale has no era information, so use the
825 normal representation. */
826 if (!recursive (HERE_D_T_FMT))
827 return NULL;
828 s.want_xday = 1;
829 break;
830 case 'C':
831 if (s.decided != raw)
833 if (s.era_cnt >= 0)
835 era = _nl_select_era_entry (s.era_cnt HELPER_LOCALE_ARG);
836 if (era != NULL && match_string (era->era_name, rp))
838 s.decided = loc;
839 break;
841 else
842 return NULL;
845 num_eras = _NL_CURRENT_WORD (LC_TIME,
846 _NL_TIME_ERA_NUM_ENTRIES);
847 for (s.era_cnt = 0; s.era_cnt < (int) num_eras;
848 ++s.era_cnt, rp = rp_backup)
850 era = _nl_select_era_entry (s.era_cnt
851 HELPER_LOCALE_ARG);
852 if (era != NULL && match_string (era->era_name, rp))
854 s.decided = loc;
855 break;
858 if (s.era_cnt != (int) num_eras)
859 break;
861 s.era_cnt = -1;
862 if (s.decided == loc)
863 return NULL;
865 s.decided = raw;
867 /* The C locale has no era information, so use the
868 normal representation. */
869 goto match_century;
870 case 'y':
871 if (s.decided != raw)
873 get_number(0, 9999, 4);
874 tm->tm_year = val;
875 s.want_era = 1;
876 s.want_xday = 1;
877 s.want_century = 1;
879 if (s.era_cnt >= 0)
881 assert (s.decided == loc);
883 era = _nl_select_era_entry (s.era_cnt HELPER_LOCALE_ARG);
884 bool match = false;
885 if (era != NULL)
887 int delta = ((tm->tm_year - era->offset)
888 * era->absolute_direction);
889 match = (delta >= 0
890 && delta < (((int64_t) era->stop_date[0]
891 - (int64_t) era->start_date[0])
892 * era->absolute_direction));
894 if (! match)
895 return NULL;
897 break;
900 num_eras = _NL_CURRENT_WORD (LC_TIME,
901 _NL_TIME_ERA_NUM_ENTRIES);
902 for (s.era_cnt = 0; s.era_cnt < (int) num_eras; ++s.era_cnt)
904 era = _nl_select_era_entry (s.era_cnt
905 HELPER_LOCALE_ARG);
906 if (era != NULL)
908 int delta = ((tm->tm_year - era->offset)
909 * era->absolute_direction);
910 if (delta >= 0
911 && delta < (((int64_t) era->stop_date[0]
912 - (int64_t) era->start_date[0])
913 * era->absolute_direction))
915 s.decided = loc;
916 break;
920 if (s.era_cnt != (int) num_eras)
921 break;
923 s.era_cnt = -1;
924 if (s.decided == loc)
925 return NULL;
927 s.decided = raw;
930 goto match_year_in_century;
931 case 'Y':
932 if (s.decided != raw)
934 num_eras = _NL_CURRENT_WORD (LC_TIME,
935 _NL_TIME_ERA_NUM_ENTRIES);
936 for (s.era_cnt = 0; s.era_cnt < (int) num_eras;
937 ++s.era_cnt, rp = rp_backup)
939 era = _nl_select_era_entry (s.era_cnt HELPER_LOCALE_ARG);
940 if (era != NULL && recursive (era->era_format))
941 break;
943 if (s.era_cnt == (int) num_eras)
945 s.era_cnt = -1;
946 if (s.decided == loc)
947 return NULL;
948 else
949 rp = rp_backup;
951 else
953 s.decided = loc;
954 s.era_cnt = -1;
955 break;
958 s.decided = raw;
960 get_number (0, 9999, 4);
961 tm->tm_year = val - 1900;
962 s.want_century = 0;
963 s.want_xday = 1;
964 break;
965 case 'x':
966 if (s.decided != raw)
968 const char *fmt = _NL_CURRENT (LC_TIME, ERA_D_FMT);
970 if (*fmt == '\0')
971 fmt = _NL_CURRENT (LC_TIME, D_FMT);
973 if (!recursive (fmt))
975 if (s.decided == loc)
976 return NULL;
977 else
978 rp = rp_backup;
980 else
982 if (strcmp (fmt, HERE_D_FMT))
983 s.decided = loc;
984 break;
986 s.decided = raw;
988 if (!recursive (HERE_D_FMT))
989 return NULL;
990 break;
991 case 'X':
992 if (s.decided != raw)
994 const char *fmt = _NL_CURRENT (LC_TIME, ERA_T_FMT);
996 if (*fmt == '\0')
997 fmt = _NL_CURRENT (LC_TIME, T_FMT);
999 if (!recursive (fmt))
1001 if (s.decided == loc)
1002 return NULL;
1003 else
1004 rp = rp_backup;
1006 else
1008 if (strcmp (fmt, HERE_T_FMT))
1009 s.decided = loc;
1010 break;
1012 s.decided = raw;
1014 if (!recursive (HERE_T_FMT))
1015 return NULL;
1016 break;
1017 default:
1018 return NULL;
1020 break;
1021 #else
1022 /* We have no information about the era format. Just use
1023 the normal format. */
1024 if (*fmt != 'c' && *fmt != 'C' && *fmt != 'y' && *fmt != 'Y'
1025 && *fmt != 'x' && *fmt != 'X')
1026 /* This is an illegal format. */
1027 return NULL;
1029 goto start_over;
1030 #endif
1031 case 'O':
1032 switch (*fmt++)
1034 case 'd':
1035 case 'e':
1036 /* Match day of month using alternate numeric symbols. */
1037 get_alt_number (1, 31, 2);
1038 tm->tm_mday = val;
1039 s.have_mday = 1;
1040 s.want_xday = 1;
1041 break;
1042 case 'H':
1043 /* Match hour in 24-hour clock using alternate numeric
1044 symbols. */
1045 get_alt_number (0, 23, 2);
1046 tm->tm_hour = val;
1047 s.have_I = 0;
1048 break;
1049 case 'I':
1050 /* Match hour in 12-hour clock using alternate numeric
1051 symbols. */
1052 get_alt_number (1, 12, 2);
1053 tm->tm_hour = val % 12;
1054 s.have_I = 1;
1055 break;
1056 case 'm':
1057 /* Match month using alternate numeric symbols. */
1058 get_alt_number (1, 12, 2);
1059 tm->tm_mon = val - 1;
1060 s.have_mon = 1;
1061 s.want_xday = 1;
1062 break;
1063 case 'M':
1064 /* Match minutes using alternate numeric symbols. */
1065 get_alt_number (0, 59, 2);
1066 tm->tm_min = val;
1067 break;
1068 case 'S':
1069 /* Match seconds using alternate numeric symbols. */
1070 get_alt_number (0, 61, 2);
1071 tm->tm_sec = val;
1072 break;
1073 case 'U':
1074 get_alt_number (0, 53, 2);
1075 s.week_no = val;
1076 s.have_uweek = 1;
1077 break;
1078 case 'W':
1079 get_alt_number (0, 53, 2);
1080 s.week_no = val;
1081 s.have_wweek = 1;
1082 break;
1083 case 'V':
1084 get_alt_number (0, 53, 2);
1085 /* XXX This cannot determine any field in TM without
1086 further information. */
1087 break;
1088 case 'w':
1089 /* Match number of weekday using alternate numeric symbols. */
1090 get_alt_number (0, 6, 1);
1091 tm->tm_wday = val;
1092 s.have_wday = 1;
1093 break;
1094 case 'y':
1095 /* Match year within century using alternate numeric symbols. */
1096 get_alt_number (0, 99, 2);
1097 tm->tm_year = val >= 69 ? val : val + 100;
1098 s.want_xday = 1;
1099 break;
1100 default:
1101 return NULL;
1103 break;
1104 default:
1105 return NULL;
1109 if (statep != NULL)
1111 /* Recursive invocation, returning success, so
1112 update parent's struct tm and state. */
1113 *(struct __strptime_state *) statep = s;
1114 *tmp = tmb;
1115 return (char *) rp;
1118 if (s.have_I && s.is_pm)
1119 tm->tm_hour += 12;
1121 if (s.century != -1)
1123 if (s.want_century)
1124 tm->tm_year = tm->tm_year % 100 + (s.century - 19) * 100;
1125 else
1126 /* Only the century, but not the year. Strange, but so be it. */
1127 tm->tm_year = (s.century - 19) * 100;
1130 if (s.era_cnt != -1)
1132 era = _nl_select_era_entry (s.era_cnt HELPER_LOCALE_ARG);
1133 if (era == NULL)
1134 return NULL;
1135 if (s.want_era)
1136 tm->tm_year = (era->start_date[0]
1137 + ((tm->tm_year - era->offset)
1138 * era->absolute_direction));
1139 else
1140 /* Era start year assumed. */
1141 tm->tm_year = era->start_date[0];
1143 else
1144 if (s.want_era)
1146 /* No era found but we have seen an E modifier. Rectify some
1147 values. */
1148 if (s.want_century && s.century == -1 && tm->tm_year < 69)
1149 tm->tm_year += 100;
1152 if (s.want_xday && !s.have_wday)
1154 if ( !(s.have_mon && s.have_mday) && s.have_yday)
1156 /* We don't have tm_mon and/or tm_mday, compute them. */
1157 int t_mon = 0;
1158 while (__mon_yday[__isleap(1900 + tm->tm_year)][t_mon] <= tm->tm_yday)
1159 t_mon++;
1160 if (!s.have_mon)
1161 tm->tm_mon = t_mon - 1;
1162 if (!s.have_mday)
1163 tm->tm_mday =
1164 (tm->tm_yday
1165 - __mon_yday[__isleap(1900 + tm->tm_year)][t_mon - 1] + 1);
1166 s.have_mon = 1;
1167 s.have_mday = 1;
1169 /* Don't crash in day_of_the_week if tm_mon is uninitialized. */
1170 if (s.have_mon || (unsigned) tm->tm_mon <= 11)
1171 day_of_the_week (tm);
1174 if (s.want_xday && !s.have_yday && (s.have_mon || (unsigned) tm->tm_mon <= 11))
1175 day_of_the_year (tm);
1177 if ((s.have_uweek || s.have_wweek) && s.have_wday)
1179 int save_wday = tm->tm_wday;
1180 int save_mday = tm->tm_mday;
1181 int save_mon = tm->tm_mon;
1182 int w_offset = s.have_uweek ? 0 : 1;
1184 tm->tm_mday = 1;
1185 tm->tm_mon = 0;
1186 day_of_the_week (tm);
1187 if (s.have_mday)
1188 tm->tm_mday = save_mday;
1189 if (s.have_mon)
1190 tm->tm_mon = save_mon;
1192 if (!s.have_yday)
1193 tm->tm_yday = ((7 - (tm->tm_wday - w_offset)) % 7
1194 + (s.week_no - 1) *7
1195 + save_wday - w_offset);
1197 if (!s.have_mday || !s.have_mon)
1199 int t_mon = 0;
1200 while (__mon_yday[__isleap(1900 + tm->tm_year)][t_mon]
1201 <= tm->tm_yday)
1202 t_mon++;
1203 if (!s.have_mon)
1204 tm->tm_mon = t_mon - 1;
1205 if (!s.have_mday)
1206 tm->tm_mday =
1207 (tm->tm_yday
1208 - __mon_yday[__isleap(1900 + tm->tm_year)][t_mon - 1] + 1);
1211 tm->tm_wday = save_wday;
1214 return (char *) rp;
1218 char *
1219 strptime (buf, format, tm LOCALE_PARAM)
1220 const char *buf;
1221 const char *format;
1222 struct tm *tm;
1223 LOCALE_PARAM_DECL
1225 return __strptime_internal (buf, format, tm, NULL LOCALE_ARG);
1228 #ifdef _LIBC
1229 weak_alias (__strptime_l, strptime_l)
1230 #endif