Update.
[glibc.git] / time / strptime.c
blob1f440a5d2fb34c33413a1278fdf7929c40c4db30
1 /* Convert a string representation of time to a time value.
2 Copyright (C) 1996-2000, 2001 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 Lesser General Public
8 License as published by the Free Software Foundation; either
9 version 2.1 of the 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 Lesser General Public License for more details.
16 You should have received a copy of the GNU Lesser General Public
17 License along with the GNU C Library; if not, write to the Free
18 Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
19 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. */
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;
62 struct tm *l = localtime (t);
63 if (! l)
64 return 0;
65 *tp = *l;
66 return tp;
68 # endif /* ! _LIBC */
69 #endif /* ! HAVE_LOCALTIME_R && ! defined (localtime_r) */
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, n) \
87 do { \
88 int __n = n; \
89 val = 0; \
90 while (*rp == ' ') \
91 ++rp; \
92 if (*rp < '0' || *rp > '9') \
93 return NULL; \
94 do { \
95 val *= 10; \
96 val += *rp++ - '0'; \
97 } while (--__n > 0 && val * 10 <= to && *rp >= '0' && *rp <= '9'); \
98 if (val < from || val > to) \
99 return NULL; \
100 } while (0)
101 #ifdef _NL_CURRENT
102 # define get_alt_number(from, to, n) \
103 ({ \
104 __label__ do_normal; \
106 if (*decided != raw) \
108 val = _nl_parse_alt_digit (&rp); \
109 if (val == -1 && *decided != loc) \
111 *decided = loc; \
112 goto do_normal; \
114 if (val < from || val > to) \
115 return NULL; \
117 else \
119 do_normal: \
120 get_number (from, to, n); \
122 0; \
124 #else
125 # define get_alt_number(from, to, n) \
126 /* We don't have the alternate representation. */ \
127 get_number(from, to, n)
128 #endif
129 #define recursive(new_fmt) \
130 (*(new_fmt) != '\0' \
131 && (rp = strptime_internal (rp, (new_fmt), tm, decided, era_cnt)) != NULL)
134 #ifdef _LIBC
135 /* This is defined in locale/C-time.c in the GNU libc. */
136 extern const struct locale_data _nl_C_LC_TIME;
137 extern const unsigned short int __mon_yday[2][13];
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)
152 # define strncasecmp(s1, s2, n) __strncasecmp (s1, s2, n)
153 #else
154 static char const weekday_name[][10] =
156 "Sunday", "Monday", "Tuesday", "Wednesday",
157 "Thursday", "Friday", "Saturday"
159 static char const ab_weekday_name[][4] =
161 "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
163 static char const month_name[][10] =
165 "January", "February", "March", "April", "May", "June",
166 "July", "August", "September", "October", "November", "December"
168 static char const ab_month_name[][4] =
170 "Jan", "Feb", "Mar", "Apr", "May", "Jun",
171 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
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"
180 const unsigned short int __mon_yday[2][13] =
182 /* Normal years. */
183 { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 },
184 /* Leap years. */
185 { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 }
187 #endif
189 /* Status of lookup: do we use the locale data or the raw data? */
190 enum locale_status { not, loc, raw };
193 #ifndef __isleap
194 /* Nonzero if YEAR is a leap year (every 4 years,
195 except every 100th isn't, and every 400th is). */
196 # define __isleap(year) \
197 ((year) % 4 == 0 && ((year) % 100 != 0 || (year) % 400 == 0))
198 #endif
200 /* Compute the day of the week. */
201 static void
202 day_of_the_week (struct tm *tm)
204 /* We know that January 1st 1970 was a Thursday (= 4). Compute the
205 the difference between this data in the one on TM and so determine
206 the weekday. */
207 int corr_year = 1900 + tm->tm_year - (tm->tm_mon < 2);
208 int wday = (-473
209 + (365 * (tm->tm_year - 70))
210 + (corr_year / 4)
211 - ((corr_year / 4) / 25) + ((corr_year / 4) % 25 < 0)
212 + (((corr_year / 4) / 25) / 4)
213 + __mon_yday[0][tm->tm_mon]
214 + tm->tm_mday - 1);
215 tm->tm_wday = ((wday % 7) + 7) % 7;
218 /* Compute the day of the year. */
219 static void
220 day_of_the_year (struct tm *tm)
222 tm->tm_yday = (__mon_yday[__isleap (1900 + tm->tm_year)][tm->tm_mon]
223 + (tm->tm_mday - 1));
226 static char *
227 #ifdef _LIBC
228 internal_function
229 #endif
230 strptime_internal __P ((const char *rp, const char *fmt, struct tm *tm,
231 enum locale_status *decided, int era_cnt));
233 static char *
234 #ifdef _LIBC
235 internal_function
236 #endif
237 strptime_internal (rp, fmt, tm, decided, era_cnt)
238 const char *rp;
239 const char *fmt;
240 struct tm *tm;
241 enum locale_status *decided;
242 int era_cnt;
244 const char *rp_backup;
245 int cnt;
246 size_t val;
247 int have_I, is_pm;
248 int century, want_century;
249 int want_era;
250 int have_wday, want_xday;
251 int have_yday;
252 int have_mon, have_mday;
253 int have_uweek, have_wweek;
254 int week_no;
255 size_t num_eras;
256 struct era_entry *era;
258 have_I = is_pm = 0;
259 century = -1;
260 want_century = 0;
261 want_era = 0;
262 era = NULL;
263 week_no = 0;
265 have_wday = want_xday = have_yday = have_mon = have_mday = have_uweek = 0;
266 have_wweek = 0;
268 while (*fmt != '\0')
270 /* A white space in the format string matches 0 more or white
271 space in the input string. */
272 if (isspace (*fmt))
274 while (isspace (*rp))
275 ++rp;
276 ++fmt;
277 continue;
280 /* Any character but `%' must be matched by the same character
281 in the iput string. */
282 if (*fmt != '%')
284 match_char (*fmt++, *rp++);
285 continue;
288 ++fmt;
289 #ifndef _NL_CURRENT
290 /* We need this for handling the `E' modifier. */
291 start_over:
292 #endif
294 /* Make back up of current processing pointer. */
295 rp_backup = rp;
297 switch (*fmt++)
299 case '%':
300 /* Match the `%' character itself. */
301 match_char ('%', *rp++);
302 break;
303 case 'a':
304 case 'A':
305 /* Match day of week. */
306 for (cnt = 0; cnt < 7; ++cnt)
308 #ifdef _NL_CURRENT
309 if (*decided !=raw)
311 if (match_string (_NL_CURRENT (LC_TIME, DAY_1 + cnt), rp))
313 if (*decided == not
314 && strcmp (_NL_CURRENT (LC_TIME, DAY_1 + cnt),
315 weekday_name[cnt]))
316 *decided = loc;
317 break;
319 if (match_string (_NL_CURRENT (LC_TIME, ABDAY_1 + cnt), rp))
321 if (*decided == not
322 && strcmp (_NL_CURRENT (LC_TIME, ABDAY_1 + cnt),
323 ab_weekday_name[cnt]))
324 *decided = loc;
325 break;
328 #endif
329 if (*decided != loc
330 && (match_string (weekday_name[cnt], rp)
331 || match_string (ab_weekday_name[cnt], rp)))
333 *decided = raw;
334 break;
337 if (cnt == 7)
338 /* Does not match a weekday name. */
339 return NULL;
340 tm->tm_wday = cnt;
341 have_wday = 1;
342 break;
343 case 'b':
344 case 'B':
345 case 'h':
346 /* Match month name. */
347 for (cnt = 0; cnt < 12; ++cnt)
349 #ifdef _NL_CURRENT
350 if (*decided !=raw)
352 if (match_string (_NL_CURRENT (LC_TIME, MON_1 + cnt), rp))
354 if (*decided == not
355 && strcmp (_NL_CURRENT (LC_TIME, MON_1 + cnt),
356 month_name[cnt]))
357 *decided = loc;
358 break;
360 if (match_string (_NL_CURRENT (LC_TIME, ABMON_1 + cnt), rp))
362 if (*decided == not
363 && strcmp (_NL_CURRENT (LC_TIME, ABMON_1 + cnt),
364 ab_month_name[cnt]))
365 *decided = loc;
366 break;
369 #endif
370 if (match_string (month_name[cnt], rp)
371 || match_string (ab_month_name[cnt], rp))
373 *decided = raw;
374 break;
377 if (cnt == 12)
378 /* Does not match a month name. */
379 return NULL;
380 tm->tm_mon = cnt;
381 want_xday = 1;
382 break;
383 case 'c':
384 /* Match locale's date and time format. */
385 #ifdef _NL_CURRENT
386 if (*decided != raw)
388 if (!recursive (_NL_CURRENT (LC_TIME, D_T_FMT)))
390 if (*decided == loc)
391 return NULL;
392 else
393 rp = rp_backup;
395 else
397 if (*decided == not &&
398 strcmp (_NL_CURRENT (LC_TIME, D_T_FMT), HERE_D_T_FMT))
399 *decided = loc;
400 want_xday = 1;
401 break;
403 *decided = raw;
405 #endif
406 if (!recursive (HERE_D_T_FMT))
407 return NULL;
408 want_xday = 1;
409 break;
410 case 'C':
411 /* Match century number. */
412 match_century:
413 get_number (0, 99, 2);
414 century = val;
415 want_xday = 1;
416 break;
417 case 'd':
418 case 'e':
419 /* Match day of month. */
420 get_number (1, 31, 2);
421 tm->tm_mday = val;
422 have_mday = 1;
423 want_xday = 1;
424 break;
425 case 'F':
426 if (!recursive ("%Y-%m-%d"))
427 return NULL;
428 want_xday = 1;
429 break;
430 case 'x':
431 #ifdef _NL_CURRENT
432 if (*decided != raw)
434 if (!recursive (_NL_CURRENT (LC_TIME, D_FMT)))
436 if (*decided == loc)
437 return NULL;
438 else
439 rp = rp_backup;
441 else
443 if (*decided == not
444 && strcmp (_NL_CURRENT (LC_TIME, D_FMT), HERE_D_FMT))
445 *decided = loc;
446 want_xday = 1;
447 break;
449 *decided = raw;
451 #endif
452 /* Fall through. */
453 case 'D':
454 /* Match standard day format. */
455 if (!recursive (HERE_D_FMT))
456 return NULL;
457 want_xday = 1;
458 break;
459 case 'k':
460 case 'H':
461 /* Match hour in 24-hour clock. */
462 get_number (0, 23, 2);
463 tm->tm_hour = val;
464 have_I = 0;
465 break;
466 case 'l':
467 /* Match hour in 12-hour clock. GNU extension. */
468 case 'I':
469 /* Match hour in 12-hour clock. */
470 get_number (1, 12, 2);
471 tm->tm_hour = val % 12;
472 have_I = 1;
473 break;
474 case 'j':
475 /* Match day number of year. */
476 get_number (1, 366, 3);
477 tm->tm_yday = val - 1;
478 have_yday = 1;
479 break;
480 case 'm':
481 /* Match number of month. */
482 get_number (1, 12, 2);
483 tm->tm_mon = val - 1;
484 have_mon = 1;
485 want_xday = 1;
486 break;
487 case 'M':
488 /* Match minute. */
489 get_number (0, 59, 2);
490 tm->tm_min = val;
491 break;
492 case 'n':
493 case 't':
494 /* Match any white space. */
495 while (isspace (*rp))
496 ++rp;
497 break;
498 case 'p':
499 /* Match locale's equivalent of AM/PM. */
500 #ifdef _NL_CURRENT
501 if (*decided != raw)
503 if (match_string (_NL_CURRENT (LC_TIME, AM_STR), rp))
505 if (strcmp (_NL_CURRENT (LC_TIME, AM_STR), HERE_AM_STR))
506 *decided = loc;
507 break;
509 if (match_string (_NL_CURRENT (LC_TIME, PM_STR), rp))
511 if (strcmp (_NL_CURRENT (LC_TIME, PM_STR), HERE_PM_STR))
512 *decided = loc;
513 is_pm = 1;
514 break;
516 *decided = raw;
518 #endif
519 if (!match_string (HERE_AM_STR, rp))
520 if (match_string (HERE_PM_STR, rp))
521 is_pm = 1;
522 else
523 return NULL;
524 break;
525 case 'r':
526 #ifdef _NL_CURRENT
527 if (*decided != raw)
529 if (!recursive (_NL_CURRENT (LC_TIME, T_FMT_AMPM)))
531 if (*decided == loc)
532 return NULL;
533 else
534 rp = rp_backup;
536 else
538 if (*decided == not &&
539 strcmp (_NL_CURRENT (LC_TIME, T_FMT_AMPM),
540 HERE_T_FMT_AMPM))
541 *decided = loc;
542 break;
544 *decided = raw;
546 #endif
547 if (!recursive (HERE_T_FMT_AMPM))
548 return NULL;
549 break;
550 case 'R':
551 if (!recursive ("%H:%M"))
552 return NULL;
553 break;
554 case 's':
556 /* The number of seconds may be very high so we cannot use
557 the `get_number' macro. Instead read the number
558 character for character and construct the result while
559 doing this. */
560 time_t secs = 0;
561 if (*rp < '0' || *rp > '9')
562 /* We need at least one digit. */
563 return NULL;
567 secs *= 10;
568 secs += *rp++ - '0';
570 while (*rp >= '0' && *rp <= '9');
572 if (localtime_r (&secs, tm) == NULL)
573 /* Error in function. */
574 return NULL;
576 break;
577 case 'S':
578 get_number (0, 61, 2);
579 tm->tm_sec = val;
580 break;
581 case 'X':
582 #ifdef _NL_CURRENT
583 if (*decided != raw)
585 if (!recursive (_NL_CURRENT (LC_TIME, T_FMT)))
587 if (*decided == loc)
588 return NULL;
589 else
590 rp = rp_backup;
592 else
594 if (strcmp (_NL_CURRENT (LC_TIME, T_FMT), HERE_T_FMT))
595 *decided = loc;
596 break;
598 *decided = raw;
600 #endif
601 /* Fall through. */
602 case 'T':
603 if (!recursive (HERE_T_FMT))
604 return NULL;
605 break;
606 case 'u':
607 get_number (1, 7, 1);
608 tm->tm_wday = val % 7;
609 have_wday = 1;
610 break;
611 case 'g':
612 get_number (0, 99, 2);
613 /* XXX This cannot determine any field in TM. */
614 break;
615 case 'G':
616 if (*rp < '0' || *rp > '9')
617 return NULL;
618 /* XXX Ignore the number since we would need some more
619 information to compute a real date. */
621 ++rp;
622 while (*rp >= '0' && *rp <= '9');
623 break;
624 case 'U':
625 get_number (0, 53, 2);
626 week_no = val;
627 have_uweek = 1;
628 break;
629 case 'W':
630 get_number (0, 53, 2);
631 week_no = val;
632 have_wweek = 1;
633 break;
634 case 'V':
635 get_number (0, 53, 2);
636 /* XXX This cannot determine any field in TM without some
637 information. */
638 break;
639 case 'w':
640 /* Match number of weekday. */
641 get_number (0, 6, 1);
642 tm->tm_wday = val;
643 have_wday = 1;
644 break;
645 case 'y':
646 match_year_in_century:
647 /* Match year within century. */
648 get_number (0, 99, 2);
649 /* The "Year 2000: The Millennium Rollover" paper suggests that
650 values in the range 69-99 refer to the twentieth century. */
651 tm->tm_year = val >= 69 ? val : val + 100;
652 /* Indicate that we want to use the century, if specified. */
653 want_century = 1;
654 want_xday = 1;
655 break;
656 case 'Y':
657 /* Match year including century number. */
658 get_number (0, 9999, 4);
659 tm->tm_year = val - 1900;
660 want_century = 0;
661 want_xday = 1;
662 break;
663 case 'Z':
664 /* XXX How to handle this? */
665 break;
666 case 'E':
667 #ifdef _NL_CURRENT
668 switch (*fmt++)
670 case 'c':
671 /* Match locale's alternate date and time format. */
672 if (*decided != raw)
674 const char *fmt = _NL_CURRENT (LC_TIME, ERA_D_T_FMT);
676 if (*fmt == '\0')
677 fmt = _NL_CURRENT (LC_TIME, D_T_FMT);
679 if (!recursive (fmt))
681 if (*decided == loc)
682 return NULL;
683 else
684 rp = rp_backup;
686 else
688 if (strcmp (fmt, HERE_D_T_FMT))
689 *decided = loc;
690 want_xday = 1;
691 break;
693 *decided = raw;
695 /* The C locale has no era information, so use the
696 normal representation. */
697 if (!recursive (HERE_D_T_FMT))
698 return NULL;
699 want_xday = 1;
700 break;
701 case 'C':
702 if (*decided != raw)
704 if (era_cnt >= 0)
706 era = _nl_select_era_entry (era_cnt);
707 if (match_string (era->era_name, rp))
709 *decided = loc;
710 break;
712 else
713 return NULL;
715 else
717 num_eras = _NL_CURRENT_WORD (LC_TIME,
718 _NL_TIME_ERA_NUM_ENTRIES);
719 for (era_cnt = 0; era_cnt < (int) num_eras;
720 ++era_cnt, rp = rp_backup)
722 era = _nl_select_era_entry (era_cnt);
723 if (match_string (era->era_name, rp))
725 *decided = loc;
726 break;
729 if (era_cnt == (int) num_eras)
731 era_cnt = -1;
732 if (*decided == loc)
733 return NULL;
735 else
736 break;
739 *decided = raw;
741 /* The C locale has no era information, so use the
742 normal representation. */
743 goto match_century;
744 case 'y':
745 if (*decided == raw)
746 goto match_year_in_century;
748 get_number(0, 9999, 4);
749 tm->tm_year = val;
750 want_era = 1;
751 want_xday = 1;
752 want_century = 1;
753 break;
754 case 'Y':
755 if (*decided != raw)
757 num_eras = _NL_CURRENT_WORD (LC_TIME,
758 _NL_TIME_ERA_NUM_ENTRIES);
759 for (era_cnt = 0; era_cnt < (int) num_eras;
760 ++era_cnt, rp = rp_backup)
762 era = _nl_select_era_entry (era_cnt);
763 if (recursive (era->era_format))
764 break;
766 if (era_cnt == (int) num_eras)
768 era_cnt = -1;
769 if (*decided == loc)
770 return NULL;
771 else
772 rp = rp_backup;
774 else
776 *decided = loc;
777 era_cnt = -1;
778 break;
781 *decided = raw;
783 get_number (0, 9999, 4);
784 tm->tm_year = val - 1900;
785 want_century = 0;
786 want_xday = 1;
787 break;
788 case 'x':
789 if (*decided != raw)
791 const char *fmt = _NL_CURRENT (LC_TIME, ERA_D_FMT);
793 if (*fmt == '\0')
794 fmt = _NL_CURRENT (LC_TIME, D_FMT);
796 if (!recursive (fmt))
798 if (*decided == loc)
799 return NULL;
800 else
801 rp = rp_backup;
803 else
805 if (strcmp (fmt, HERE_D_FMT))
806 *decided = loc;
807 break;
809 *decided = raw;
811 if (!recursive (HERE_D_FMT))
812 return NULL;
813 break;
814 case 'X':
815 if (*decided != raw)
817 const char *fmt = _NL_CURRENT (LC_TIME, ERA_T_FMT);
819 if (*fmt == '\0')
820 fmt = _NL_CURRENT (LC_TIME, T_FMT);
822 if (!recursive (fmt))
824 if (*decided == loc)
825 return NULL;
826 else
827 rp = rp_backup;
829 else
831 if (strcmp (fmt, HERE_T_FMT))
832 *decided = loc;
833 break;
835 *decided = raw;
837 if (!recursive (HERE_T_FMT))
838 return NULL;
839 break;
840 default:
841 return NULL;
843 break;
844 #else
845 /* We have no information about the era format. Just use
846 the normal format. */
847 if (*fmt != 'c' && *fmt != 'C' && *fmt != 'y' && *fmt != 'Y'
848 && *fmt != 'x' && *fmt != 'X')
849 /* This is an illegal format. */
850 return NULL;
852 goto start_over;
853 #endif
854 case 'O':
855 switch (*fmt++)
857 case 'd':
858 case 'e':
859 /* Match day of month using alternate numeric symbols. */
860 get_alt_number (1, 31, 2);
861 tm->tm_mday = val;
862 have_mday = 1;
863 want_xday = 1;
864 break;
865 case 'H':
866 /* Match hour in 24-hour clock using alternate numeric
867 symbols. */
868 get_alt_number (0, 23, 2);
869 tm->tm_hour = val;
870 have_I = 0;
871 break;
872 case 'I':
873 /* Match hour in 12-hour clock using alternate numeric
874 symbols. */
875 get_alt_number (1, 12, 2);
876 tm->tm_hour = val % 12;
877 have_I = 1;
878 break;
879 case 'm':
880 /* Match month using alternate numeric symbols. */
881 get_alt_number (1, 12, 2);
882 tm->tm_mon = val - 1;
883 have_mon = 1;
884 want_xday = 1;
885 break;
886 case 'M':
887 /* Match minutes using alternate numeric symbols. */
888 get_alt_number (0, 59, 2);
889 tm->tm_min = val;
890 break;
891 case 'S':
892 /* Match seconds using alternate numeric symbols. */
893 get_alt_number (0, 61, 2);
894 tm->tm_sec = val;
895 break;
896 case 'U':
897 get_alt_number (0, 53, 2);
898 week_no = val;
899 have_uweek = 1;
900 break;
901 case 'W':
902 get_alt_number (0, 53, 2);
903 week_no = val;
904 have_wweek = 1;
905 break;
906 case 'V':
907 get_alt_number (0, 53, 2);
908 /* XXX This cannot determine any field in TM without
909 further information. */
910 break;
911 case 'w':
912 /* Match number of weekday using alternate numeric symbols. */
913 get_alt_number (0, 6, 1);
914 tm->tm_wday = val;
915 have_wday = 1;
916 break;
917 case 'y':
918 /* Match year within century using alternate numeric symbols. */
919 get_alt_number (0, 99, 2);
920 tm->tm_year = val >= 69 ? val : val + 100;
921 want_xday = 1;
922 break;
923 default:
924 return NULL;
926 break;
927 default:
928 return NULL;
932 if (have_I && is_pm)
933 tm->tm_hour += 12;
935 if (century != -1)
937 if (want_century)
938 tm->tm_year = tm->tm_year % 100 + (century - 19) * 100;
939 else
940 /* Only the century, but not the year. Strange, but so be it. */
941 tm->tm_year = (century - 19) * 100;
944 if (era_cnt != -1)
946 era = _nl_select_era_entry (era_cnt);
947 if (want_era)
948 tm->tm_year = (era->start_date[0]
949 + ((tm->tm_year - era->offset)
950 * era->absolute_direction));
951 else
952 /* Era start year assumed. */
953 tm->tm_year = era->start_date[0];
955 else
956 if (want_era)
958 /* No era found but we have seen an E modifier. Rectify some
959 values. */
960 if (want_century && century == -1 && tm->tm_year < 69)
961 tm->tm_year += 100;
964 if (want_xday && !have_wday)
966 if ( !(have_mon && have_mday) && have_yday)
968 /* We don't have tm_mon and/or tm_mday, compute them. */
969 int t_mon = 0;
970 while (__mon_yday[__isleap(1900 + tm->tm_year)][t_mon] <= tm->tm_yday)
971 t_mon++;
972 if (!have_mon)
973 tm->tm_mon = t_mon - 1;
974 if (!have_mday)
975 tm->tm_mday =
976 (tm->tm_yday
977 - __mon_yday[__isleap(1900 + tm->tm_year)][t_mon - 1] + 1);
979 day_of_the_week (tm);
982 if (want_xday && !have_yday)
983 day_of_the_year (tm);
985 if ((have_uweek || have_wweek) && have_wday)
987 int save_wday = tm->tm_wday;
988 int save_mday = tm->tm_mday;
989 int save_mon = tm->tm_mon;
990 int w_offset = have_uweek ? 0 : 1;
992 tm->tm_mday = 1;
993 tm->tm_mon = 0;
994 day_of_the_week (tm);
995 if (have_mday)
996 tm->tm_mday = save_mday;
997 if (have_mon)
998 tm->tm_mon = save_mon;
1000 if (!have_yday)
1001 tm->tm_yday = ((7 - (tm->tm_wday - w_offset)) % 7
1002 + (week_no - 1) *7
1003 + save_wday - w_offset);
1005 if (!have_mday || !have_mon)
1007 int t_mon = 0;
1008 while (__mon_yday[__isleap(1900 + tm->tm_year)][t_mon]
1009 <= tm->tm_yday)
1010 t_mon++;
1011 if (!have_mon)
1012 tm->tm_mon = t_mon - 1;
1013 if (!have_mday)
1014 tm->tm_mday =
1015 (tm->tm_yday
1016 - __mon_yday[__isleap(1900 + tm->tm_year)][t_mon - 1] + 1);
1019 tm->tm_wday = save_wday;
1022 return (char *) rp;
1026 char *
1027 strptime (buf, format, tm)
1028 const char *buf;
1029 const char *format;
1030 struct tm *tm;
1032 enum locale_status decided;
1034 #ifdef _NL_CURRENT
1035 decided = not;
1036 #else
1037 decided = raw;
1038 #endif
1039 return strptime_internal (buf, format, tm, &decided, -1);