vasnprintf: Don't use %n on modern, ISO C 99 compliant platforms.
[gnulib.git] / lib / strptime.c
blobb97fa5c32f22de7068008db1e9af34625d37b194
1 /* Copyright (C) 2002, 2004-2005, 2007, 2009-2020 Free Software Foundation,
2 Inc.
3 This file is part of the GNU C Library.
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2, or (at your option)
8 any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License along
16 with this program; if not, see <https://www.gnu.org/licenses/>. */
18 #ifndef _LIBC
19 # include <config.h>
20 #endif
22 #include <time.h>
24 #include <assert.h>
25 #include <ctype.h>
26 #ifdef _LIBC
27 # include <langinfo.h>
28 #endif
29 #include <limits.h>
30 #include <string.h>
31 #include <strings.h>
32 #include <stdbool.h>
34 #ifdef _LIBC
35 # include "../locale/localeinfo.h"
36 #endif
38 #ifndef _LIBC
39 enum ptime_locale_status { not, loc, raw };
40 #endif
44 #define match_char(ch1, ch2) if (ch1 != ch2) return NULL
45 #if defined _LIBC && defined __GNUC__ && __GNUC__ >= 2
46 # define match_string(cs1, s2) \
47 ({ size_t len = strlen (cs1); \
48 int result = __strncasecmp_l ((cs1), (s2), len, locale) == 0; \
49 if (result) (s2) += len; \
50 result; })
51 #else
52 /* Oh come on. Get a reasonable compiler. */
53 # define match_string(cs1, s2) \
54 (strncasecmp ((cs1), (s2), strlen (cs1)) ? 0 : ((s2) += strlen (cs1), 1))
55 #endif
56 /* We intentionally do not use isdigit() for testing because this will
57 lead to problems with the wide character version. */
58 #define get_number(from, to, n) \
59 do { \
60 int __n = n; \
61 val = 0; \
62 while (*rp == ' ') \
63 ++rp; \
64 if (*rp < '0' || *rp > '9') \
65 return NULL; \
66 do { \
67 val *= 10; \
68 val += *rp++ - '0'; \
69 } while (--__n > 0 && val * 10 <= to && *rp >= '0' && *rp <= '9'); \
70 if (val < from || val > to) \
71 return NULL; \
72 } while (0)
73 #ifdef _NL_CURRENT
74 # define get_alt_number(from, to, n) \
75 ({ \
76 __label__ do_normal; \
78 if (*decided != raw) \
79 { \
80 val = _nl_parse_alt_digit (&rp HELPER_LOCALE_ARG); \
81 if (val == -1 && *decided != loc) \
82 { \
83 *decided = loc; \
84 goto do_normal; \
85 } \
86 if (val < from || val > to) \
87 return NULL; \
88 } \
89 else \
90 { \
91 do_normal: \
92 get_number (from, to, n); \
93 } \
94 0; \
96 #else
97 # define get_alt_number(from, to, n) \
98 /* We don't have the alternate representation. */ \
99 get_number(from, to, n)
100 #endif
101 #define recursive(new_fmt) \
102 (*(new_fmt) != '\0' \
103 && (rp = __strptime_internal (rp, (new_fmt), tm, \
104 decided, era_cnt LOCALE_ARG)) != NULL)
107 #ifdef _LIBC
108 /* This is defined in locale/C-time.c in the GNU libc. */
109 extern const struct locale_data _nl_C_LC_TIME attribute_hidden;
111 # define weekday_name (&_nl_C_LC_TIME.values[_NL_ITEM_INDEX (DAY_1)].string)
112 # define ab_weekday_name \
113 (&_nl_C_LC_TIME.values[_NL_ITEM_INDEX (ABDAY_1)].string)
114 # define month_name (&_nl_C_LC_TIME.values[_NL_ITEM_INDEX (MON_1)].string)
115 # define ab_month_name (&_nl_C_LC_TIME.values[_NL_ITEM_INDEX (ABMON_1)].string)
116 # define HERE_D_T_FMT (_nl_C_LC_TIME.values[_NL_ITEM_INDEX (D_T_FMT)].string)
117 # define HERE_D_FMT (_nl_C_LC_TIME.values[_NL_ITEM_INDEX (D_FMT)].string)
118 # define HERE_AM_STR (_nl_C_LC_TIME.values[_NL_ITEM_INDEX (AM_STR)].string)
119 # define HERE_PM_STR (_nl_C_LC_TIME.values[_NL_ITEM_INDEX (PM_STR)].string)
120 # define HERE_T_FMT_AMPM \
121 (_nl_C_LC_TIME.values[_NL_ITEM_INDEX (T_FMT_AMPM)].string)
122 # define HERE_T_FMT (_nl_C_LC_TIME.values[_NL_ITEM_INDEX (T_FMT)].string)
124 # define strncasecmp(s1, s2, n) __strncasecmp (s1, s2, n)
125 #else
126 static char const weekday_name[][10] =
128 "Sunday", "Monday", "Tuesday", "Wednesday",
129 "Thursday", "Friday", "Saturday"
131 static char const ab_weekday_name[][4] =
133 "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
135 static char const month_name[][10] =
137 "January", "February", "March", "April", "May", "June",
138 "July", "August", "September", "October", "November", "December"
140 static char const ab_month_name[][4] =
142 "Jan", "Feb", "Mar", "Apr", "May", "Jun",
143 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
145 # define HERE_D_T_FMT "%a %b %e %H:%M:%S %Y"
146 # define HERE_D_FMT "%m/%d/%y"
147 # define HERE_AM_STR "AM"
148 # define HERE_PM_STR "PM"
149 # define HERE_T_FMT_AMPM "%I:%M:%S %p"
150 # define HERE_T_FMT "%H:%M:%S"
152 static const unsigned short int __mon_yday[2][13] =
154 /* Normal years. */
155 { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 },
156 /* Leap years. */
157 { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 }
159 #endif
161 #if defined _LIBC
162 /* We use this code also for the extended locale handling where the
163 function gets as an additional argument the locale which has to be
164 used. To access the values we have to redefine the _NL_CURRENT
165 macro. */
166 # define strptime __strptime_l
167 # undef _NL_CURRENT
168 # define _NL_CURRENT(category, item) \
169 (current->values[_NL_ITEM_INDEX (item)].string)
170 # undef _NL_CURRENT_WORD
171 # define _NL_CURRENT_WORD(category, item) \
172 (current->values[_NL_ITEM_INDEX (item)].word)
173 # define LOCALE_PARAM , locale
174 # define LOCALE_ARG , locale
175 # define LOCALE_PARAM_PROTO , __locale_t locale
176 # define LOCALE_PARAM_DECL __locale_t locale;
177 # define HELPER_LOCALE_ARG , current
178 # define ISSPACE(Ch) __isspace_l (Ch, locale)
179 #else
180 # define LOCALE_PARAM
181 # define LOCALE_ARG
182 # define LOCALE_PARAM_DECL
183 # define LOCALE_PARAM_PROTO
184 # define HELPER_LOCALE_ARG
185 # define ISSPACE(Ch) isspace (Ch)
186 #endif
191 #ifndef __isleap
192 /* Nonzero if YEAR is a leap year (every 4 years,
193 except every 100th isn't, and every 400th is). */
194 # define __isleap(year) \
195 ((year) % 4 == 0 && ((year) % 100 != 0 || (year) % 400 == 0))
196 #endif
198 /* Compute the day of the week. */
199 static void
200 day_of_the_week (struct tm *tm)
202 /* We know that January 1st 1970 was a Thursday (= 4). Compute the
203 difference between this data in the one on TM and so determine
204 the weekday. */
205 int corr_year = 1900 + tm->tm_year - (tm->tm_mon < 2);
206 int corr_quad = corr_year / 4;
207 int wday = (-473
208 + (365 * (tm->tm_year - 70))
209 + corr_quad
210 - ((corr_quad + (corr_quad < 0)) / 25 - (corr_quad < 0))
211 + ((corr_quad / 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));
226 #ifdef _LIBC
227 char *
228 internal_function
229 #else
230 static char *
231 #endif
232 __strptime_internal (rp, fmt, tm, decided, era_cnt LOCALE_PARAM)
233 const char *rp;
234 const char *fmt;
235 struct tm *tm;
236 enum ptime_locale_status *decided;
237 int era_cnt;
238 LOCALE_PARAM_DECL
240 #ifdef _LIBC
241 struct locale_data *const current = locale->__locales[LC_TIME];
242 #endif
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 #ifdef _NL_CURRENT
255 size_t num_eras;
256 struct era_entry *era = NULL;
257 const char *rp_backup;
258 #endif
260 have_I = is_pm = 0;
261 century = -1;
262 want_century = 0;
263 want_era = 0;
264 week_no = 0;
266 have_wday = want_xday = have_yday = have_mon = have_mday = have_uweek = 0;
267 have_wweek = 0;
269 while (*fmt != '\0')
271 /* A white space in the format string matches 0 more or white
272 space in the input string. */
273 if (ISSPACE (*fmt))
275 while (ISSPACE (*rp))
276 ++rp;
277 ++fmt;
278 continue;
281 /* Any character but '%' must be matched by the same character
282 in the input string. */
283 if (*fmt != '%')
285 match_char (*fmt++, *rp++);
286 continue;
289 ++fmt;
290 #ifndef _NL_CURRENT
291 /* We need this for handling the 'E' modifier. */
292 start_over:
293 #else
294 /* Make back up of current processing pointer. */
295 rp_backup = rp;
296 #endif
298 switch (*fmt++)
300 case '%':
301 /* Match the '%' character itself. */
302 match_char ('%', *rp++);
303 break;
304 case 'a':
305 case 'A':
306 /* Match day of week. */
307 for (cnt = 0; cnt < 7; ++cnt)
309 #ifdef _NL_CURRENT
310 if (*decided !=raw)
312 if (match_string (_NL_CURRENT (LC_TIME, DAY_1 + cnt), rp))
314 if (*decided == not
315 && strcmp (_NL_CURRENT (LC_TIME, DAY_1 + cnt),
316 weekday_name[cnt]))
317 *decided = loc;
318 break;
320 if (match_string (_NL_CURRENT (LC_TIME, ABDAY_1 + cnt), rp))
322 if (*decided == not
323 && strcmp (_NL_CURRENT (LC_TIME, ABDAY_1 + cnt),
324 ab_weekday_name[cnt]))
325 *decided = loc;
326 break;
329 #endif
330 if (*decided != loc
331 && (match_string (weekday_name[cnt], rp)
332 || match_string (ab_weekday_name[cnt], rp)))
334 *decided = raw;
335 break;
338 if (cnt == 7)
339 /* Does not match a weekday name. */
340 return NULL;
341 tm->tm_wday = cnt;
342 have_wday = 1;
343 break;
344 case 'b':
345 case 'B':
346 case 'h':
347 /* Match month name. */
348 for (cnt = 0; cnt < 12; ++cnt)
350 #ifdef _NL_CURRENT
351 if (*decided !=raw)
353 if (match_string (_NL_CURRENT (LC_TIME, MON_1 + cnt), rp))
355 if (*decided == not
356 && strcmp (_NL_CURRENT (LC_TIME, MON_1 + cnt),
357 month_name[cnt]))
358 *decided = loc;
359 break;
361 if (match_string (_NL_CURRENT (LC_TIME, ABMON_1 + cnt), rp))
363 if (*decided == not
364 && strcmp (_NL_CURRENT (LC_TIME, ABMON_1 + cnt),
365 ab_month_name[cnt]))
366 *decided = loc;
367 break;
370 #endif
371 if (match_string (month_name[cnt], rp)
372 || match_string (ab_month_name[cnt], rp))
374 *decided = raw;
375 break;
378 if (cnt == 12)
379 /* Does not match a month name. */
380 return NULL;
381 tm->tm_mon = cnt;
382 want_xday = 1;
383 break;
384 case 'c':
385 /* Match locale's date and time format. */
386 #ifdef _NL_CURRENT
387 if (*decided != raw)
389 if (!recursive (_NL_CURRENT (LC_TIME, D_T_FMT)))
391 if (*decided == loc)
392 return NULL;
393 else
394 rp = rp_backup;
396 else
398 if (*decided == not &&
399 strcmp (_NL_CURRENT (LC_TIME, D_T_FMT), HERE_D_T_FMT))
400 *decided = loc;
401 want_xday = 1;
402 break;
404 *decided = raw;
406 #endif
407 if (!recursive (HERE_D_T_FMT))
408 return NULL;
409 want_xday = 1;
410 break;
411 case 'C':
412 /* Match century number. */
413 #ifdef _NL_CURRENT
414 match_century:
415 #endif
416 get_number (0, 99, 2);
417 century = val;
418 want_xday = 1;
419 break;
420 case 'd':
421 case 'e':
422 /* Match day of month. */
423 get_number (1, 31, 2);
424 tm->tm_mday = val;
425 have_mday = 1;
426 want_xday = 1;
427 break;
428 case 'F':
429 if (!recursive ("%Y-%m-%d"))
430 return NULL;
431 want_xday = 1;
432 break;
433 case 'x':
434 #ifdef _NL_CURRENT
435 if (*decided != raw)
437 if (!recursive (_NL_CURRENT (LC_TIME, D_FMT)))
439 if (*decided == loc)
440 return NULL;
441 else
442 rp = rp_backup;
444 else
446 if (*decided == not
447 && strcmp (_NL_CURRENT (LC_TIME, D_FMT), HERE_D_FMT))
448 *decided = loc;
449 want_xday = 1;
450 break;
452 *decided = raw;
454 #endif
455 /* Fall through. */
456 case 'D':
457 /* Match standard day format. */
458 if (!recursive (HERE_D_FMT))
459 return NULL;
460 want_xday = 1;
461 break;
462 case 'k':
463 case 'H':
464 /* Match hour in 24-hour clock. */
465 get_number (0, 23, 2);
466 tm->tm_hour = val;
467 have_I = 0;
468 break;
469 case 'l':
470 /* Match hour in 12-hour clock. GNU extension. */
471 case 'I':
472 /* Match hour in 12-hour clock. */
473 get_number (1, 12, 2);
474 tm->tm_hour = val % 12;
475 have_I = 1;
476 break;
477 case 'j':
478 /* Match day number of year. */
479 get_number (1, 366, 3);
480 tm->tm_yday = val - 1;
481 have_yday = 1;
482 break;
483 case 'm':
484 /* Match number of month. */
485 get_number (1, 12, 2);
486 tm->tm_mon = val - 1;
487 have_mon = 1;
488 want_xday = 1;
489 break;
490 case 'M':
491 /* Match minute. */
492 get_number (0, 59, 2);
493 tm->tm_min = val;
494 break;
495 case 'n':
496 case 't':
497 /* Match any white space. */
498 while (ISSPACE (*rp))
499 ++rp;
500 break;
501 case 'p':
502 /* Match locale's equivalent of AM/PM. */
503 #ifdef _NL_CURRENT
504 if (*decided != raw)
506 if (match_string (_NL_CURRENT (LC_TIME, AM_STR), rp))
508 if (strcmp (_NL_CURRENT (LC_TIME, AM_STR), HERE_AM_STR))
509 *decided = loc;
510 break;
512 if (match_string (_NL_CURRENT (LC_TIME, PM_STR), rp))
514 if (strcmp (_NL_CURRENT (LC_TIME, PM_STR), HERE_PM_STR))
515 *decided = loc;
516 is_pm = 1;
517 break;
519 *decided = raw;
521 #endif
522 if (!match_string (HERE_AM_STR, rp))
524 if (match_string (HERE_PM_STR, rp))
525 is_pm = 1;
526 else
527 return NULL;
529 break;
530 case 'q':
531 /* Match quarter of year. GNU extension. */
532 get_number (1, 4, 1);
533 tm->tm_mon = (val - 1) * 3;
534 tm->tm_mday = 1;
535 have_mon = 1;
536 have_mday = 1;
537 want_xday = 1;
538 break;
539 case 'r':
540 #ifdef _NL_CURRENT
541 if (*decided != raw)
543 if (!recursive (_NL_CURRENT (LC_TIME, T_FMT_AMPM)))
545 if (*decided == loc)
546 return NULL;
547 else
548 rp = rp_backup;
550 else
552 if (*decided == not &&
553 strcmp (_NL_CURRENT (LC_TIME, T_FMT_AMPM),
554 HERE_T_FMT_AMPM))
555 *decided = loc;
556 break;
558 *decided = raw;
560 #endif
561 if (!recursive (HERE_T_FMT_AMPM))
562 return NULL;
563 break;
564 case 'R':
565 if (!recursive ("%H:%M"))
566 return NULL;
567 break;
568 case 's':
570 /* The number of seconds may be very high so we cannot use
571 the 'get_number' macro. Instead read the number
572 character for character and construct the result while
573 doing this. */
574 time_t secs = 0;
575 if (*rp < '0' || *rp > '9')
576 /* We need at least one digit. */
577 return NULL;
581 secs *= 10;
582 secs += *rp++ - '0';
584 while (*rp >= '0' && *rp <= '9');
586 if (localtime_r (&secs, tm) == NULL)
587 /* Error in function. */
588 return NULL;
590 break;
591 case 'S':
592 get_number (0, 61, 2);
593 tm->tm_sec = val;
594 break;
595 case 'X':
596 #ifdef _NL_CURRENT
597 if (*decided != raw)
599 if (!recursive (_NL_CURRENT (LC_TIME, T_FMT)))
601 if (*decided == loc)
602 return NULL;
603 else
604 rp = rp_backup;
606 else
608 if (strcmp (_NL_CURRENT (LC_TIME, T_FMT), HERE_T_FMT))
609 *decided = loc;
610 break;
612 *decided = raw;
614 #endif
615 /* Fall through. */
616 case 'T':
617 if (!recursive (HERE_T_FMT))
618 return NULL;
619 break;
620 case 'u':
621 get_number (1, 7, 1);
622 tm->tm_wday = val % 7;
623 have_wday = 1;
624 break;
625 case 'g':
626 get_number (0, 99, 2);
627 /* XXX This cannot determine any field in TM. */
628 break;
629 case 'G':
630 if (*rp < '0' || *rp > '9')
631 return NULL;
632 /* XXX Ignore the number since we would need some more
633 information to compute a real date. */
635 ++rp;
636 while (*rp >= '0' && *rp <= '9');
637 break;
638 case 'U':
639 get_number (0, 53, 2);
640 week_no = val;
641 have_uweek = 1;
642 break;
643 case 'W':
644 get_number (0, 53, 2);
645 week_no = val;
646 have_wweek = 1;
647 break;
648 case 'V':
649 get_number (0, 53, 2);
650 /* XXX This cannot determine any field in TM without some
651 information. */
652 break;
653 case 'w':
654 /* Match number of weekday. */
655 get_number (0, 6, 1);
656 tm->tm_wday = val;
657 have_wday = 1;
658 break;
659 case 'y':
660 #ifdef _NL_CURRENT
661 match_year_in_century:
662 #endif
663 /* Match year within century. */
664 get_number (0, 99, 2);
665 /* The "Year 2000: The Millennium Rollover" paper suggests that
666 values in the range 69-99 refer to the twentieth century. */
667 tm->tm_year = val >= 69 ? val : val + 100;
668 /* Indicate that we want to use the century, if specified. */
669 want_century = 1;
670 want_xday = 1;
671 break;
672 case 'Y':
673 /* Match year including century number. */
674 get_number (0, 9999, 4);
675 tm->tm_year = val - 1900;
676 want_century = 0;
677 want_xday = 1;
678 break;
679 case 'Z':
680 /* XXX How to handle this? */
681 break;
682 case 'z':
683 /* We recognize two formats: if two digits are given, these
684 specify hours. If fours digits are used, minutes are
685 also specified. */
687 bool neg _GL_UNUSED;
688 int n;
690 val = 0;
691 while (*rp == ' ')
692 ++rp;
693 if (*rp != '+' && *rp != '-')
694 return NULL;
695 neg = *rp++ == '-';
696 n = 0;
697 while (n < 4 && *rp >= '0' && *rp <= '9')
699 val = val * 10 + *rp++ - '0';
700 ++n;
702 if (n == 2)
703 val *= 100;
704 else if (n != 4)
705 /* Only two or four digits recognized. */
706 return NULL;
707 else
709 /* We have to convert the minutes into decimal. */
710 if (val % 100 >= 60)
711 return NULL;
712 val = (val / 100) * 100 + ((val % 100) * 50) / 30;
714 if (val > 1200)
715 return NULL;
716 #if defined _LIBC || HAVE_TM_GMTOFF
717 tm->tm_gmtoff = (val * 3600) / 100;
718 if (neg)
719 tm->tm_gmtoff = -tm->tm_gmtoff;
720 #endif
722 break;
723 case 'E':
724 #ifdef _NL_CURRENT
725 switch (*fmt++)
727 case 'c':
728 /* Match locale's alternate date and time format. */
729 if (*decided != raw)
731 const char *fmt = _NL_CURRENT (LC_TIME, ERA_D_T_FMT);
733 if (*fmt == '\0')
734 fmt = _NL_CURRENT (LC_TIME, D_T_FMT);
736 if (!recursive (fmt))
738 if (*decided == loc)
739 return NULL;
740 else
741 rp = rp_backup;
743 else
745 if (strcmp (fmt, HERE_D_T_FMT))
746 *decided = loc;
747 want_xday = 1;
748 break;
750 *decided = raw;
752 /* The C locale has no era information, so use the
753 normal representation. */
754 if (!recursive (HERE_D_T_FMT))
755 return NULL;
756 want_xday = 1;
757 break;
758 case 'C':
759 if (*decided != raw)
761 if (era_cnt >= 0)
763 era = _nl_select_era_entry (era_cnt HELPER_LOCALE_ARG);
764 if (era != NULL && match_string (era->era_name, rp))
766 *decided = loc;
767 break;
769 else
770 return NULL;
773 num_eras = _NL_CURRENT_WORD (LC_TIME,
774 _NL_TIME_ERA_NUM_ENTRIES);
775 for (era_cnt = 0; era_cnt < (int) num_eras;
776 ++era_cnt, rp = rp_backup)
778 era = _nl_select_era_entry (era_cnt
779 HELPER_LOCALE_ARG);
780 if (era != NULL && match_string (era->era_name, rp))
782 *decided = loc;
783 break;
786 if (era_cnt != (int) num_eras)
787 break;
789 era_cnt = -1;
790 if (*decided == loc)
791 return NULL;
793 *decided = raw;
795 /* The C locale has no era information, so use the
796 normal representation. */
797 goto match_century;
798 case 'y':
799 if (*decided != raw)
801 get_number(0, 9999, 4);
802 tm->tm_year = val;
803 want_era = 1;
804 want_xday = 1;
805 want_century = 1;
807 if (era_cnt >= 0)
809 assert (*decided == loc);
811 era = _nl_select_era_entry (era_cnt HELPER_LOCALE_ARG);
812 bool match = false;
813 if (era != NULL)
815 int delta = ((tm->tm_year - era->offset)
816 * era->absolute_direction);
817 match = (delta >= 0
818 && delta < (((int64_t) era->stop_date[0]
819 - (int64_t) era->start_date[0])
820 * era->absolute_direction));
822 if (! match)
823 return NULL;
825 break;
828 num_eras = _NL_CURRENT_WORD (LC_TIME,
829 _NL_TIME_ERA_NUM_ENTRIES);
830 for (era_cnt = 0; era_cnt < (int) num_eras; ++era_cnt)
832 era = _nl_select_era_entry (era_cnt
833 HELPER_LOCALE_ARG);
834 if (era != NULL)
836 int delta = ((tm->tm_year - era->offset)
837 * era->absolute_direction);
838 if (delta >= 0
839 && delta < (((int64_t) era->stop_date[0]
840 - (int64_t) era->start_date[0])
841 * era->absolute_direction))
843 *decided = loc;
844 break;
848 if (era_cnt != (int) num_eras)
849 break;
851 era_cnt = -1;
852 if (*decided == loc)
853 return NULL;
855 *decided = raw;
858 goto match_year_in_century;
859 case 'Y':
860 if (*decided != raw)
862 num_eras = _NL_CURRENT_WORD (LC_TIME,
863 _NL_TIME_ERA_NUM_ENTRIES);
864 for (era_cnt = 0; era_cnt < (int) num_eras;
865 ++era_cnt, rp = rp_backup)
867 era = _nl_select_era_entry (era_cnt HELPER_LOCALE_ARG);
868 if (era != NULL && recursive (era->era_format))
869 break;
871 if (era_cnt == (int) num_eras)
873 era_cnt = -1;
874 if (*decided == loc)
875 return NULL;
876 else
877 rp = rp_backup;
879 else
881 *decided = loc;
882 era_cnt = -1;
883 break;
886 *decided = raw;
888 get_number (0, 9999, 4);
889 tm->tm_year = val - 1900;
890 want_century = 0;
891 want_xday = 1;
892 break;
893 case 'x':
894 if (*decided != raw)
896 const char *fmt = _NL_CURRENT (LC_TIME, ERA_D_FMT);
898 if (*fmt == '\0')
899 fmt = _NL_CURRENT (LC_TIME, D_FMT);
901 if (!recursive (fmt))
903 if (*decided == loc)
904 return NULL;
905 else
906 rp = rp_backup;
908 else
910 if (strcmp (fmt, HERE_D_FMT))
911 *decided = loc;
912 break;
914 *decided = raw;
916 if (!recursive (HERE_D_FMT))
917 return NULL;
918 break;
919 case 'X':
920 if (*decided != raw)
922 const char *fmt = _NL_CURRENT (LC_TIME, ERA_T_FMT);
924 if (*fmt == '\0')
925 fmt = _NL_CURRENT (LC_TIME, T_FMT);
927 if (!recursive (fmt))
929 if (*decided == loc)
930 return NULL;
931 else
932 rp = rp_backup;
934 else
936 if (strcmp (fmt, HERE_T_FMT))
937 *decided = loc;
938 break;
940 *decided = raw;
942 if (!recursive (HERE_T_FMT))
943 return NULL;
944 break;
945 default:
946 return NULL;
948 break;
949 #else
950 /* We have no information about the era format. Just use
951 the normal format. */
952 if (*fmt != 'c' && *fmt != 'C' && *fmt != 'y' && *fmt != 'Y'
953 && *fmt != 'x' && *fmt != 'X')
954 /* This is an illegal format. */
955 return NULL;
957 goto start_over;
958 #endif
959 case 'O':
960 switch (*fmt++)
962 case 'd':
963 case 'e':
964 /* Match day of month using alternate numeric symbols. */
965 get_alt_number (1, 31, 2);
966 tm->tm_mday = val;
967 have_mday = 1;
968 want_xday = 1;
969 break;
970 case 'H':
971 /* Match hour in 24-hour clock using alternate numeric
972 symbols. */
973 get_alt_number (0, 23, 2);
974 tm->tm_hour = val;
975 have_I = 0;
976 break;
977 case 'I':
978 /* Match hour in 12-hour clock using alternate numeric
979 symbols. */
980 get_alt_number (1, 12, 2);
981 tm->tm_hour = val % 12;
982 have_I = 1;
983 break;
984 case 'm':
985 /* Match month using alternate numeric symbols. */
986 get_alt_number (1, 12, 2);
987 tm->tm_mon = val - 1;
988 have_mon = 1;
989 want_xday = 1;
990 break;
991 case 'M':
992 /* Match minutes using alternate numeric symbols. */
993 get_alt_number (0, 59, 2);
994 tm->tm_min = val;
995 break;
996 case 'q':
997 /* Match quarter using alternate numeric symbols. */
998 get_alt_number (1, 4, 1);
999 tm->tm_mon = (val - 1) * 3;
1000 tm->tm_mday = 1;
1001 have_mon = 1;
1002 have_mday = 1;
1003 want_xday = 1;
1004 break;
1005 case 'S':
1006 /* Match seconds using alternate numeric symbols. */
1007 get_alt_number (0, 61, 2);
1008 tm->tm_sec = val;
1009 break;
1010 case 'U':
1011 get_alt_number (0, 53, 2);
1012 week_no = val;
1013 have_uweek = 1;
1014 break;
1015 case 'W':
1016 get_alt_number (0, 53, 2);
1017 week_no = val;
1018 have_wweek = 1;
1019 break;
1020 case 'V':
1021 get_alt_number (0, 53, 2);
1022 /* XXX This cannot determine any field in TM without
1023 further information. */
1024 break;
1025 case 'w':
1026 /* Match number of weekday using alternate numeric symbols. */
1027 get_alt_number (0, 6, 1);
1028 tm->tm_wday = val;
1029 have_wday = 1;
1030 break;
1031 case 'y':
1032 /* Match year within century using alternate numeric symbols. */
1033 get_alt_number (0, 99, 2);
1034 tm->tm_year = val >= 69 ? val : val + 100;
1035 want_xday = 1;
1036 break;
1037 default:
1038 return NULL;
1040 break;
1041 default:
1042 return NULL;
1046 if (have_I && is_pm)
1047 tm->tm_hour += 12;
1049 if (century != -1)
1051 if (want_century)
1052 tm->tm_year = tm->tm_year % 100 + (century - 19) * 100;
1053 else
1054 /* Only the century, but not the year. Strange, but so be it. */
1055 tm->tm_year = (century - 19) * 100;
1058 if (era_cnt != -1)
1060 #ifdef _NL_CURRENT
1061 era = _nl_select_era_entry (era_cnt HELPER_LOCALE_ARG);
1062 if (era == NULL)
1063 return NULL;
1064 if (want_era)
1065 tm->tm_year = (era->start_date[0]
1066 + ((tm->tm_year - era->offset)
1067 * era->absolute_direction));
1068 else
1069 /* Era start year assumed. */
1070 tm->tm_year = era->start_date[0];
1071 #endif
1073 else
1074 if (want_era)
1076 /* No era found but we have seen an E modifier. Rectify some
1077 values. */
1078 if (want_century && century == -1 && tm->tm_year < 69)
1079 tm->tm_year += 100;
1082 if (want_xday && !have_wday)
1084 if ( !(have_mon && have_mday) && have_yday)
1086 /* We don't have tm_mon and/or tm_mday, compute them. */
1087 int t_mon = 0;
1088 while (__mon_yday[__isleap(1900 + tm->tm_year)][t_mon] <= tm->tm_yday)
1089 t_mon++;
1090 if (!have_mon)
1091 tm->tm_mon = t_mon - 1;
1092 if (!have_mday)
1093 tm->tm_mday =
1094 (tm->tm_yday
1095 - __mon_yday[__isleap(1900 + tm->tm_year)][t_mon - 1] + 1);
1097 day_of_the_week (tm);
1100 if (want_xday && !have_yday)
1101 day_of_the_year (tm);
1103 if ((have_uweek || have_wweek) && have_wday)
1105 int save_wday = tm->tm_wday;
1106 int save_mday = tm->tm_mday;
1107 int save_mon = tm->tm_mon;
1108 int w_offset = have_uweek ? 0 : 1;
1110 tm->tm_mday = 1;
1111 tm->tm_mon = 0;
1112 day_of_the_week (tm);
1113 if (have_mday)
1114 tm->tm_mday = save_mday;
1115 if (have_mon)
1116 tm->tm_mon = save_mon;
1118 if (!have_yday)
1119 tm->tm_yday = ((7 - (tm->tm_wday - w_offset)) % 7
1120 + (week_no - 1) *7
1121 + save_wday - w_offset);
1123 if (!have_mday || !have_mon)
1125 int t_mon = 0;
1126 while (__mon_yday[__isleap(1900 + tm->tm_year)][t_mon]
1127 <= tm->tm_yday)
1128 t_mon++;
1129 if (!have_mon)
1130 tm->tm_mon = t_mon - 1;
1131 if (!have_mday)
1132 tm->tm_mday =
1133 (tm->tm_yday
1134 - __mon_yday[__isleap(1900 + tm->tm_year)][t_mon - 1] + 1);
1137 tm->tm_wday = save_wday;
1140 return (char *) rp;
1144 char *
1145 strptime (buf, format, tm LOCALE_PARAM)
1146 const char *restrict buf;
1147 const char *restrict format;
1148 struct tm *restrict tm;
1149 LOCALE_PARAM_DECL
1151 enum ptime_locale_status decided;
1153 #ifdef _NL_CURRENT
1154 decided = not;
1155 #else
1156 decided = raw;
1157 #endif
1158 return __strptime_internal (buf, format, tm, &decided, -1 LOCALE_ARG);
1161 #ifdef _LIBC
1162 weak_alias (__strptime_l, strptime_l)
1163 #endif