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