Correctly determine era information for wide wcsftime.
[kopensolaris-gnu/glibc.git] / time / strptime.c
1 /* Convert a string representation of time to a time value.
2    Copyright (C) 1996, 1997, 1998, 1999 Free Software Foundation, Inc.
3    This file is part of the GNU C Library.
4    Contributed by Ulrich Drepper <drepper@cygnus.com>, 1996.
5
6    The GNU C Library is free software; you can redistribute it and/or
7    modify it under the terms of the GNU Library General Public License as
8    published by the Free Software Foundation; either version 2 of the
9    License, or (at your option) any later version.
10
11    The GNU C Library is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14    Library General Public License for more details.
15
16    You should have received a copy of the GNU Library General Public
17    License along with the GNU C Library; see the file COPYING.LIB.  If not,
18    write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19    Boston, MA 02111-1307, USA.  */
20
21 /* XXX This version of the implementation is not really complete.
22    Some of the fields cannot add information alone.  But if seeing
23    some of them in the same format (such as year, week and weekday)
24    this is enough information for determining the date.  */
25
26 #ifdef HAVE_CONFIG_H
27 # include <config.h>
28 #endif
29
30 #include <ctype.h>
31 #include <langinfo.h>
32 #include <limits.h>
33 #include <string.h>
34 #include <time.h>
35
36 #ifdef _LIBC
37 # include "../locale/localeinfo.h"
38 #endif
39
40
41 #ifndef __P
42 # if defined (__GNUC__) || (defined (__STDC__) && __STDC__)
43 #  define __P(args) args
44 # else
45 #  define __P(args) ()
46 # endif  /* GCC.  */
47 #endif  /* Not __P.  */
48
49
50 #if ! HAVE_LOCALTIME_R && ! defined localtime_r
51 # ifdef _LIBC
52 #  define localtime_r __localtime_r
53 # else
54 /* Approximate localtime_r as best we can in its absence.  */
55 #  define localtime_r my_localtime_r
56 static struct tm *localtime_r __P ((const time_t *, struct tm *));
57 static struct tm *
58 localtime_r (t, tp)
59      const time_t *t;
60      struct tm *tp;
61 {
62   struct tm *l = localtime (t);
63   if (! l)
64     return 0;
65   *tp = *l;
66   return tp;
67 }
68 # endif /* ! _LIBC */
69 #endif /* ! HAVE_LOCALTIME_R && ! defined (localtime_r) */
70
71
72 #define match_char(ch1, ch2) if (ch1 != ch2) return NULL
73 #if defined __GNUC__ && __GNUC__ >= 2
74 # define match_string(cs1, s2) \
75   ({ size_t len = strlen (cs1);                                               \
76      int result = strncasecmp ((cs1), (s2), len) == 0;                        \
77      if (result) (s2) += len;                                                 \
78      result; })
79 #else
80 /* Oh come on.  Get a reasonable compiler.  */
81 # define match_string(cs1, s2) \
82   (strncasecmp ((cs1), (s2), strlen (cs1)) ? 0 : ((s2) += strlen (cs1), 1))
83 #endif
84 /* We intentionally do not use isdigit() for testing because this will
85    lead to problems with the wide character version.  */
86 #define get_number(from, to, n) \
87   do {                                                                        \
88     int __n = n;                                                              \
89     val = 0;                                                                  \
90     while (*rp == ' ')                                                        \
91       ++rp;                                                                   \
92     if (*rp < '0' || *rp > '9')                                               \
93       return NULL;                                                            \
94     do {                                                                      \
95       val *= 10;                                                              \
96       val += *rp++ - '0';                                                     \
97     } while (--__n > 0 && val * 10 <= to && *rp >= '0' && *rp <= '9');        \
98     if (val < from || val > to)                                               \
99       return NULL;                                                            \
100   } while (0)
101 #ifdef _NL_CURRENT
102 # define get_alt_number(from, to, n) \
103   ({                                                                          \
104     __label__ do_normal;                                                      \
105     if (*decided != raw)                                                      \
106       {                                                                       \
107         const char *alts = _NL_CURRENT (LC_TIME, ALT_DIGITS);                 \
108         int __n = n;                                                          \
109         int any = 0;                                                          \
110         while (*rp == ' ')                                                    \
111           ++rp;                                                               \
112         val = 0;                                                              \
113         do {                                                                  \
114           val *= 10;                                                          \
115           while (*alts != '\0')                                               \
116             {                                                                 \
117               size_t len = strlen (alts);                                     \
118               if (strncasecmp (alts, rp, len) == 0)                           \
119                 break;                                                        \
120               alts += len + 1;                                                \
121               ++val;                                                          \
122             }                                                                 \
123           if (*alts == '\0')                                                  \
124             {                                                                 \
125               if (*decided == not && ! any)                                   \
126                 goto do_normal;                                               \
127               /* If we haven't read anything it's an error.  */               \
128               if (! any)                                                      \
129                 return NULL;                                                  \
130               /* Correct the premature multiplication.  */                    \
131               val /= 10;                                                      \
132               break;                                                          \
133             }                                                                 \
134           else                                                                \
135             *decided = loc;                                                   \
136         } while (--__n > 0 && val * 10 <= to);                                \
137         if (val < from || val > to)                                           \
138           return NULL;                                                        \
139       }                                                                       \
140     else                                                                      \
141       {                                                                       \
142        do_normal:                                                             \
143         get_number (from, to, n);                                             \
144       }                                                                       \
145     0;                                                                        \
146   })
147 #else
148 # define get_alt_number(from, to, n) \
149   /* We don't have the alternate representation.  */                          \
150   get_number(from, to, n)
151 #endif
152 #define recursive(new_fmt) \
153   (*(new_fmt) != '\0'                                                         \
154    && (rp = strptime_internal (rp, (new_fmt), tm, decided)) != NULL)
155
156
157 #ifdef _LIBC
158 /* This is defined in locale/C-time.c in the GNU libc.  */
159 extern const struct locale_data _nl_C_LC_TIME;
160 extern const unsigned short int __mon_yday[2][13];
161
162 # define weekday_name (&_nl_C_LC_TIME.values[_NL_ITEM_INDEX (DAY_1)].string)
163 # define ab_weekday_name \
164   (&_nl_C_LC_TIME.values[_NL_ITEM_INDEX (ABDAY_1)].string)
165 # define month_name (&_nl_C_LC_TIME.values[_NL_ITEM_INDEX (MON_1)].string)
166 # define ab_month_name (&_nl_C_LC_TIME.values[_NL_ITEM_INDEX (ABMON_1)].string)
167 # define HERE_D_T_FMT (_nl_C_LC_TIME.values[_NL_ITEM_INDEX (D_T_FMT)].string)
168 # define HERE_D_FMT (_nl_C_LC_TIME.values[_NL_ITEM_INDEX (D_FMT)].string)
169 # define HERE_AM_STR (_nl_C_LC_TIME.values[_NL_ITEM_INDEX (AM_STR)].string)
170 # define HERE_PM_STR (_nl_C_LC_TIME.values[_NL_ITEM_INDEX (PM_STR)].string)
171 # define HERE_T_FMT_AMPM \
172   (_nl_C_LC_TIME.values[_NL_ITEM_INDEX (T_FMT_AMPM)].string)
173 # define HERE_T_FMT (_nl_C_LC_TIME.values[_NL_ITEM_INDEX (T_FMT)].string)
174
175 # define strncasecmp(s1, s2, n) __strncasecmp (s1, s2, n)
176 #else
177 static char const weekday_name[][10] =
178   {
179     "Sunday", "Monday", "Tuesday", "Wednesday",
180     "Thursday", "Friday", "Saturday"
181   };
182 static char const ab_weekday_name[][4] =
183   {
184     "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
185   };
186 static char const month_name[][10] =
187   {
188     "January", "February", "March", "April", "May", "June",
189     "July", "August", "September", "October", "November", "December"
190   };
191 static char const ab_month_name[][4] =
192   {
193     "Jan", "Feb", "Mar", "Apr", "May", "Jun",
194     "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
195   };
196 # define HERE_D_T_FMT "%a %b %e %H:%M:%S %Y"
197 # define HERE_D_FMT "%m/%d/%y"
198 # define HERE_AM_STR "AM"
199 # define HERE_PM_STR "PM"
200 # define HERE_T_FMT_AMPM "%I:%M:%S %p"
201 # define HERE_T_FMT "%H:%M:%S"
202
203 const unsigned short int __mon_yday[2][13] =
204   {
205     /* Normal years.  */
206     { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 },
207     /* Leap years.  */
208     { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 }
209   };
210 #endif
211
212 /* Status of lookup: do we use the locale data or the raw data?  */
213 enum locale_status { not, loc, raw };
214
215
216 #ifndef __isleap
217 /* Nonzero if YEAR is a leap year (every 4 years,
218    except every 100th isn't, and every 400th is).  */
219 # define __isleap(year) \
220   ((year) % 4 == 0 && ((year) % 100 != 0 || (year) % 400 == 0))
221 #endif
222
223 /* Compute the day of the week.  */
224 static void
225 day_of_the_week (struct tm *tm)
226 {
227   /* We know that January 1st 1970 was a Thursday (= 4).  Compute the
228      the difference between this data in the one on TM and so determine
229      the weekday.  */
230   int corr_year = 1900 + tm->tm_year - (tm->tm_mon < 2);
231   int wday = (-473
232               + (365 * (tm->tm_year - 70))
233               + (corr_year / 4)
234               - ((corr_year / 4) / 25) + ((corr_year / 4) % 25 < 0)
235               + (((corr_year / 4) / 25) / 4)
236               + __mon_yday[0][tm->tm_mon]
237               + tm->tm_mday - 1);
238   tm->tm_wday = wday % 7;
239 }
240
241 /* Compute the day of the year.  */
242 static void
243 day_of_the_year (struct tm *tm)
244 {
245   tm->tm_yday = (__mon_yday[__isleap (1900 + tm->tm_year)][tm->tm_mon]
246                  + (tm->tm_mday - 1));
247 }
248
249 static char *
250 #ifdef _LIBC
251 internal_function
252 #endif
253 strptime_internal __P ((const char *buf, const char *format, struct tm *tm,
254                         enum locale_status *decided));
255
256 static char *
257 #ifdef _LIBC
258 internal_function
259 #endif
260 strptime_internal (buf, format, tm, decided)
261      const char *buf;
262      const char *format;
263      struct tm *tm;
264      enum locale_status *decided;
265 {
266   const char *rp;
267   const char *fmt;
268   int cnt;
269   size_t val;
270   int have_I, is_pm;
271   int century, want_century;
272   int have_wday, want_xday;
273   int have_yday;
274   int have_mon, have_mday;
275
276   rp = buf;
277   fmt = format;
278   have_I = is_pm = 0;
279   century = -1;
280   want_century = 0;
281   have_wday = want_xday = have_yday = have_mon = have_mday = 0;
282
283   while (*fmt != '\0')
284     {
285       /* A white space in the format string matches 0 more or white
286          space in the input string.  */
287       if (isspace (*fmt))
288         {
289           while (isspace (*rp))
290             ++rp;
291           ++fmt;
292           continue;
293         }
294
295       /* Any character but `%' must be matched by the same character
296          in the iput string.  */
297       if (*fmt != '%')
298         {
299           match_char (*fmt++, *rp++);
300           continue;
301         }
302
303       ++fmt;
304 #ifndef _NL_CURRENT
305       /* We need this for handling the `E' modifier.  */
306     start_over:
307 #endif
308       switch (*fmt++)
309         {
310         case '%':
311           /* Match the `%' character itself.  */
312           match_char ('%', *rp++);
313           break;
314         case 'a':
315         case 'A':
316           /* Match day of week.  */
317           for (cnt = 0; cnt < 7; ++cnt)
318             {
319 #ifdef _NL_CURRENT
320               if (*decided !=raw)
321                 {
322                   if (match_string (_NL_CURRENT (LC_TIME, DAY_1 + cnt), rp))
323                     {
324                       if (*decided == not
325                           && strcmp (_NL_CURRENT (LC_TIME, DAY_1 + cnt),
326                                      weekday_name[cnt]))
327                         *decided = loc;
328                       break;
329                     }
330                   if (match_string (_NL_CURRENT (LC_TIME, ABDAY_1 + cnt), rp))
331                     {
332                       if (*decided == not
333                           && strcmp (_NL_CURRENT (LC_TIME, ABDAY_1 + cnt),
334                                      ab_weekday_name[cnt]))
335                         *decided = loc;
336                       break;
337                     }
338                 }
339 #endif
340               if (*decided != loc
341                   && (match_string (weekday_name[cnt], rp)
342                       || match_string (ab_weekday_name[cnt], rp)))
343                 {
344                   *decided = raw;
345                   break;
346                 }
347             }
348           if (cnt == 7)
349             /* Does not match a weekday name.  */
350             return NULL;
351           tm->tm_wday = cnt;
352           have_wday = 1;
353           break;
354         case 'b':
355         case 'B':
356         case 'h':
357           /* Match month name.  */
358           for (cnt = 0; cnt < 12; ++cnt)
359             {
360 #ifdef _NL_CURRENT
361               if (*decided !=raw)
362                 {
363                   if (match_string (_NL_CURRENT (LC_TIME, MON_1 + cnt), rp))
364                     {
365                       if (*decided == not
366                           && strcmp (_NL_CURRENT (LC_TIME, MON_1 + cnt),
367                                      month_name[cnt]))
368                         *decided = loc;
369                       break;
370                     }
371                   if (match_string (_NL_CURRENT (LC_TIME, ABMON_1 + cnt), rp))
372                     {
373                       if (*decided == not
374                           && strcmp (_NL_CURRENT (LC_TIME, ABMON_1 + cnt),
375                                      ab_month_name[cnt]))
376                         *decided = loc;
377                       break;
378                     }
379                 }
380 #endif
381               if (match_string (month_name[cnt], rp)
382                   || match_string (ab_month_name[cnt], rp))
383                 {
384                   *decided = raw;
385                   break;
386                 }
387             }
388           if (cnt == 12)
389             /* Does not match a month name.  */
390             return NULL;
391           tm->tm_mon = cnt;
392           want_xday = 1;
393           break;
394         case 'c':
395           /* Match locale's date and time format.  */
396 #ifdef _NL_CURRENT
397           if (*decided != raw)
398             {
399               if (!recursive (_NL_CURRENT (LC_TIME, D_T_FMT)))
400                 {
401                   if (*decided == loc)
402                     return NULL;
403                 }
404               else
405                 {
406                   if (*decided == not &&
407                       strcmp (_NL_CURRENT (LC_TIME, D_T_FMT), HERE_D_T_FMT))
408                     *decided = loc;
409                   want_xday = 1;
410                   break;
411                 }
412               *decided = raw;
413             }
414 #endif
415           if (!recursive (HERE_D_T_FMT))
416             return NULL;
417           want_xday = 1;
418           break;
419         case 'C':
420           /* Match century number.  */
421           get_number (0, 99, 2);
422           century = val;
423           want_xday = 1;
424           break;
425         case 'd':
426         case 'e':
427           /* Match day of month.  */
428           get_number (1, 31, 2);
429           tm->tm_mday = val;
430           have_mday = 1;
431           want_xday = 1;
432           break;
433         case 'F':
434           if (!recursive ("%Y-%m-%d"))
435             return NULL;
436           want_xday = 1;
437           break;
438         case 'x':
439 #ifdef _NL_CURRENT
440           if (*decided != raw)
441             {
442               if (!recursive (_NL_CURRENT (LC_TIME, D_FMT)))
443                 {
444                   if (*decided == loc)
445                     return NULL;
446                 }
447               else
448                 {
449                   if (decided == not
450                       && strcmp (_NL_CURRENT (LC_TIME, D_FMT), HERE_D_FMT))
451                     *decided = loc;
452                   want_xday = 1;
453                   break;
454                 }
455               *decided = raw;
456             }
457 #endif
458           /* Fall through.  */
459         case 'D':
460           /* Match standard day format.  */
461           if (!recursive (HERE_D_FMT))
462             return NULL;
463           want_xday = 1;
464           break;
465         case 'k':
466         case 'H':
467           /* Match hour in 24-hour clock.  */
468           get_number (0, 23, 2);
469           tm->tm_hour = val;
470           have_I = 0;
471           break;
472         case 'I':
473           /* Match hour in 12-hour clock.  */
474           get_number (1, 12, 2);
475           tm->tm_hour = val % 12;
476           have_I = 1;
477           break;
478         case 'j':
479           /* Match day number of year.  */
480           get_number (1, 366, 3);
481           tm->tm_yday = val - 1;
482           have_yday = 1;
483           break;
484         case 'm':
485           /* Match number of month.  */
486           get_number (1, 12, 2);
487           tm->tm_mon = val - 1;
488           have_mon = 1;
489           want_xday = 1;
490           break;
491         case 'M':
492           /* Match minute.  */
493           get_number (0, 59, 2);
494           tm->tm_min = val;
495           break;
496         case 'n':
497         case 't':
498           /* Match any white space.  */
499           while (isspace (*rp))
500             ++rp;
501           break;
502         case 'p':
503           /* Match locale's equivalent of AM/PM.  */
504 #ifdef _NL_CURRENT
505           if (*decided != raw)
506             {
507               if (match_string (_NL_CURRENT (LC_TIME, AM_STR), rp))
508                 {
509                   if (strcmp (_NL_CURRENT (LC_TIME, AM_STR), HERE_AM_STR))
510                     *decided = loc;
511                   break;
512                 }
513               if (match_string (_NL_CURRENT (LC_TIME, PM_STR), rp))
514                 {
515                   if (strcmp (_NL_CURRENT (LC_TIME, PM_STR), HERE_PM_STR))
516                     *decided = loc;
517                   is_pm = 1;
518                   break;
519                 }
520               *decided = raw;
521             }
522 #endif
523           if (!match_string (HERE_AM_STR, rp))
524             if (match_string (HERE_PM_STR, rp))
525               is_pm = 1;
526             else
527               return NULL;
528           break;
529         case 'r':
530 #ifdef _NL_CURRENT
531           if (*decided != raw)
532             {
533               if (!recursive (_NL_CURRENT (LC_TIME, T_FMT_AMPM)))
534                 {
535                   if (*decided == loc)
536                     return NULL;
537                 }
538               else
539                 {
540                   if (*decided == not &&
541                       strcmp (_NL_CURRENT (LC_TIME, T_FMT_AMPM),
542                               HERE_T_FMT_AMPM))
543                     *decided = loc;
544                   break;
545                 }
546               *decided = raw;
547             }
548 #endif
549           if (!recursive (HERE_T_FMT_AMPM))
550             return NULL;
551           break;
552         case 'R':
553           if (!recursive ("%H:%M"))
554             return NULL;
555           break;
556         case 's':
557           {
558             /* The number of seconds may be very high so we cannot use
559                the `get_number' macro.  Instead read the number
560                character for character and construct the result while
561                doing this.  */
562             time_t secs = 0;
563             if (*rp < '0' || *rp > '9')
564               /* We need at least one digit.  */
565               return NULL;
566
567             do
568               {
569                 secs *= 10;
570                 secs += *rp++ - '0';
571               }
572             while (*rp >= '0' && *rp <= '9');
573
574             if (localtime_r (&secs, tm) == NULL)
575               /* Error in function.  */
576               return NULL;
577           }
578           break;
579         case 'S':
580           get_number (0, 61, 2);
581           tm->tm_sec = val;
582           break;
583         case 'X':
584 #ifdef _NL_CURRENT
585           if (*decided != raw)
586             {
587               if (!recursive (_NL_CURRENT (LC_TIME, T_FMT)))
588                 {
589                   if (*decided == loc)
590                     return NULL;
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         case 'V':
626         case 'W':
627           get_number (0, 53, 2);
628           /* XXX This cannot determine any field in TM without some
629              information.  */
630           break;
631         case 'w':
632           /* Match number of weekday.  */
633           get_number (0, 6, 1);
634           tm->tm_wday = val;
635           have_wday = 1;
636           break;
637         case 'y':
638           /* Match year within century.  */
639           get_number (0, 99, 2);
640           /* The "Year 2000: The Millennium Rollover" paper suggests that
641              values in the range 69-99 refer to the twentieth century.  */
642           tm->tm_year = val >= 69 ? val : val + 100;
643           /* Indicate that we want to use the century, if specified.  */
644           want_century = 1;
645           want_xday = 1;
646           break;
647         case 'Y':
648           /* Match year including century number.  */
649           get_number (0, 9999, 4);
650           tm->tm_year = val - 1900;
651           want_century = 0;
652           want_xday = 1;
653           break;
654         case 'Z':
655           /* XXX How to handle this?  */
656           break;
657         case 'E':
658 #ifdef _NL_CURRENT
659           switch (*fmt++)
660             {
661             case 'c':
662               /* Match locale's alternate date and time format.  */
663               if (*decided != raw)
664                 {
665                   const char *fmt = _NL_CURRENT (LC_TIME, ERA_D_T_FMT);
666
667                   if (*fmt == '\0')
668                     fmt = _NL_CURRENT (LC_TIME, D_T_FMT);
669
670                   if (!recursive (fmt))
671                     {
672                       if (*decided == loc)
673                         return NULL;
674                     }
675                   else
676                     {
677                       if (strcmp (fmt, HERE_D_T_FMT))
678                         *decided = loc;
679                       want_xday = 1;
680                       break;
681                     }
682                   *decided = raw;
683                 }
684               /* The C locale has no era information, so use the
685                  normal representation.  */
686               if (!recursive (HERE_D_T_FMT))
687                 return NULL;
688               want_xday = 1;
689               break;
690             case 'C':
691             case 'y':
692             case 'Y':
693               /* Match name of base year in locale's alternate
694                  representation.  */
695               /* XXX This is currently not implemented.  It should
696                  use the value _NL_CURRENT (LC_TIME, ERA).  */
697               break;
698             case 'x':
699               if (*decided != raw)
700                 {
701                   const char *fmt = _NL_CURRENT (LC_TIME, ERA_D_FMT);
702
703                   if (*fmt == '\0')
704                     fmt = _NL_CURRENT (LC_TIME, D_FMT);
705
706                   if (!recursive (fmt))
707                     {
708                       if (*decided == loc)
709                         return NULL;
710                     }
711                   else
712                     {
713                       if (strcmp (fmt, HERE_D_FMT))
714                         *decided = loc;
715                       break;
716                     }
717                   *decided = raw;
718                 }
719               if (!recursive (HERE_D_FMT))
720                 return NULL;
721               break;
722             case 'X':
723               if (*decided != raw)
724                 {
725                   const char *fmt = _NL_CURRENT (LC_TIME, ERA_T_FMT);
726
727                   if (*fmt == '\0')
728                     fmt = _NL_CURRENT (LC_TIME, T_FMT);
729
730                   if (!recursive (fmt))
731                     {
732                       if (*decided == loc)
733                         return NULL;
734                     }
735                   else
736                     {
737                       if (strcmp (fmt, HERE_T_FMT))
738                         *decided = loc;
739                       break;
740                     }
741                   *decided = raw;
742                 }
743               if (!recursive (HERE_T_FMT))
744                 return NULL;
745               break;
746             default:
747               return NULL;
748             }
749           break;
750 #else
751           /* We have no information about the era format.  Just use
752              the normal format.  */
753           if (*fmt != 'c' && *fmt != 'C' && *fmt != 'y' && *fmt != 'Y'
754               && *fmt != 'x' && *fmt != 'X')
755             /* This is an illegal format.  */
756             return NULL;
757
758           goto start_over;
759 #endif
760         case 'O':
761           switch (*fmt++)
762             {
763             case 'd':
764             case 'e':
765               /* Match day of month using alternate numeric symbols.  */
766               get_alt_number (1, 31, 2);
767               tm->tm_mday = val;
768               have_mday = 1;
769               want_xday = 1;
770               break;
771             case 'H':
772               /* Match hour in 24-hour clock using alternate numeric
773                  symbols.  */
774               get_alt_number (0, 23, 2);
775               tm->tm_hour = val;
776               have_I = 0;
777               break;
778             case 'I':
779               /* Match hour in 12-hour clock using alternate numeric
780                  symbols.  */
781               get_alt_number (1, 12, 2);
782               tm->tm_hour = val - 1;
783               have_I = 1;
784               break;
785             case 'm':
786               /* Match month using alternate numeric symbols.  */
787               get_alt_number (1, 12, 2);
788               tm->tm_mon = val - 1;
789               have_mon = 1;
790               want_xday = 1;
791               break;
792             case 'M':
793               /* Match minutes using alternate numeric symbols.  */
794               get_alt_number (0, 59, 2);
795               tm->tm_min = val;
796               break;
797             case 'S':
798               /* Match seconds using alternate numeric symbols.  */
799               get_alt_number (0, 61, 2);
800               tm->tm_sec = val;
801               break;
802             case 'U':
803             case 'V':
804             case 'W':
805               get_alt_number (0, 53, 2);
806               /* XXX This cannot determine any field in TM without
807                  further information.  */
808               break;
809             case 'w':
810               /* Match number of weekday using alternate numeric symbols.  */
811               get_alt_number (0, 6, 1);
812               tm->tm_wday = val;
813               have_wday = 1;
814               break;
815             case 'y':
816               /* Match year within century using alternate numeric symbols.  */
817               get_alt_number (0, 99, 2);
818               tm->tm_year = val >= 69 ? val : val + 100;
819               want_xday = 1;
820               break;
821             default:
822               return NULL;
823             }
824           break;
825         default:
826           return NULL;
827         }
828     }
829
830   if (have_I && is_pm)
831     tm->tm_hour += 12;
832
833   if (want_century && century != -1)
834     tm->tm_year = tm->tm_year % 100 + (century - 19) * 100;
835
836   if (want_xday && !have_wday) {
837       if ( !(have_mon && have_mday) && have_yday)  {
838           /* we don't have tm_mon and/or tm_mday, compute them */
839           int t_mon = 0;
840           while (__mon_yday[__isleap(1900 + tm->tm_year)][t_mon] <= tm->tm_yday)
841               t_mon++;
842           if (!have_mon)
843               tm->tm_mon = t_mon - 1;
844           if (!have_mday)
845               tm->tm_mday = tm->tm_yday - __mon_yday[__isleap(1900 + tm->tm_year)][t_mon - 1] + 1;
846       }
847       day_of_the_week (tm);
848   }
849   if (want_xday && !have_yday)
850     day_of_the_year (tm);
851
852   return (char *) rp;
853 }
854
855
856 char *
857 strptime (buf, format, tm)
858      const char *buf;
859      const char *format;
860      struct tm *tm;
861 {
862   enum locale_status decided;
863 #ifdef _NL_CURRENT
864   decided = not;
865 #else
866   decided = raw;
867 #endif
868   return strptime_internal (buf, format, tm, &decided);
869 }