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