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