Change bug-glibc-manual@prep.ai.mit.edu to bug-glibc-manual@gnu.org.
[glibc.git] / time / strptime.c
blob8d650716fe9f7af3db713ee43a801006dec22a29
1 /* Convert a string representation of time to a time value.
2 Copyright (C) 1996, 1997 Free Software Foundation, Inc.
3 This file is part of the GNU C Library.
4 Contributed by Ulrich Drepper <drepper@cygnus.com>, 1996.
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.
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.
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. */
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. */
26 #ifdef HAVE_CONFIG_H
27 # include <config.h>
28 #endif
30 #include <ctype.h>
31 #include <langinfo.h>
32 #include <limits.h>
33 #include <string.h>
34 #include <time.h>
36 #ifdef _LIBC
37 # include "../locale/localeinfo.h"
38 #endif
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. */
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;
61 struct tm *l = localtime (t);
62 if (! l)
63 return 0;
64 *tp = *l;
65 return tp;
67 #endif /* ! _LIBC */
68 #endif /* ! HAVE_LOCALTIME_R && ! defined (localtime_r) */
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) \
102 const char *alts = _NL_CURRENT (LC_TIME, ALT_DIGITS); \
103 val = 0; \
104 while (*alts != '\0') \
106 size_t len = strlen (alts); \
107 if (strncasecmp (alts, rp, len) == 0) \
108 break; \
109 alts = strchr (alts, '\0') + 1; \
110 ++val; \
112 if (*alts == '\0') \
114 if (*decided == loc && val != 0) \
115 return NULL; \
117 else \
119 *decided = loc; \
120 break; \
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)
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;
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_T_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 #else
152 static char const weekday_name[][10] =
154 "Sunday", "Monday", "Tuesday", "Wednesday",
155 "Thursday", "Friday", "Saturday"
157 static char const ab_weekday_name[][4] =
159 "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
161 static char const month_name[][10] =
163 "January", "February", "March", "April", "May", "June",
164 "July", "August", "September", "October", "November", "December"
166 static char const ab_month_name[][4] =
168 "Jan", "Feb", "Mar", "Apr", "May", "Jun",
169 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
171 # define HERE_D_T_FMT "%a %b %e %H:%M:%S %Y"
172 # define HERE_D_FMT "%m/%d/%y"
173 # define HERE_AM_STR "AM"
174 # define HERE_PM_STR "PM"
175 # define HERE_T_FMT_AMPM "%I:%M:%S %p"
176 # define HERE_T_FMT "%H:%M:%S"
177 #endif
179 /* Status of lookup: do we use the locale data or the raw data? */
180 enum locale_status { not, loc, raw };
182 static char *
183 strptime_internal __P ((const char *buf, const char *format, struct tm *tm,
184 enum locale_status *decided));
186 static char *
187 strptime_internal (buf, format, tm, decided)
188 const char *buf;
189 const char *format;
190 struct tm *tm;
191 enum locale_status *decided;
193 const char *rp;
194 const char *fmt;
195 int cnt;
196 size_t val;
197 int have_I, is_pm;
199 rp = buf;
200 fmt = format;
201 have_I = is_pm = 0;
203 while (*fmt != '\0')
205 /* A white space in the format string matches 0 more or white
206 space in the input string. */
207 if (isspace (*fmt))
209 while (isspace (*rp))
210 ++rp;
211 ++fmt;
212 continue;
215 /* Any character but `%' must be matched by the same character
216 in the iput string. */
217 if (*fmt != '%')
219 match_char (*fmt++, *rp++);
220 continue;
223 ++fmt;
224 #ifndef _NL_CURRENT
225 /* We need this for handling the `E' modifier. */
226 start_over:
227 #endif
228 switch (*fmt++)
230 case '%':
231 /* Match the `%' character itself. */
232 match_char ('%', *rp++);
233 break;
234 case 'a':
235 case 'A':
236 /* Match day of week. */
237 for (cnt = 0; cnt < 7; ++cnt)
239 #ifdef _NL_CURRENT
240 if (*decided !=raw)
242 if (match_string (_NL_CURRENT (LC_TIME, DAY_1 + cnt), rp))
244 if (*decided == not
245 && strcmp (_NL_CURRENT (LC_TIME, DAY_1 + cnt),
246 weekday_name[cnt]))
247 *decided = loc;
248 break;
250 if (match_string (_NL_CURRENT (LC_TIME, ABDAY_1 + cnt), rp))
252 if (*decided == not
253 && strcmp (_NL_CURRENT (LC_TIME, ABDAY_1 + cnt),
254 ab_weekday_name[cnt]))
255 *decided = loc;
256 break;
259 #endif
260 if (*decided != loc
261 && (match_string (weekday_name[cnt], rp)
262 || match_string (ab_weekday_name[cnt], rp)))
264 *decided = raw;
265 break;
268 if (cnt == 7)
269 /* Does not match a weekday name. */
270 return NULL;
271 tm->tm_wday = cnt;
272 break;
273 case 'b':
274 case 'B':
275 case 'h':
276 /* Match month name. */
277 for (cnt = 0; cnt < 12; ++cnt)
279 #ifdef _NL_CURRENT
280 if (*decided !=raw)
282 if (match_string (_NL_CURRENT (LC_TIME, MON_1 + cnt), rp))
284 if (*decided == not
285 && strcmp (_NL_CURRENT (LC_TIME, MON_1 + cnt),
286 month_name[cnt]))
287 *decided = loc;
288 break;
290 if (match_string (_NL_CURRENT (LC_TIME, ABMON_1 + cnt), rp))
292 if (*decided == not
293 && strcmp (_NL_CURRENT (LC_TIME, ABMON_1 + cnt),
294 ab_month_name[cnt]))
295 *decided = loc;
296 break;
299 #endif
300 if (match_string (month_name[cnt], rp)
301 || match_string (ab_month_name[cnt], rp))
303 *decided = raw;
304 break;
307 if (cnt == 12)
308 /* Does not match a month name. */
309 return NULL;
310 tm->tm_mon = cnt;
311 break;
312 case 'c':
313 /* Match locale's date and time format. */
314 #ifdef _NL_CURRENT
315 if (*decided != raw)
317 if (!recursive (_NL_CURRENT (LC_TIME, D_T_FMT)))
319 if (*decided == loc)
320 return NULL;
322 else
324 if (*decided == not &&
325 strcmp (_NL_CURRENT (LC_TIME, D_T_FMT), HERE_D_T_FMT))
326 *decided = loc;
327 break;
329 *decided = raw;
331 #endif
332 if (!recursive (HERE_D_T_FMT))
333 return NULL;
334 break;
335 case 'C':
336 /* Match century number. */
337 get_number (0, 99);
338 /* We don't need the number. */
339 break;
340 case 'd':
341 case 'e':
342 /* Match day of month. */
343 get_number (1, 31);
344 tm->tm_mday = val;
345 break;
346 case 'x':
347 #ifdef _NL_CURRENT
348 if (*decided != raw)
350 if (!recursive (_NL_CURRENT (LC_TIME, D_FMT)))
352 if (*decided == loc)
353 return NULL;
355 else
357 if (decided == not
358 && strcmp (_NL_CURRENT (LC_TIME, D_FMT), HERE_D_FMT))
359 *decided = loc;
360 break;
362 *decided = raw;
364 #endif
365 /* Fall through. */
366 case 'D':
367 /* Match standard day format. */
368 if (!recursive (HERE_D_FMT))
369 return NULL;
370 break;
371 case 'H':
372 /* Match hour in 24-hour clock. */
373 get_number (0, 23);
374 tm->tm_hour = val;
375 have_I = 0;
376 break;
377 case 'I':
378 /* Match hour in 12-hour clock. */
379 get_number (1, 12);
380 tm->tm_hour = val % 12;
381 have_I = 1;
382 break;
383 case 'j':
384 /* Match day number of year. */
385 get_number (1, 366);
386 tm->tm_yday = val - 1;
387 break;
388 case 'm':
389 /* Match number of month. */
390 get_number (1, 12);
391 tm->tm_mon = val - 1;
392 break;
393 case 'M':
394 /* Match minute. */
395 get_number (0, 59);
396 tm->tm_min = val;
397 break;
398 case 'n':
399 case 't':
400 /* Match any white space. */
401 while (isspace (*rp))
402 ++rp;
403 break;
404 case 'p':
405 /* Match locale's equivalent of AM/PM. */
406 #ifdef _NL_CURRENT
407 if (*decided != raw)
409 if (match_string (_NL_CURRENT (LC_TIME, AM_STR), rp))
411 if (strcmp (_NL_CURRENT (LC_TIME, AM_STR), HERE_AM_STR))
412 *decided = loc;
413 break;
415 if (match_string (_NL_CURRENT (LC_TIME, PM_STR), rp))
417 if (strcmp (_NL_CURRENT (LC_TIME, PM_STR), HERE_PM_STR))
418 *decided = loc;
419 is_pm = 1;
420 break;
422 *decided = raw;
424 #endif
425 if (!match_string (HERE_AM_STR, rp))
426 if (match_string (HERE_PM_STR, rp))
427 is_pm = 1;
428 else
429 return NULL;
430 break;
431 case 'r':
432 #ifdef _NL_CURRENT
433 if (*decided != raw)
435 if (!recursive (_NL_CURRENT (LC_TIME, T_FMT_AMPM)))
437 if (*decided == loc)
438 return NULL;
440 else
442 if (*decided == not &&
443 strcmp (_NL_CURRENT (LC_TIME, T_FMT_AMPM),
444 HERE_T_FMT_AMPM))
445 *decided = loc;
446 break;
448 *decided = raw;
450 #endif
451 if (!recursive (HERE_T_FMT_AMPM))
452 return NULL;
453 break;
454 case 'R':
455 if (!recursive ("%H:%M"))
456 return NULL;
457 break;
458 case 's':
460 /* The number of seconds may be very high so we cannot use
461 the `get_number' macro. Instead read the number
462 character for character and construct the result while
463 doing this. */
464 time_t secs;
465 if (*rp < '0' || *rp > '9')
466 /* We need at least one digit. */
467 return NULL;
471 secs *= 10;
472 secs += *rp++ - '0';
474 while (*rp >= '0' && *rp <= '9');
476 if (localtime_r (&secs, tm) == NULL)
477 /* Error in function. */
478 return NULL;
480 break;
481 case 'S':
482 get_number (0, 61);
483 tm->tm_sec = val;
484 break;
485 case 'X':
486 #ifdef _NL_CURRENT
487 if (*decided != raw)
489 if (!recursive (_NL_CURRENT (LC_TIME, T_FMT)))
491 if (*decided == loc)
492 return NULL;
494 else
496 if (strcmp (_NL_CURRENT (LC_TIME, T_FMT), HERE_T_FMT))
497 *decided = loc;
498 break;
500 *decided = raw;
502 #endif
503 /* Fall through. */
504 case 'T':
505 if (!recursive (HERE_T_FMT))
506 return NULL;
507 break;
508 case 'u':
509 get_number (1, 7);
510 tm->tm_wday = val % 7;
511 break;
512 case 'g':
513 get_number (0, 99);
514 /* XXX This cannot determine any field in TM. */
515 break;
516 case 'G':
517 if (*rp < '0' || *rp > '9')
518 return NULL;
519 /* XXX Ignore the number since we would need some more
520 information to compute a real date. */
522 ++rp;
523 while (*rp >= '0' && *rp <= '9');
524 break;
525 case 'U':
526 case 'V':
527 case 'W':
528 get_number (0, 53);
529 /* XXX This cannot determine any field in TM without some
530 information. */
531 break;
532 case 'w':
533 /* Match number of weekday. */
534 get_number (0, 6);
535 tm->tm_wday = val;
536 break;
537 case 'y':
538 /* Match year within century. */
539 get_number (0, 99);
540 tm->tm_year = val >= 50 ? val : val + 100;
541 break;
542 case 'Y':
543 /* Match year including century number. */
544 if (sizeof (time_t) > 4)
545 get_number (0, 9999);
546 else
547 get_number (0, 2036);
548 tm->tm_year = val - 1900;
549 break;
550 case 'Z':
551 /* XXX How to handle this? */
552 break;
553 case 'E':
554 #ifdef _NL_CURRENT
555 switch (*fmt++)
557 case 'c':
558 /* Match locale's alternate date and time format. */
559 if (*decided != raw)
561 const char *fmt = _NL_CURRENT (LC_TIME, ERA_D_T_FMT);
563 if (*fmt == '\0')
564 fmt = _NL_CURRENT (LC_TIME, D_T_FMT);
566 if (!recursive (fmt))
568 if (*decided == loc)
569 return NULL;
571 else
573 if (strcmp (fmt, HERE_D_T_FMT))
574 *decided = loc;
575 break;
577 *decided = raw;
579 /* The C locale has no era information, so use the
580 normal representation. */
581 if (!recursive (HERE_D_T_FMT))
582 return NULL;
583 break;
584 case 'C':
585 case 'y':
586 case 'Y':
587 /* Match name of base year in locale's alternate
588 representation. */
589 /* XXX This is currently not implemented. It should
590 use the value _NL_CURRENT (LC_TIME, ERA). */
591 break;
592 case 'x':
593 if (*decided != raw)
595 const char *fmt = _NL_CURRENT (LC_TIME, ERA_D_FMT);
597 if (*fmt == '\0')
598 fmt = _NL_CURRENT (LC_TIME, D_FMT);
600 if (!recursive (fmt))
602 if (*decided == loc)
603 return NULL;
605 else
607 if (strcmp (fmt, HERE_D_FMT))
608 *decided = loc;
609 break;
611 *decided = raw;
613 if (!recursive (HERE_D_FMT))
614 return NULL;
615 break;
616 case 'X':
617 if (*decided != raw)
619 const char *fmt = _NL_CURRENT (LC_TIME, ERA_T_FMT);
621 if (*fmt == '\0')
622 fmt = _NL_CURRENT (LC_TIME, T_FMT);
624 if (!recursive (fmt))
626 if (*decided == loc)
627 return NULL;
629 else
631 if (strcmp (fmt, HERE_T_FMT))
632 *decided = loc;
633 break;
635 *decided = raw;
637 if (!recursive (HERE_T_FMT))
638 return NULL;
639 break;
640 default:
641 return NULL;
643 break;
644 #else
645 /* We have no information about the era format. Just use
646 the normal format. */
647 if (*fmt != 'c' && *fmt != 'C' && *fmt != 'y' && *fmt != 'Y'
648 && *fmt != 'x' && *fmt != 'X')
649 /* This is an illegal format. */
650 return NULL;
652 goto start_over;
653 #endif
654 case 'O':
655 switch (*fmt++)
657 case 'd':
658 case 'e':
659 /* Match day of month using alternate numeric symbols. */
660 get_alt_number (1, 31);
661 tm->tm_mday = val;
662 break;
663 case 'H':
664 /* Match hour in 24-hour clock using alternate numeric
665 symbols. */
666 get_alt_number (0, 23);
667 tm->tm_hour = val;
668 have_I = 0;
669 break;
670 case 'I':
671 /* Match hour in 12-hour clock using alternate numeric
672 symbols. */
673 get_alt_number (1, 12);
674 tm->tm_hour = val - 1;
675 have_I = 1;
676 break;
677 case 'm':
678 /* Match month using alternate numeric symbols. */
679 get_alt_number (1, 12);
680 tm->tm_mon = val - 1;
681 break;
682 case 'M':
683 /* Match minutes using alternate numeric symbols. */
684 get_alt_number (0, 59);
685 tm->tm_min = val;
686 break;
687 case 'S':
688 /* Match seconds using alternate numeric symbols. */
689 get_alt_number (0, 61);
690 tm->tm_sec = val;
691 break;
692 case 'U':
693 case 'V':
694 case 'W':
695 get_alt_number (0, 53);
696 /* XXX This cannot determine any field in TM without
697 further information. */
698 break;
699 case 'w':
700 /* Match number of weekday using alternate numeric symbols. */
701 get_alt_number (0, 6);
702 tm->tm_wday = val;
703 break;
704 case 'y':
705 /* Match year within century using alternate numeric symbols. */
706 get_alt_number (0, 99);
707 break;
708 default:
709 return NULL;
711 break;
712 default:
713 return NULL;
717 if (have_I && is_pm)
718 tm->tm_hour += 12;
720 return (char *) rp;
724 char *
725 strptime (buf, format, tm)
726 const char *buf;
727 const char *format;
728 struct tm *tm;
730 enum locale_status decided;
731 #ifdef _NL_CURRENT
732 decided = not;
733 #else
734 decided = raw;
735 #endif
736 return strptime_internal (buf, format, tm, &decided);