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