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