(strptime_internal): Set tm_wday and tm_yday in any of tm_year, tm_mon, or
[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 - 1970))
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 static void
221 day_of_the_year (struct tm *tm)
222 {
223   tm->tm_yday = __mon_yday[__isleap (tm->tm_year)][tm->tm_mon] + tm->tm_mday;
224 }
225
226 static char *
227 #ifdef _LIBC
228 internal_function
229 #endif
230 strptime_internal __P ((const char *buf, const char *format, struct tm *tm,
231                         enum locale_status *decided));
232
233 static char *
234 #ifdef _LIBC
235 internal_function
236 #endif
237 strptime_internal (buf, format, tm, decided)
238      const char *buf;
239      const char *format;
240      struct tm *tm;
241      enum locale_status *decided;
242 {
243   const char *rp;
244   const char *fmt;
245   int cnt;
246   size_t val;
247   int have_I, is_pm;
248   int century, want_century;
249   int have_wday, want_xday;
250   int have_yday;
251
252   rp = buf;
253   fmt = format;
254   have_I = is_pm = 0;
255   century = -1;
256   want_century = 0;
257   have_wday = want_xday = have_yday = 0;
258
259   while (*fmt != '\0')
260     {
261       /* A white space in the format string matches 0 more or white
262          space in the input string.  */
263       if (isspace (*fmt))
264         {
265           while (isspace (*rp))
266             ++rp;
267           ++fmt;
268           continue;
269         }
270
271       /* Any character but `%' must be matched by the same character
272          in the iput string.  */
273       if (*fmt != '%')
274         {
275           match_char (*fmt++, *rp++);
276           continue;
277         }
278
279       ++fmt;
280 #ifndef _NL_CURRENT
281       /* We need this for handling the `E' modifier.  */
282     start_over:
283 #endif
284       switch (*fmt++)
285         {
286         case '%':
287           /* Match the `%' character itself.  */
288           match_char ('%', *rp++);
289           break;
290         case 'a':
291         case 'A':
292           /* Match day of week.  */
293           for (cnt = 0; cnt < 7; ++cnt)
294             {
295 #ifdef _NL_CURRENT
296               if (*decided !=raw)
297                 {
298                   if (match_string (_NL_CURRENT (LC_TIME, DAY_1 + cnt), rp))
299                     {
300                       if (*decided == not
301                           && strcmp (_NL_CURRENT (LC_TIME, DAY_1 + cnt),
302                                      weekday_name[cnt]))
303                         *decided = loc;
304                       break;
305                     }
306                   if (match_string (_NL_CURRENT (LC_TIME, ABDAY_1 + cnt), rp))
307                     {
308                       if (*decided == not
309                           && strcmp (_NL_CURRENT (LC_TIME, ABDAY_1 + cnt),
310                                      ab_weekday_name[cnt]))
311                         *decided = loc;
312                       break;
313                     }
314                 }
315 #endif
316               if (*decided != loc
317                   && (match_string (weekday_name[cnt], rp)
318                       || match_string (ab_weekday_name[cnt], rp)))
319                 {
320                   *decided = raw;
321                   break;
322                 }
323             }
324           if (cnt == 7)
325             /* Does not match a weekday name.  */
326             return NULL;
327           tm->tm_wday = cnt;
328           have_wday = 1;
329           break;
330         case 'b':
331         case 'B':
332         case 'h':
333           /* Match month name.  */
334           for (cnt = 0; cnt < 12; ++cnt)
335             {
336 #ifdef _NL_CURRENT
337               if (*decided !=raw)
338                 {
339                   if (match_string (_NL_CURRENT (LC_TIME, MON_1 + cnt), rp))
340                     {
341                       if (*decided == not
342                           && strcmp (_NL_CURRENT (LC_TIME, MON_1 + cnt),
343                                      month_name[cnt]))
344                         *decided = loc;
345                       break;
346                     }
347                   if (match_string (_NL_CURRENT (LC_TIME, ABMON_1 + cnt), rp))
348                     {
349                       if (*decided == not
350                           && strcmp (_NL_CURRENT (LC_TIME, ABMON_1 + cnt),
351                                      ab_month_name[cnt]))
352                         *decided = loc;
353                       break;
354                     }
355                 }
356 #endif
357               if (match_string (month_name[cnt], rp)
358                   || match_string (ab_month_name[cnt], rp))
359                 {
360                   *decided = raw;
361                   break;
362                 }
363             }
364           if (cnt == 12)
365             /* Does not match a month name.  */
366             return NULL;
367           tm->tm_mon = cnt;
368           want_xday = 1;
369           break;
370         case 'c':
371           /* Match locale's date and time format.  */
372 #ifdef _NL_CURRENT
373           if (*decided != raw)
374             {
375               if (!recursive (_NL_CURRENT (LC_TIME, D_T_FMT)))
376                 {
377                   if (*decided == loc)
378                     return NULL;
379                 }
380               else
381                 {
382                   if (*decided == not &&
383                       strcmp (_NL_CURRENT (LC_TIME, D_T_FMT), HERE_D_T_FMT))
384                     *decided = loc;
385                   want_xday = 1;
386                   break;
387                 }
388               *decided = raw;
389             }
390 #endif
391           if (!recursive (HERE_D_T_FMT))
392             return NULL;
393           want_xday = 1;
394           break;
395         case 'C':
396           /* Match century number.  */
397           get_number (0, 99);
398           century = val;
399           want_xday = 1;
400           break;
401         case 'd':
402         case 'e':
403           /* Match day of month.  */
404           get_number (1, 31);
405           tm->tm_mday = val;
406           break;
407         case 'x':
408 #ifdef _NL_CURRENT
409           if (*decided != raw)
410             {
411               if (!recursive (_NL_CURRENT (LC_TIME, D_FMT)))
412                 {
413                   if (*decided == loc)
414                     return NULL;
415                 }
416               else
417                 {
418                   if (decided == not
419                       && strcmp (_NL_CURRENT (LC_TIME, D_FMT), HERE_D_FMT))
420                     *decided = loc;
421                   want_xday = 1;
422                   break;
423                 }
424               *decided = raw;
425             }
426 #endif
427           /* Fall through.  */
428         case 'D':
429           /* Match standard day format.  */
430           if (!recursive (HERE_D_FMT))
431             return NULL;
432           want_xday = 1;
433           break;
434         case 'H':
435           /* Match hour in 24-hour clock.  */
436           get_number (0, 23);
437           tm->tm_hour = val;
438           have_I = 0;
439           break;
440         case 'I':
441           /* Match hour in 12-hour clock.  */
442           get_number (1, 12);
443           tm->tm_hour = val % 12;
444           have_I = 1;
445           break;
446         case 'j':
447           /* Match day number of year.  */
448           get_number (1, 366);
449           tm->tm_yday = val - 1;
450           have_yday = 1;
451           break;
452         case 'm':
453           /* Match number of month.  */
454           get_number (1, 12);
455           tm->tm_mon = val - 1;
456           want_xday = 1;
457           break;
458         case 'M':
459           /* Match minute.  */
460           get_number (0, 59);
461           tm->tm_min = val;
462           break;
463         case 'n':
464         case 't':
465           /* Match any white space.  */
466           while (isspace (*rp))
467             ++rp;
468           break;
469         case 'p':
470           /* Match locale's equivalent of AM/PM.  */
471 #ifdef _NL_CURRENT
472           if (*decided != raw)
473             {
474               if (match_string (_NL_CURRENT (LC_TIME, AM_STR), rp))
475                 {
476                   if (strcmp (_NL_CURRENT (LC_TIME, AM_STR), HERE_AM_STR))
477                     *decided = loc;
478                   break;
479                 }
480               if (match_string (_NL_CURRENT (LC_TIME, PM_STR), rp))
481                 {
482                   if (strcmp (_NL_CURRENT (LC_TIME, PM_STR), HERE_PM_STR))
483                     *decided = loc;
484                   is_pm = 1;
485                   break;
486                 }
487               *decided = raw;
488             }
489 #endif
490           if (!match_string (HERE_AM_STR, rp))
491             if (match_string (HERE_PM_STR, rp))
492               is_pm = 1;
493             else
494               return NULL;
495           break;
496         case 'r':
497 #ifdef _NL_CURRENT
498           if (*decided != raw)
499             {
500               if (!recursive (_NL_CURRENT (LC_TIME, T_FMT_AMPM)))
501                 {
502                   if (*decided == loc)
503                     return NULL;
504                 }
505               else
506                 {
507                   if (*decided == not &&
508                       strcmp (_NL_CURRENT (LC_TIME, T_FMT_AMPM),
509                               HERE_T_FMT_AMPM))
510                     *decided = loc;
511                   break;
512                 }
513               *decided = raw;
514             }
515 #endif
516           if (!recursive (HERE_T_FMT_AMPM))
517             return NULL;
518           break;
519         case 'R':
520           if (!recursive ("%H:%M"))
521             return NULL;
522           break;
523         case 's':
524           {
525             /* The number of seconds may be very high so we cannot use
526                the `get_number' macro.  Instead read the number
527                character for character and construct the result while
528                doing this.  */
529             time_t secs;
530             if (*rp < '0' || *rp > '9')
531               /* We need at least one digit.  */
532               return NULL;
533
534             do
535               {
536                 secs *= 10;
537                 secs += *rp++ - '0';
538               }
539             while (*rp >= '0' && *rp <= '9');
540
541             if (localtime_r (&secs, tm) == NULL)
542               /* Error in function.  */
543               return NULL;
544           }
545           break;
546         case 'S':
547           get_number (0, 61);
548           tm->tm_sec = val;
549           break;
550         case 'X':
551 #ifdef _NL_CURRENT
552           if (*decided != raw)
553             {
554               if (!recursive (_NL_CURRENT (LC_TIME, T_FMT)))
555                 {
556                   if (*decided == loc)
557                     return NULL;
558                 }
559               else
560                 {
561                   if (strcmp (_NL_CURRENT (LC_TIME, T_FMT), HERE_T_FMT))
562                     *decided = loc;
563                   break;
564                 }
565               *decided = raw;
566             }
567 #endif
568           /* Fall through.  */
569         case 'T':
570           if (!recursive (HERE_T_FMT))
571             return NULL;
572           break;
573         case 'u':
574           get_number (1, 7);
575           tm->tm_wday = val % 7;
576           have_wday = 1;
577           break;
578         case 'g':
579           get_number (0, 99);
580           /* XXX This cannot determine any field in TM.  */
581           break;
582         case 'G':
583           if (*rp < '0' || *rp > '9')
584             return NULL;
585           /* XXX Ignore the number since we would need some more
586              information to compute a real date.  */
587           do
588             ++rp;
589           while (*rp >= '0' && *rp <= '9');
590           break;
591         case 'U':
592         case 'V':
593         case 'W':
594           get_number (0, 53);
595           /* XXX This cannot determine any field in TM without some
596              information.  */
597           break;
598         case 'w':
599           /* Match number of weekday.  */
600           get_number (0, 6);
601           tm->tm_wday = val;
602           have_wday = 1;
603           break;
604         case 'y':
605           /* Match year within century.  */
606           get_number (0, 99);
607           /* The "Year 2000: The Millennium Rollover" paper suggests that
608              values in the range 69-99 refer to the twentieth century.  */
609           tm->tm_year = val >= 69 ? val : val + 100;
610           /* Indicate that we want to use the century, if specified.  */
611           want_century = 1;
612           want_xday = 1;
613           break;
614         case 'Y':
615           /* Match year including century number.  */
616           get_number (0, 9999);
617           tm->tm_year = val - 1900;
618           want_century = 0;
619           want_xday = 1;
620           break;
621         case 'Z':
622           /* XXX How to handle this?  */
623           break;
624         case 'E':
625 #ifdef _NL_CURRENT
626           switch (*fmt++)
627             {
628             case 'c':
629               /* Match locale's alternate date and time format.  */
630               if (*decided != raw)
631                 {
632                   const char *fmt = _NL_CURRENT (LC_TIME, ERA_D_T_FMT);
633
634                   if (*fmt == '\0')
635                     fmt = _NL_CURRENT (LC_TIME, D_T_FMT);
636
637                   if (!recursive (fmt))
638                     {
639                       if (*decided == loc)
640                         return NULL;
641                     }
642                   else
643                     {
644                       if (strcmp (fmt, HERE_D_T_FMT))
645                         *decided = loc;
646                       want_xday = 1;
647                       break;
648                     }
649                   *decided = raw;
650                 }
651               /* The C locale has no era information, so use the
652                  normal representation.  */
653               if (!recursive (HERE_D_T_FMT))
654                 return NULL;
655               want_xday = 1;
656               break;
657             case 'C':
658             case 'y':
659             case 'Y':
660               /* Match name of base year in locale's alternate
661                  representation.  */
662               /* XXX This is currently not implemented.  It should
663                  use the value _NL_CURRENT (LC_TIME, ERA).  */
664               break;
665             case 'x':
666               if (*decided != raw)
667                 {
668                   const char *fmt = _NL_CURRENT (LC_TIME, ERA_D_FMT);
669
670                   if (*fmt == '\0')
671                     fmt = _NL_CURRENT (LC_TIME, D_FMT);
672
673                   if (!recursive (fmt))
674                     {
675                       if (*decided == loc)
676                         return NULL;
677                     }
678                   else
679                     {
680                       if (strcmp (fmt, HERE_D_FMT))
681                         *decided = loc;
682                       break;
683                     }
684                   *decided = raw;
685                 }
686               if (!recursive (HERE_D_FMT))
687                 return NULL;
688               break;
689             case 'X':
690               if (*decided != raw)
691                 {
692                   const char *fmt = _NL_CURRENT (LC_TIME, ERA_T_FMT);
693
694                   if (*fmt == '\0')
695                     fmt = _NL_CURRENT (LC_TIME, T_FMT);
696
697                   if (!recursive (fmt))
698                     {
699                       if (*decided == loc)
700                         return NULL;
701                     }
702                   else
703                     {
704                       if (strcmp (fmt, HERE_T_FMT))
705                         *decided = loc;
706                       break;
707                     }
708                   *decided = raw;
709                 }
710               if (!recursive (HERE_T_FMT))
711                 return NULL;
712               break;
713             default:
714               return NULL;
715             }
716           break;
717 #else
718           /* We have no information about the era format.  Just use
719              the normal format.  */
720           if (*fmt != 'c' && *fmt != 'C' && *fmt != 'y' && *fmt != 'Y'
721               && *fmt != 'x' && *fmt != 'X')
722             /* This is an illegal format.  */
723             return NULL;
724
725           goto start_over;
726 #endif
727         case 'O':
728           switch (*fmt++)
729             {
730             case 'd':
731             case 'e':
732               /* Match day of month using alternate numeric symbols.  */
733               get_alt_number (1, 31);
734               tm->tm_mday = val;
735               want_xday = 1;
736               break;
737             case 'H':
738               /* Match hour in 24-hour clock using alternate numeric
739                  symbols.  */
740               get_alt_number (0, 23);
741               tm->tm_hour = val;
742               have_I = 0;
743               break;
744             case 'I':
745               /* Match hour in 12-hour clock using alternate numeric
746                  symbols.  */
747               get_alt_number (1, 12);
748               tm->tm_hour = val - 1;
749               have_I = 1;
750               break;
751             case 'm':
752               /* Match month using alternate numeric symbols.  */
753               get_alt_number (1, 12);
754               tm->tm_mon = val - 1;
755               want_xday = 1;
756               break;
757             case 'M':
758               /* Match minutes using alternate numeric symbols.  */
759               get_alt_number (0, 59);
760               tm->tm_min = val;
761               break;
762             case 'S':
763               /* Match seconds using alternate numeric symbols.  */
764               get_alt_number (0, 61);
765               tm->tm_sec = val;
766               break;
767             case 'U':
768             case 'V':
769             case 'W':
770               get_alt_number (0, 53);
771               /* XXX This cannot determine any field in TM without
772                  further information.  */
773               break;
774             case 'w':
775               /* Match number of weekday using alternate numeric symbols.  */
776               get_alt_number (0, 6);
777               tm->tm_wday = val;
778               have_wday = 1;
779               break;
780             case 'y':
781               /* Match year within century using alternate numeric symbols.  */
782               get_alt_number (0, 99);
783               tm->tm_year = val >= 69 ? val : val + 100;
784               want_xday = 1;
785               break;
786             default:
787               return NULL;
788             }
789           break;
790         default:
791           return NULL;
792         }
793     }
794
795   if (have_I && is_pm)
796     tm->tm_hour += 12;
797
798   if (want_century && century != -1)
799     tm->tm_year = tm->tm_year % 100 + (century - 19) * 100;
800
801   if (want_xday && !have_wday)
802     day_of_the_week (tm);
803   if (want_xday && !have_yday)
804     day_of_the_year (tm);
805
806   return (char *) rp;
807 }
808
809
810 char *
811 strptime (buf, format, tm)
812      const char *buf;
813      const char *format;
814      struct tm *tm;
815 {
816   enum locale_status decided;
817 #ifdef _NL_CURRENT
818   decided = not;
819 #else
820   decided = raw;
821 #endif
822   return strptime_internal (buf, format, tm, &decided);
823 }