Update.
[glibc.git] / time / strptime.c
blobf1279051026f0a8ce13c1671cf7cb23232b9bc45
1 /* Convert a string representation of time to a time value.
2 Copyright (C) 1996-2000, 2001, 2002 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 attribute_hidden;
138 # define weekday_name (&_nl_C_LC_TIME.values[_NL_ITEM_INDEX (DAY_1)].string)
139 # define ab_weekday_name \
140 (&_nl_C_LC_TIME.values[_NL_ITEM_INDEX (ABDAY_1)].string)
141 # define month_name (&_nl_C_LC_TIME.values[_NL_ITEM_INDEX (MON_1)].string)
142 # define ab_month_name (&_nl_C_LC_TIME.values[_NL_ITEM_INDEX (ABMON_1)].string)
143 # define HERE_D_T_FMT (_nl_C_LC_TIME.values[_NL_ITEM_INDEX (D_T_FMT)].string)
144 # define HERE_D_FMT (_nl_C_LC_TIME.values[_NL_ITEM_INDEX (D_FMT)].string)
145 # define HERE_AM_STR (_nl_C_LC_TIME.values[_NL_ITEM_INDEX (AM_STR)].string)
146 # define HERE_PM_STR (_nl_C_LC_TIME.values[_NL_ITEM_INDEX (PM_STR)].string)
147 # define HERE_T_FMT_AMPM \
148 (_nl_C_LC_TIME.values[_NL_ITEM_INDEX (T_FMT_AMPM)].string)
149 # define HERE_T_FMT (_nl_C_LC_TIME.values[_NL_ITEM_INDEX (T_FMT)].string)
151 # define strncasecmp(s1, s2, n) __strncasecmp (s1, s2, n)
152 #else
153 static char const weekday_name[][10] =
155 "Sunday", "Monday", "Tuesday", "Wednesday",
156 "Thursday", "Friday", "Saturday"
158 static char const ab_weekday_name[][4] =
160 "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
162 static char const month_name[][10] =
164 "January", "February", "March", "April", "May", "June",
165 "July", "August", "September", "October", "November", "December"
167 static char const ab_month_name[][4] =
169 "Jan", "Feb", "Mar", "Apr", "May", "Jun",
170 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
172 # define HERE_D_T_FMT "%a %b %e %H:%M:%S %Y"
173 # define HERE_D_FMT "%m/%d/%y"
174 # define HERE_AM_STR "AM"
175 # define HERE_PM_STR "PM"
176 # define HERE_T_FMT_AMPM "%I:%M:%S %p"
177 # define HERE_T_FMT "%H:%M:%S"
179 const unsigned short int __mon_yday[2][13] =
181 /* Normal years. */
182 { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 },
183 /* Leap years. */
184 { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 }
186 #endif
188 /* Status of lookup: do we use the locale data or the raw data? */
189 enum locale_status { not, loc, raw };
192 #ifndef __isleap
193 /* Nonzero if YEAR is a leap year (every 4 years,
194 except every 100th isn't, and every 400th is). */
195 # define __isleap(year) \
196 ((year) % 4 == 0 && ((year) % 100 != 0 || (year) % 400 == 0))
197 #endif
199 /* Compute the day of the week. */
200 static void
201 day_of_the_week (struct tm *tm)
203 /* We know that January 1st 1970 was a Thursday (= 4). Compute the
204 the difference between this data in the one on TM and so determine
205 the weekday. */
206 int corr_year = 1900 + tm->tm_year - (tm->tm_mon < 2);
207 int wday = (-473
208 + (365 * (tm->tm_year - 70))
209 + (corr_year / 4)
210 - ((corr_year / 4) / 25) + ((corr_year / 4) % 25 < 0)
211 + (((corr_year / 4) / 25) / 4)
212 + __mon_yday[0][tm->tm_mon]
213 + tm->tm_mday - 1);
214 tm->tm_wday = ((wday % 7) + 7) % 7;
217 /* Compute the day of the year. */
218 static void
219 day_of_the_year (struct tm *tm)
221 tm->tm_yday = (__mon_yday[__isleap (1900 + tm->tm_year)][tm->tm_mon]
222 + (tm->tm_mday - 1));
225 static char *
226 #ifdef _LIBC
227 internal_function
228 #endif
229 strptime_internal __P ((const char *rp, const char *fmt, struct tm *tm,
230 enum locale_status *decided, int era_cnt));
232 static char *
233 #ifdef _LIBC
234 internal_function
235 #endif
236 strptime_internal (rp, fmt, tm, decided, era_cnt)
237 const char *rp;
238 const char *fmt;
239 struct tm *tm;
240 enum locale_status *decided;
241 int era_cnt;
243 const char *rp_backup;
244 int cnt;
245 size_t val;
246 int have_I, is_pm;
247 int century, want_century;
248 int want_era;
249 int have_wday, want_xday;
250 int have_yday;
251 int have_mon, have_mday;
252 int have_uweek, have_wweek;
253 int week_no;
254 size_t num_eras;
255 struct era_entry *era;
257 have_I = is_pm = 0;
258 century = -1;
259 want_century = 0;
260 want_era = 0;
261 era = NULL;
262 week_no = 0;
264 have_wday = want_xday = have_yday = have_mon = have_mday = have_uweek = 0;
265 have_wweek = 0;
267 while (*fmt != '\0')
269 /* A white space in the format string matches 0 more or white
270 space in the input string. */
271 if (isspace (*fmt))
273 while (isspace (*rp))
274 ++rp;
275 ++fmt;
276 continue;
279 /* Any character but `%' must be matched by the same character
280 in the iput string. */
281 if (*fmt != '%')
283 match_char (*fmt++, *rp++);
284 continue;
287 ++fmt;
288 #ifndef _NL_CURRENT
289 /* We need this for handling the `E' modifier. */
290 start_over:
291 #endif
293 /* Make back up of current processing pointer. */
294 rp_backup = rp;
296 switch (*fmt++)
298 case '%':
299 /* Match the `%' character itself. */
300 match_char ('%', *rp++);
301 break;
302 case 'a':
303 case 'A':
304 /* Match day of week. */
305 for (cnt = 0; cnt < 7; ++cnt)
307 #ifdef _NL_CURRENT
308 if (*decided !=raw)
310 if (match_string (_NL_CURRENT (LC_TIME, DAY_1 + cnt), rp))
312 if (*decided == not
313 && strcmp (_NL_CURRENT (LC_TIME, DAY_1 + cnt),
314 weekday_name[cnt]))
315 *decided = loc;
316 break;
318 if (match_string (_NL_CURRENT (LC_TIME, ABDAY_1 + cnt), rp))
320 if (*decided == not
321 && strcmp (_NL_CURRENT (LC_TIME, ABDAY_1 + cnt),
322 ab_weekday_name[cnt]))
323 *decided = loc;
324 break;
327 #endif
328 if (*decided != loc
329 && (match_string (weekday_name[cnt], rp)
330 || match_string (ab_weekday_name[cnt], rp)))
332 *decided = raw;
333 break;
336 if (cnt == 7)
337 /* Does not match a weekday name. */
338 return NULL;
339 tm->tm_wday = cnt;
340 have_wday = 1;
341 break;
342 case 'b':
343 case 'B':
344 case 'h':
345 /* Match month name. */
346 for (cnt = 0; cnt < 12; ++cnt)
348 #ifdef _NL_CURRENT
349 if (*decided !=raw)
351 if (match_string (_NL_CURRENT (LC_TIME, MON_1 + cnt), rp))
353 if (*decided == not
354 && strcmp (_NL_CURRENT (LC_TIME, MON_1 + cnt),
355 month_name[cnt]))
356 *decided = loc;
357 break;
359 if (match_string (_NL_CURRENT (LC_TIME, ABMON_1 + cnt), rp))
361 if (*decided == not
362 && strcmp (_NL_CURRENT (LC_TIME, ABMON_1 + cnt),
363 ab_month_name[cnt]))
364 *decided = loc;
365 break;
368 #endif
369 if (match_string (month_name[cnt], rp)
370 || match_string (ab_month_name[cnt], rp))
372 *decided = raw;
373 break;
376 if (cnt == 12)
377 /* Does not match a month name. */
378 return NULL;
379 tm->tm_mon = cnt;
380 want_xday = 1;
381 break;
382 case 'c':
383 /* Match locale's date and time format. */
384 #ifdef _NL_CURRENT
385 if (*decided != raw)
387 if (!recursive (_NL_CURRENT (LC_TIME, D_T_FMT)))
389 if (*decided == loc)
390 return NULL;
391 else
392 rp = rp_backup;
394 else
396 if (*decided == not &&
397 strcmp (_NL_CURRENT (LC_TIME, D_T_FMT), HERE_D_T_FMT))
398 *decided = loc;
399 want_xday = 1;
400 break;
402 *decided = raw;
404 #endif
405 if (!recursive (HERE_D_T_FMT))
406 return NULL;
407 want_xday = 1;
408 break;
409 case 'C':
410 /* Match century number. */
411 match_century:
412 get_number (0, 99, 2);
413 century = val;
414 want_xday = 1;
415 break;
416 case 'd':
417 case 'e':
418 /* Match day of month. */
419 get_number (1, 31, 2);
420 tm->tm_mday = val;
421 have_mday = 1;
422 want_xday = 1;
423 break;
424 case 'F':
425 if (!recursive ("%Y-%m-%d"))
426 return NULL;
427 want_xday = 1;
428 break;
429 case 'x':
430 #ifdef _NL_CURRENT
431 if (*decided != raw)
433 if (!recursive (_NL_CURRENT (LC_TIME, D_FMT)))
435 if (*decided == loc)
436 return NULL;
437 else
438 rp = rp_backup;
440 else
442 if (*decided == not
443 && strcmp (_NL_CURRENT (LC_TIME, D_FMT), HERE_D_FMT))
444 *decided = loc;
445 want_xday = 1;
446 break;
448 *decided = raw;
450 #endif
451 /* Fall through. */
452 case 'D':
453 /* Match standard day format. */
454 if (!recursive (HERE_D_FMT))
455 return NULL;
456 want_xday = 1;
457 break;
458 case 'k':
459 case 'H':
460 /* Match hour in 24-hour clock. */
461 get_number (0, 23, 2);
462 tm->tm_hour = val;
463 have_I = 0;
464 break;
465 case 'l':
466 /* Match hour in 12-hour clock. GNU extension. */
467 case 'I':
468 /* Match hour in 12-hour clock. */
469 get_number (1, 12, 2);
470 tm->tm_hour = val % 12;
471 have_I = 1;
472 break;
473 case 'j':
474 /* Match day number of year. */
475 get_number (1, 366, 3);
476 tm->tm_yday = val - 1;
477 have_yday = 1;
478 break;
479 case 'm':
480 /* Match number of month. */
481 get_number (1, 12, 2);
482 tm->tm_mon = val - 1;
483 have_mon = 1;
484 want_xday = 1;
485 break;
486 case 'M':
487 /* Match minute. */
488 get_number (0, 59, 2);
489 tm->tm_min = val;
490 break;
491 case 'n':
492 case 't':
493 /* Match any white space. */
494 while (isspace (*rp))
495 ++rp;
496 break;
497 case 'p':
498 /* Match locale's equivalent of AM/PM. */
499 #ifdef _NL_CURRENT
500 if (*decided != raw)
502 if (match_string (_NL_CURRENT (LC_TIME, AM_STR), rp))
504 if (strcmp (_NL_CURRENT (LC_TIME, AM_STR), HERE_AM_STR))
505 *decided = loc;
506 break;
508 if (match_string (_NL_CURRENT (LC_TIME, PM_STR), rp))
510 if (strcmp (_NL_CURRENT (LC_TIME, PM_STR), HERE_PM_STR))
511 *decided = loc;
512 is_pm = 1;
513 break;
515 *decided = raw;
517 #endif
518 if (!match_string (HERE_AM_STR, rp))
519 if (match_string (HERE_PM_STR, rp))
520 is_pm = 1;
521 else
522 return NULL;
523 break;
524 case 'r':
525 #ifdef _NL_CURRENT
526 if (*decided != raw)
528 if (!recursive (_NL_CURRENT (LC_TIME, T_FMT_AMPM)))
530 if (*decided == loc)
531 return NULL;
532 else
533 rp = rp_backup;
535 else
537 if (*decided == not &&
538 strcmp (_NL_CURRENT (LC_TIME, T_FMT_AMPM),
539 HERE_T_FMT_AMPM))
540 *decided = loc;
541 break;
543 *decided = raw;
545 #endif
546 if (!recursive (HERE_T_FMT_AMPM))
547 return NULL;
548 break;
549 case 'R':
550 if (!recursive ("%H:%M"))
551 return NULL;
552 break;
553 case 's':
555 /* The number of seconds may be very high so we cannot use
556 the `get_number' macro. Instead read the number
557 character for character and construct the result while
558 doing this. */
559 time_t secs = 0;
560 if (*rp < '0' || *rp > '9')
561 /* We need at least one digit. */
562 return NULL;
566 secs *= 10;
567 secs += *rp++ - '0';
569 while (*rp >= '0' && *rp <= '9');
571 if (localtime_r (&secs, tm) == NULL)
572 /* Error in function. */
573 return NULL;
575 break;
576 case 'S':
577 get_number (0, 61, 2);
578 tm->tm_sec = val;
579 break;
580 case 'X':
581 #ifdef _NL_CURRENT
582 if (*decided != raw)
584 if (!recursive (_NL_CURRENT (LC_TIME, T_FMT)))
586 if (*decided == loc)
587 return NULL;
588 else
589 rp = rp_backup;
591 else
593 if (strcmp (_NL_CURRENT (LC_TIME, T_FMT), HERE_T_FMT))
594 *decided = loc;
595 break;
597 *decided = raw;
599 #endif
600 /* Fall through. */
601 case 'T':
602 if (!recursive (HERE_T_FMT))
603 return NULL;
604 break;
605 case 'u':
606 get_number (1, 7, 1);
607 tm->tm_wday = val % 7;
608 have_wday = 1;
609 break;
610 case 'g':
611 get_number (0, 99, 2);
612 /* XXX This cannot determine any field in TM. */
613 break;
614 case 'G':
615 if (*rp < '0' || *rp > '9')
616 return NULL;
617 /* XXX Ignore the number since we would need some more
618 information to compute a real date. */
620 ++rp;
621 while (*rp >= '0' && *rp <= '9');
622 break;
623 case 'U':
624 get_number (0, 53, 2);
625 week_no = val;
626 have_uweek = 1;
627 break;
628 case 'W':
629 get_number (0, 53, 2);
630 week_no = val;
631 have_wweek = 1;
632 break;
633 case 'V':
634 get_number (0, 53, 2);
635 /* XXX This cannot determine any field in TM without some
636 information. */
637 break;
638 case 'w':
639 /* Match number of weekday. */
640 get_number (0, 6, 1);
641 tm->tm_wday = val;
642 have_wday = 1;
643 break;
644 case 'y':
645 match_year_in_century:
646 /* Match year within century. */
647 get_number (0, 99, 2);
648 /* The "Year 2000: The Millennium Rollover" paper suggests that
649 values in the range 69-99 refer to the twentieth century. */
650 tm->tm_year = val >= 69 ? val : val + 100;
651 /* Indicate that we want to use the century, if specified. */
652 want_century = 1;
653 want_xday = 1;
654 break;
655 case 'Y':
656 /* Match year including century number. */
657 get_number (0, 9999, 4);
658 tm->tm_year = val - 1900;
659 want_century = 0;
660 want_xday = 1;
661 break;
662 case 'Z':
663 /* XXX How to handle this? */
664 break;
665 case 'E':
666 #ifdef _NL_CURRENT
667 switch (*fmt++)
669 case 'c':
670 /* Match locale's alternate date and time format. */
671 if (*decided != raw)
673 const char *fmt = _NL_CURRENT (LC_TIME, ERA_D_T_FMT);
675 if (*fmt == '\0')
676 fmt = _NL_CURRENT (LC_TIME, D_T_FMT);
678 if (!recursive (fmt))
680 if (*decided == loc)
681 return NULL;
682 else
683 rp = rp_backup;
685 else
687 if (strcmp (fmt, HERE_D_T_FMT))
688 *decided = loc;
689 want_xday = 1;
690 break;
692 *decided = raw;
694 /* The C locale has no era information, so use the
695 normal representation. */
696 if (!recursive (HERE_D_T_FMT))
697 return NULL;
698 want_xday = 1;
699 break;
700 case 'C':
701 if (*decided != raw)
703 if (era_cnt >= 0)
705 era = _nl_select_era_entry (era_cnt);
706 if (match_string (era->era_name, rp))
708 *decided = loc;
709 break;
711 else
712 return NULL;
714 else
716 num_eras = _NL_CURRENT_WORD (LC_TIME,
717 _NL_TIME_ERA_NUM_ENTRIES);
718 for (era_cnt = 0; era_cnt < (int) num_eras;
719 ++era_cnt, rp = rp_backup)
721 era = _nl_select_era_entry (era_cnt);
722 if (match_string (era->era_name, rp))
724 *decided = loc;
725 break;
728 if (era_cnt == (int) num_eras)
730 era_cnt = -1;
731 if (*decided == loc)
732 return NULL;
734 else
735 break;
738 *decided = raw;
740 /* The C locale has no era information, so use the
741 normal representation. */
742 goto match_century;
743 case 'y':
744 if (*decided == raw)
745 goto match_year_in_century;
747 get_number(0, 9999, 4);
748 tm->tm_year = val;
749 want_era = 1;
750 want_xday = 1;
751 want_century = 1;
752 break;
753 case 'Y':
754 if (*decided != raw)
756 num_eras = _NL_CURRENT_WORD (LC_TIME,
757 _NL_TIME_ERA_NUM_ENTRIES);
758 for (era_cnt = 0; era_cnt < (int) num_eras;
759 ++era_cnt, rp = rp_backup)
761 era = _nl_select_era_entry (era_cnt);
762 if (recursive (era->era_format))
763 break;
765 if (era_cnt == (int) num_eras)
767 era_cnt = -1;
768 if (*decided == loc)
769 return NULL;
770 else
771 rp = rp_backup;
773 else
775 *decided = loc;
776 era_cnt = -1;
777 break;
780 *decided = raw;
782 get_number (0, 9999, 4);
783 tm->tm_year = val - 1900;
784 want_century = 0;
785 want_xday = 1;
786 break;
787 case 'x':
788 if (*decided != raw)
790 const char *fmt = _NL_CURRENT (LC_TIME, ERA_D_FMT);
792 if (*fmt == '\0')
793 fmt = _NL_CURRENT (LC_TIME, D_FMT);
795 if (!recursive (fmt))
797 if (*decided == loc)
798 return NULL;
799 else
800 rp = rp_backup;
802 else
804 if (strcmp (fmt, HERE_D_FMT))
805 *decided = loc;
806 break;
808 *decided = raw;
810 if (!recursive (HERE_D_FMT))
811 return NULL;
812 break;
813 case 'X':
814 if (*decided != raw)
816 const char *fmt = _NL_CURRENT (LC_TIME, ERA_T_FMT);
818 if (*fmt == '\0')
819 fmt = _NL_CURRENT (LC_TIME, T_FMT);
821 if (!recursive (fmt))
823 if (*decided == loc)
824 return NULL;
825 else
826 rp = rp_backup;
828 else
830 if (strcmp (fmt, HERE_T_FMT))
831 *decided = loc;
832 break;
834 *decided = raw;
836 if (!recursive (HERE_T_FMT))
837 return NULL;
838 break;
839 default:
840 return NULL;
842 break;
843 #else
844 /* We have no information about the era format. Just use
845 the normal format. */
846 if (*fmt != 'c' && *fmt != 'C' && *fmt != 'y' && *fmt != 'Y'
847 && *fmt != 'x' && *fmt != 'X')
848 /* This is an illegal format. */
849 return NULL;
851 goto start_over;
852 #endif
853 case 'O':
854 switch (*fmt++)
856 case 'd':
857 case 'e':
858 /* Match day of month using alternate numeric symbols. */
859 get_alt_number (1, 31, 2);
860 tm->tm_mday = val;
861 have_mday = 1;
862 want_xday = 1;
863 break;
864 case 'H':
865 /* Match hour in 24-hour clock using alternate numeric
866 symbols. */
867 get_alt_number (0, 23, 2);
868 tm->tm_hour = val;
869 have_I = 0;
870 break;
871 case 'I':
872 /* Match hour in 12-hour clock using alternate numeric
873 symbols. */
874 get_alt_number (1, 12, 2);
875 tm->tm_hour = val % 12;
876 have_I = 1;
877 break;
878 case 'm':
879 /* Match month using alternate numeric symbols. */
880 get_alt_number (1, 12, 2);
881 tm->tm_mon = val - 1;
882 have_mon = 1;
883 want_xday = 1;
884 break;
885 case 'M':
886 /* Match minutes using alternate numeric symbols. */
887 get_alt_number (0, 59, 2);
888 tm->tm_min = val;
889 break;
890 case 'S':
891 /* Match seconds using alternate numeric symbols. */
892 get_alt_number (0, 61, 2);
893 tm->tm_sec = val;
894 break;
895 case 'U':
896 get_alt_number (0, 53, 2);
897 week_no = val;
898 have_uweek = 1;
899 break;
900 case 'W':
901 get_alt_number (0, 53, 2);
902 week_no = val;
903 have_wweek = 1;
904 break;
905 case 'V':
906 get_alt_number (0, 53, 2);
907 /* XXX This cannot determine any field in TM without
908 further information. */
909 break;
910 case 'w':
911 /* Match number of weekday using alternate numeric symbols. */
912 get_alt_number (0, 6, 1);
913 tm->tm_wday = val;
914 have_wday = 1;
915 break;
916 case 'y':
917 /* Match year within century using alternate numeric symbols. */
918 get_alt_number (0, 99, 2);
919 tm->tm_year = val >= 69 ? val : val + 100;
920 want_xday = 1;
921 break;
922 default:
923 return NULL;
925 break;
926 default:
927 return NULL;
931 if (have_I && is_pm)
932 tm->tm_hour += 12;
934 if (century != -1)
936 if (want_century)
937 tm->tm_year = tm->tm_year % 100 + (century - 19) * 100;
938 else
939 /* Only the century, but not the year. Strange, but so be it. */
940 tm->tm_year = (century - 19) * 100;
943 if (era_cnt != -1)
945 era = _nl_select_era_entry (era_cnt);
946 if (want_era)
947 tm->tm_year = (era->start_date[0]
948 + ((tm->tm_year - era->offset)
949 * era->absolute_direction));
950 else
951 /* Era start year assumed. */
952 tm->tm_year = era->start_date[0];
954 else
955 if (want_era)
957 /* No era found but we have seen an E modifier. Rectify some
958 values. */
959 if (want_century && century == -1 && tm->tm_year < 69)
960 tm->tm_year += 100;
963 if (want_xday && !have_wday)
965 if ( !(have_mon && have_mday) && have_yday)
967 /* We don't have tm_mon and/or tm_mday, compute them. */
968 int t_mon = 0;
969 while (__mon_yday[__isleap(1900 + tm->tm_year)][t_mon] <= tm->tm_yday)
970 t_mon++;
971 if (!have_mon)
972 tm->tm_mon = t_mon - 1;
973 if (!have_mday)
974 tm->tm_mday =
975 (tm->tm_yday
976 - __mon_yday[__isleap(1900 + tm->tm_year)][t_mon - 1] + 1);
978 day_of_the_week (tm);
981 if (want_xday && !have_yday)
982 day_of_the_year (tm);
984 if ((have_uweek || have_wweek) && have_wday)
986 int save_wday = tm->tm_wday;
987 int save_mday = tm->tm_mday;
988 int save_mon = tm->tm_mon;
989 int w_offset = have_uweek ? 0 : 1;
991 tm->tm_mday = 1;
992 tm->tm_mon = 0;
993 day_of_the_week (tm);
994 if (have_mday)
995 tm->tm_mday = save_mday;
996 if (have_mon)
997 tm->tm_mon = save_mon;
999 if (!have_yday)
1000 tm->tm_yday = ((7 - (tm->tm_wday - w_offset)) % 7
1001 + (week_no - 1) *7
1002 + save_wday - w_offset);
1004 if (!have_mday || !have_mon)
1006 int t_mon = 0;
1007 while (__mon_yday[__isleap(1900 + tm->tm_year)][t_mon]
1008 <= tm->tm_yday)
1009 t_mon++;
1010 if (!have_mon)
1011 tm->tm_mon = t_mon - 1;
1012 if (!have_mday)
1013 tm->tm_mday =
1014 (tm->tm_yday
1015 - __mon_yday[__isleap(1900 + tm->tm_year)][t_mon - 1] + 1);
1018 tm->tm_wday = save_wday;
1021 return (char *) rp;
1025 char *
1026 strptime (buf, format, tm)
1027 const char *buf;
1028 const char *format;
1029 struct tm *tm;
1031 enum locale_status decided;
1033 #ifdef _NL_CURRENT
1034 decided = not;
1035 #else
1036 decided = raw;
1037 #endif
1038 return strptime_internal (buf, format, tm, &decided, -1);
1040 #ifdef _LIBC
1041 libc_hidden_def (strptime)
1042 #endif