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