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