stdlib: Reinstate stable mergesort implementation on qsort
[glibc.git] / time / strptime_l.c
blob56f1c32cadbcf4f2b6fe4dbf7c1a792c4873fc9b
1 /* Copyright (C) 2002-2024 Free Software Foundation, Inc.
2 This file is part of the GNU C Library.
4 The GNU C Library is free software; you can redistribute it and/or
5 modify it under the terms of the GNU Lesser General Public
6 License as published by the Free Software Foundation; either
7 version 2.1 of the License, or (at your option) any later version.
9 The GNU C Library is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 Lesser General Public License for more details.
14 You should have received a copy of the GNU Lesser General Public
15 License along with the GNU C Library; if not, see
16 <https://www.gnu.org/licenses/>. */
18 #ifdef HAVE_CONFIG_H
19 # include <config.h>
20 #endif
22 #include <assert.h>
23 #include <ctype.h>
24 #include <langinfo.h>
25 #include <limits.h>
26 #include <string.h>
27 #include <time.h>
28 #include <stdbool.h>
30 #ifdef _LIBC
31 # define HAVE_LOCALTIME_R 0
32 # include "../locale/localeinfo.h"
34 # define time_t __time64_t
35 # define __localtime_r(t, tp) __localtime64_r (t, tp)
36 #endif
38 #if ! HAVE_LOCALTIME_R && ! defined localtime_r
39 # ifdef _LIBC
40 # define localtime_r __localtime_r
41 # else
42 /* Approximate localtime_r as best we can in its absence. */
43 # define localtime_r my_localtime_r
44 static struct tm *localtime_r (const time_t *, struct tm *);
45 static struct tm *
46 localtime_r (const time_t *t, struct tm *tp)
48 struct tm *l = localtime (t);
49 if (! l)
50 return 0;
51 *tp = *l;
52 return tp;
54 # endif /* ! _LIBC */
55 #endif /* ! HAVE_LOCALTIME_R && ! defined (localtime_r) */
58 #define match_char(ch1, ch2) if (ch1 != ch2) return NULL
59 #if defined __GNUC__ && __GNUC__ >= 2
60 # define match_string(cs1, s2) \
61 ({ size_t len = strlen (cs1); \
62 int result = __strncasecmp_l ((cs1), (s2), len, locale) == 0; \
63 if (result) (s2) += len; \
64 result; })
65 #else
66 /* Oh come on. Get a reasonable compiler. */
67 # define match_string(cs1, s2) \
68 (strncasecmp ((cs1), (s2), strlen (cs1)) ? 0 : ((s2) += strlen (cs1), 1))
69 #endif
70 /* We intentionally do not use isdigit() for testing because this will
71 lead to problems with the wide character version. */
72 #define get_number(from, to, n) \
73 do { \
74 int __n = n; \
75 val = 0; \
76 while (ISSPACE (*rp)) \
77 ++rp; \
78 if (*rp < '0' || *rp > '9') \
79 return NULL; \
80 do { \
81 val *= 10; \
82 val += *rp++ - '0'; \
83 } while (--__n > 0 && val * 10 <= to && *rp >= '0' && *rp <= '9'); \
84 if (val < from || val > to) \
85 return NULL; \
86 } while (0)
87 #ifdef _NL_CURRENT
88 # define get_alt_number(from, to, n) \
89 ({ \
90 __label__ do_normal; \
92 if (s.decided != raw) \
93 { \
94 val = _nl_parse_alt_digit (&rp HELPER_LOCALE_ARG); \
95 if (val == -1 && s.decided != loc) \
96 { \
97 s.decided = loc; \
98 goto do_normal; \
99 } \
100 if (val < from || val > to) \
101 return NULL; \
103 else \
105 do_normal: \
106 get_number (from, to, n); \
108 0; \
110 #else
111 # define get_alt_number(from, to, n) \
112 /* We don't have the alternate representation. */ \
113 get_number(from, to, n)
114 #endif
115 #define recursive(new_fmt) \
116 (*(new_fmt) != '\0' \
117 && (rp = __strptime_internal (rp, (new_fmt), tm, &s LOCALE_ARG)) != NULL)
120 #ifdef _LIBC
121 /* This is defined in locale/C-time.c in the GNU libc. */
122 extern const struct __locale_data _nl_C_LC_TIME attribute_hidden;
124 # define weekday_name (&_nl_C_LC_TIME.values[_NL_ITEM_INDEX (DAY_1)].string)
125 # define ab_weekday_name \
126 (&_nl_C_LC_TIME.values[_NL_ITEM_INDEX (ABDAY_1)].string)
127 # define month_name (&_nl_C_LC_TIME.values[_NL_ITEM_INDEX (MON_1)].string)
128 # define ab_month_name (&_nl_C_LC_TIME.values[_NL_ITEM_INDEX (ABMON_1)].string)
129 # define alt_month_name \
130 (&_nl_C_LC_TIME.values[_NL_ITEM_INDEX (ALTMON_1)].string)
131 # define ab_alt_month_name \
132 (&_nl_C_LC_TIME.values[_NL_ITEM_INDEX (_NL_ABALTMON_1)].string)
133 # define HERE_D_T_FMT (_nl_C_LC_TIME.values[_NL_ITEM_INDEX (D_T_FMT)].string)
134 # define HERE_D_FMT (_nl_C_LC_TIME.values[_NL_ITEM_INDEX (D_FMT)].string)
135 # define HERE_AM_STR (_nl_C_LC_TIME.values[_NL_ITEM_INDEX (AM_STR)].string)
136 # define HERE_PM_STR (_nl_C_LC_TIME.values[_NL_ITEM_INDEX (PM_STR)].string)
137 # define HERE_T_FMT_AMPM \
138 (_nl_C_LC_TIME.values[_NL_ITEM_INDEX (T_FMT_AMPM)].string)
139 # define HERE_T_FMT (_nl_C_LC_TIME.values[_NL_ITEM_INDEX (T_FMT)].string)
141 # define strncasecmp(s1, s2, n) __strncasecmp (s1, s2, n)
142 #else
143 static char const weekday_name[][10] =
145 "Sunday", "Monday", "Tuesday", "Wednesday",
146 "Thursday", "Friday", "Saturday"
148 static char const ab_weekday_name[][4] =
150 "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
152 static char const month_name[][10] =
154 "January", "February", "March", "April", "May", "June",
155 "July", "August", "September", "October", "November", "December"
157 static char const ab_month_name[][4] =
159 "Jan", "Feb", "Mar", "Apr", "May", "Jun",
160 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
162 # define HERE_D_T_FMT "%a %b %e %H:%M:%S %Y"
163 # define HERE_D_FMT "%m/%d/%y"
164 # define HERE_AM_STR "AM"
165 # define HERE_PM_STR "PM"
166 # define HERE_T_FMT_AMPM "%I:%M:%S %p"
167 # define HERE_T_FMT "%H:%M:%S"
169 static const unsigned short int __mon_yday[2][13] =
171 /* Normal years. */
172 { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 },
173 /* Leap years. */
174 { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 }
176 #endif
178 #if defined _LIBC
179 /* We use this code also for the extended locale handling where the
180 function gets as an additional argument the locale which has to be
181 used. To access the values we have to redefine the _NL_CURRENT
182 macro. */
183 # define strptime __strptime_l
184 # undef _NL_CURRENT
185 # define _NL_CURRENT(category, item) \
186 (current->values[_NL_ITEM_INDEX (item)].string)
187 # undef _NL_CURRENT_WORD
188 # define _NL_CURRENT_WORD(category, item) \
189 (current->values[_NL_ITEM_INDEX (item)].word)
190 # define LOCALE_PARAM , locale_t locale
191 # define LOCALE_ARG , locale
192 # define HELPER_LOCALE_ARG , current
193 # define ISSPACE(Ch) __isspace_l (Ch, locale)
194 #else
195 # define LOCALE_PARAM
196 # define LOCALE_ARG
197 # define HELPER_LOCALE_ARG
198 # define ISSPACE(Ch) isspace (Ch)
199 #endif
204 #ifndef __isleap
205 /* Nonzero if YEAR is a leap year (every 4 years,
206 except every 100th isn't, and every 400th is). */
207 # define __isleap(year) \
208 ((year) % 4 == 0 && ((year) % 100 != 0 || (year) % 400 == 0))
209 #endif
211 /* Compute the day of the week. */
212 static void
213 day_of_the_week (struct tm *tm)
215 /* We know that January 1st 1970 was a Thursday (= 4). Compute the
216 difference between this data in the one on TM and so determine
217 the weekday. */
218 int corr_year = 1900 + tm->tm_year - (tm->tm_mon < 2);
219 int wday = (-473
220 + (365 * (tm->tm_year - 70))
221 + (corr_year / 4)
222 - ((corr_year / 4) / 25) + ((corr_year / 4) % 25 < 0)
223 + (((corr_year / 4) / 25) / 4)
224 + __mon_yday[0][tm->tm_mon]
225 + tm->tm_mday - 1);
226 tm->tm_wday = ((wday % 7) + 7) % 7;
229 /* Compute the day of the year. */
230 static void
231 day_of_the_year (struct tm *tm)
233 tm->tm_yday = (__mon_yday[__isleap (1900 + tm->tm_year)][tm->tm_mon]
234 + (tm->tm_mday - 1));
238 #ifdef _LIBC
239 char *
240 #else
241 static char *
242 #endif
243 __strptime_internal (const char *rp, const char *fmt, struct tm *tmp,
244 void *statep LOCALE_PARAM)
246 #ifdef _LIBC
247 struct __locale_data *const current = locale->__locales[LC_TIME];
248 #endif
250 const char *rp_backup;
251 const char *rp_longest;
252 int cnt;
253 int cnt_longest;
254 size_t val;
255 size_t num_eras;
256 struct era_entry *era = NULL;
257 enum ptime_locale_status { not, loc, raw } decided_longest;
258 struct __strptime_state
260 unsigned int have_I : 1;
261 unsigned int have_wday : 1;
262 unsigned int have_yday : 1;
263 unsigned int have_mon : 1;
264 unsigned int have_mday : 1;
265 unsigned int have_uweek : 1;
266 unsigned int have_wweek : 1;
267 unsigned int is_pm : 1;
268 unsigned int want_century : 1;
269 unsigned int want_era : 1;
270 unsigned int want_xday : 1;
271 enum ptime_locale_status decided : 2;
272 signed char week_no;
273 signed char century;
274 int era_cnt;
275 } s;
276 struct tm tmb;
277 struct tm *tm;
279 if (statep == NULL)
281 memset (&s, 0, sizeof (s));
282 s.century = -1;
283 s.era_cnt = -1;
284 #ifdef _NL_CURRENT
285 s.decided = not;
286 #else
287 s.decided = raw;
288 #endif
289 tm = tmp;
291 else
293 s = *(struct __strptime_state *) statep;
294 tmb = *tmp;
295 tm = &tmb;
298 while (*fmt != '\0')
300 /* A white space in the format string matches 0 more or white
301 space in the input string. */
302 if (ISSPACE (*fmt))
304 while (ISSPACE (*rp))
305 ++rp;
306 ++fmt;
307 continue;
310 /* Any character but `%' must be matched by the same character
311 in the input string. */
312 if (*fmt != '%')
314 match_char (*fmt++, *rp++);
315 continue;
318 ++fmt;
319 /* We discard strftime modifiers. */
320 while (*fmt == '-' || *fmt == '_' || *fmt == '0'
321 || *fmt == '^' || *fmt == '#')
322 ++fmt;
324 /* And field width. */
325 while (*fmt >= '0' && *fmt <= '9')
326 ++fmt;
328 /* In some cases, modifiers are handled by adjusting state and
329 then restarting the switch statement below. */
330 start_over:
332 /* Make back up of current processing pointer. */
333 rp_backup = rp;
335 switch (*fmt++)
337 case '%':
338 /* Match the `%' character itself. */
339 match_char ('%', *rp++);
340 break;
341 case 'a':
342 case 'A':
343 /* Match day of week. */
344 rp_longest = NULL;
345 decided_longest = s.decided;
346 cnt_longest = -1;
347 for (cnt = 0; cnt < 7; ++cnt)
349 const char *trp;
350 #ifdef _NL_CURRENT
351 if (s.decided !=raw)
353 trp = rp;
354 if (match_string (_NL_CURRENT (LC_TIME, DAY_1 + cnt), trp)
355 && trp > rp_longest)
357 rp_longest = trp;
358 cnt_longest = cnt;
359 if (s.decided == not
360 && strcmp (_NL_CURRENT (LC_TIME, DAY_1 + cnt),
361 weekday_name[cnt]))
362 decided_longest = loc;
364 trp = rp;
365 if (match_string (_NL_CURRENT (LC_TIME, ABDAY_1 + cnt), trp)
366 && trp > rp_longest)
368 rp_longest = trp;
369 cnt_longest = cnt;
370 if (s.decided == not
371 && strcmp (_NL_CURRENT (LC_TIME, ABDAY_1 + cnt),
372 ab_weekday_name[cnt]))
373 decided_longest = loc;
376 #endif
377 if (s.decided != loc
378 && (((trp = rp, match_string (weekday_name[cnt], trp))
379 && trp > rp_longest)
380 || ((trp = rp, match_string (ab_weekday_name[cnt], rp))
381 && trp > rp_longest)))
383 rp_longest = trp;
384 cnt_longest = cnt;
385 decided_longest = raw;
388 if (rp_longest == NULL)
389 /* Does not match a weekday name. */
390 return NULL;
391 rp = rp_longest;
392 s.decided = decided_longest;
393 tm->tm_wday = cnt_longest;
394 s.have_wday = 1;
395 break;
396 case 'b':
397 case 'B':
398 case 'h':
399 /* Match month name. */
400 rp_longest = NULL;
401 decided_longest = s.decided;
402 cnt_longest = -1;
403 for (cnt = 0; cnt < 12; ++cnt)
405 const char *trp;
406 #ifdef _NL_CURRENT
407 if (s.decided !=raw)
409 trp = rp;
410 if (match_string (_NL_CURRENT (LC_TIME, MON_1 + cnt), trp)
411 && trp > rp_longest)
413 rp_longest = trp;
414 cnt_longest = cnt;
415 if (s.decided == not
416 && strcmp (_NL_CURRENT (LC_TIME, MON_1 + cnt),
417 month_name[cnt]))
418 decided_longest = loc;
420 trp = rp;
421 if (match_string (_NL_CURRENT (LC_TIME, ABMON_1 + cnt), trp)
422 && trp > rp_longest)
424 rp_longest = trp;
425 cnt_longest = cnt;
426 if (s.decided == not
427 && strcmp (_NL_CURRENT (LC_TIME, ABMON_1 + cnt),
428 ab_month_name[cnt]))
429 decided_longest = loc;
431 #ifdef _LIBC
432 /* Now check the alt month. */
433 trp = rp;
434 if (match_string (_NL_CURRENT (LC_TIME, ALTMON_1 + cnt), trp)
435 && trp > rp_longest)
437 rp_longest = trp;
438 cnt_longest = cnt;
439 if (s.decided == not
440 && strcmp (_NL_CURRENT (LC_TIME, ALTMON_1 + cnt),
441 alt_month_name[cnt]))
442 decided_longest = loc;
444 trp = rp;
445 if (match_string (_NL_CURRENT (LC_TIME, _NL_ABALTMON_1 + cnt),
446 trp)
447 && trp > rp_longest)
449 rp_longest = trp;
450 cnt_longest = cnt;
451 if (s.decided == not
452 && strcmp (_NL_CURRENT (LC_TIME, _NL_ABALTMON_1 + cnt),
453 alt_month_name[cnt]))
454 decided_longest = loc;
456 #endif
458 #endif
459 if (s.decided != loc
460 && (((trp = rp, match_string (month_name[cnt], trp))
461 && trp > rp_longest)
462 || ((trp = rp, match_string (ab_month_name[cnt], trp))
463 && trp > rp_longest)
464 #ifdef _LIBC
465 || ((trp = rp, match_string (alt_month_name[cnt], trp))
466 && trp > rp_longest)
467 || ((trp = rp, match_string (ab_alt_month_name[cnt], trp))
468 && trp > rp_longest)
469 #endif
472 rp_longest = trp;
473 cnt_longest = cnt;
474 decided_longest = raw;
477 if (rp_longest == NULL)
478 /* Does not match a month name. */
479 return NULL;
480 rp = rp_longest;
481 s.decided = decided_longest;
482 tm->tm_mon = cnt_longest;
483 s.have_mon = 1;
484 s.want_xday = 1;
485 break;
486 case 'c':
487 /* Match locale's date and time format. */
488 #ifdef _NL_CURRENT
489 if (s.decided != raw)
491 if (!recursive (_NL_CURRENT (LC_TIME, D_T_FMT)))
493 if (s.decided == loc)
494 return NULL;
495 else
496 rp = rp_backup;
498 else
500 if (s.decided == not
501 && strcmp (_NL_CURRENT (LC_TIME, D_T_FMT), HERE_D_T_FMT))
502 s.decided = loc;
503 s.want_xday = 1;
504 break;
506 s.decided = raw;
508 #endif
509 if (!recursive (HERE_D_T_FMT))
510 return NULL;
511 s.want_xday = 1;
512 break;
513 case 'C':
514 /* Match century number. */
515 match_century:
516 get_number (0, 99, 2);
517 s.century = val;
518 s.want_xday = 1;
519 break;
520 case 'd':
521 case 'e':
522 /* Match day of month. */
523 get_number (1, 31, 2);
524 tm->tm_mday = val;
525 s.have_mday = 1;
526 s.want_xday = 1;
527 break;
528 case 'F':
529 if (!recursive ("%Y-%m-%d"))
530 return NULL;
531 s.want_xday = 1;
532 break;
533 case 'x':
534 #ifdef _NL_CURRENT
535 if (s.decided != raw)
537 if (!recursive (_NL_CURRENT (LC_TIME, D_FMT)))
539 if (s.decided == loc)
540 return NULL;
541 else
542 rp = rp_backup;
544 else
546 if (s.decided == not
547 && strcmp (_NL_CURRENT (LC_TIME, D_FMT), HERE_D_FMT))
548 s.decided = loc;
549 s.want_xday = 1;
550 break;
552 s.decided = raw;
554 #endif
555 /* Fall through. */
556 case 'D':
557 /* Match standard day format. */
558 if (!recursive (HERE_D_FMT))
559 return NULL;
560 s.want_xday = 1;
561 break;
562 case 'k':
563 case 'H':
564 /* Match hour in 24-hour clock. */
565 get_number (0, 23, 2);
566 tm->tm_hour = val;
567 s.have_I = 0;
568 break;
569 case 'l':
570 /* Match hour in 12-hour clock. GNU extension. */
571 case 'I':
572 /* Match hour in 12-hour clock. */
573 get_number (1, 12, 2);
574 tm->tm_hour = val % 12;
575 s.have_I = 1;
576 break;
577 case 'j':
578 /* Match day number of year. */
579 get_number (1, 366, 3);
580 tm->tm_yday = val - 1;
581 s.have_yday = 1;
582 break;
583 case 'm':
584 /* Match number of month. */
585 get_number (1, 12, 2);
586 tm->tm_mon = val - 1;
587 s.have_mon = 1;
588 s.want_xday = 1;
589 break;
590 case 'M':
591 /* Match minute. */
592 get_number (0, 59, 2);
593 tm->tm_min = val;
594 break;
595 case 'n':
596 case 't':
597 /* Match any white space. */
598 while (ISSPACE (*rp))
599 ++rp;
600 break;
601 case 'p':
602 /* Match locale's equivalent of AM/PM. */
603 #ifdef _NL_CURRENT
604 if (s.decided != raw)
606 if (match_string (_NL_CURRENT (LC_TIME, AM_STR), rp))
608 if (strcmp (_NL_CURRENT (LC_TIME, AM_STR), HERE_AM_STR))
609 s.decided = loc;
610 s.is_pm = 0;
611 break;
613 if (match_string (_NL_CURRENT (LC_TIME, PM_STR), rp))
615 if (strcmp (_NL_CURRENT (LC_TIME, PM_STR), HERE_PM_STR))
616 s.decided = loc;
617 s.is_pm = 1;
618 break;
620 s.decided = raw;
622 #endif
623 if (!match_string (HERE_AM_STR, rp))
625 if (match_string (HERE_PM_STR, rp))
626 s.is_pm = 1;
627 else
628 return NULL;
630 else
631 s.is_pm = 0;
632 break;
633 case 'r':
634 #ifdef _NL_CURRENT
635 if (s.decided != raw)
637 if (!recursive (_NL_CURRENT (LC_TIME, T_FMT_AMPM)))
639 if (s.decided == loc)
640 return NULL;
641 else
642 rp = rp_backup;
644 else
646 if (s.decided == not
647 && strcmp (_NL_CURRENT (LC_TIME, T_FMT_AMPM),
648 HERE_T_FMT_AMPM))
649 s.decided = loc;
650 break;
652 s.decided = raw;
654 #endif
655 if (!recursive (HERE_T_FMT_AMPM))
656 return NULL;
657 break;
658 case 'R':
659 if (!recursive ("%H:%M"))
660 return NULL;
661 break;
662 case 's':
664 /* The number of seconds may be very high so we cannot use
665 the `get_number' macro. Instead read the number
666 character for character and construct the result while
667 doing this. */
668 time_t secs = 0;
669 if (*rp < '0' || *rp > '9')
670 /* We need at least one digit. */
671 return NULL;
675 secs *= 10;
676 secs += *rp++ - '0';
678 while (*rp >= '0' && *rp <= '9');
680 if (localtime_r (&secs, tm) == NULL)
681 /* Error in function. */
682 return NULL;
684 break;
685 case 'S':
686 get_number (0, 61, 2);
687 tm->tm_sec = val;
688 break;
689 case 'X':
690 #ifdef _NL_CURRENT
691 if (s.decided != raw)
693 if (!recursive (_NL_CURRENT (LC_TIME, T_FMT)))
695 if (s.decided == loc)
696 return NULL;
697 else
698 rp = rp_backup;
700 else
702 if (strcmp (_NL_CURRENT (LC_TIME, T_FMT), HERE_T_FMT))
703 s.decided = loc;
704 break;
706 s.decided = raw;
708 #endif
709 /* Fall through. */
710 case 'T':
711 if (!recursive (HERE_T_FMT))
712 return NULL;
713 break;
714 case 'u':
715 get_number (1, 7, 1);
716 tm->tm_wday = val % 7;
717 s.have_wday = 1;
718 break;
719 case 'g':
720 get_number (0, 99, 2);
721 /* XXX This cannot determine any field in TM. */
722 break;
723 case 'G':
724 if (*rp < '0' || *rp > '9')
725 return NULL;
726 /* XXX Ignore the number since we would need some more
727 information to compute a real date. */
729 ++rp;
730 while (*rp >= '0' && *rp <= '9');
731 break;
732 case 'U':
733 get_number (0, 53, 2);
734 s.week_no = val;
735 s.have_uweek = 1;
736 break;
737 case 'W':
738 get_number (0, 53, 2);
739 s.week_no = val;
740 s.have_wweek = 1;
741 break;
742 case 'V':
743 get_number (0, 53, 2);
744 /* XXX This cannot determine any field in TM without some
745 information. */
746 break;
747 case 'w':
748 /* Match number of weekday. */
749 get_number (0, 6, 1);
750 tm->tm_wday = val;
751 s.have_wday = 1;
752 break;
753 case 'y':
754 match_year_in_century:
755 /* Match year within century. */
756 get_number (0, 99, 2);
757 /* The "Year 2000: The Millennium Rollover" paper suggests that
758 values in the range 69-99 refer to the twentieth century. */
759 tm->tm_year = val >= 69 ? val : val + 100;
760 /* Indicate that we want to use the century, if specified. */
761 s.want_century = 1;
762 s.want_xday = 1;
763 break;
764 case 'Y':
765 /* Match year including century number. */
766 get_number (0, 9999, 4);
767 tm->tm_year = val - 1900;
768 s.want_century = 0;
769 s.want_xday = 1;
770 break;
771 case 'Z':
772 /* Read timezone but perform no conversion. */
773 while (ISSPACE (*rp))
774 rp++;
775 while (!ISSPACE (*rp) && *rp != '\0')
776 rp++;
777 break;
778 case 'z':
779 /* We recognize four formats:
780 1. Two digits specify hours.
781 2. Four digits specify hours and minutes.
782 3. Two digits, ':', and two digits specify hours and minutes.
783 4. 'Z' is equivalent to +0000. */
785 val = 0;
786 while (ISSPACE (*rp))
787 ++rp;
788 if (*rp == 'Z')
790 ++rp;
791 tm->tm_gmtoff = 0;
792 break;
794 if (*rp != '+' && *rp != '-')
795 return NULL;
796 bool neg = *rp++ == '-';
797 int n = 0;
798 while (n < 4 && *rp >= '0' && *rp <= '9')
800 val = val * 10 + *rp++ - '0';
801 ++n;
802 if (*rp == ':' && n == 2 && isdigit (*(rp + 1)))
803 ++rp;
805 if (n == 2)
806 val *= 100;
807 else if (n != 4)
808 /* Only two or four digits recognized. */
809 return NULL;
810 else if (val % 100 >= 60)
811 /* Minutes valid range is 0 through 59. */
812 return NULL;
813 tm->tm_gmtoff = (val / 100) * 3600 + (val % 100) * 60;
814 if (neg)
815 tm->tm_gmtoff = -tm->tm_gmtoff;
817 break;
818 case 'E':
819 #ifdef _NL_CURRENT
820 switch (*fmt++)
822 case 'c':
823 /* Match locale's alternate date and time format. */
824 if (s.decided != raw)
826 const char *fmt = _NL_CURRENT (LC_TIME, ERA_D_T_FMT);
828 if (*fmt == '\0')
829 fmt = _NL_CURRENT (LC_TIME, D_T_FMT);
831 if (!recursive (fmt))
833 if (s.decided == loc)
834 return NULL;
835 else
836 rp = rp_backup;
838 else
840 if (strcmp (fmt, HERE_D_T_FMT))
841 s.decided = loc;
842 s.want_xday = 1;
843 break;
845 s.decided = raw;
847 /* The C locale has no era information, so use the
848 normal representation. */
849 if (!recursive (HERE_D_T_FMT))
850 return NULL;
851 s.want_xday = 1;
852 break;
853 case 'C':
854 if (s.decided != raw)
856 if (s.era_cnt >= 0)
858 era = _nl_select_era_entry (s.era_cnt HELPER_LOCALE_ARG);
859 if (era != NULL && match_string (era->era_name, rp))
861 s.decided = loc;
862 break;
864 else
865 return NULL;
868 num_eras = _NL_CURRENT_WORD (LC_TIME,
869 _NL_TIME_ERA_NUM_ENTRIES);
870 for (s.era_cnt = 0; s.era_cnt < (int) num_eras;
871 ++s.era_cnt, rp = rp_backup)
873 era = _nl_select_era_entry (s.era_cnt
874 HELPER_LOCALE_ARG);
875 if (era != NULL && match_string (era->era_name, rp))
877 s.decided = loc;
878 break;
881 if (s.era_cnt != (int) num_eras)
882 break;
884 s.era_cnt = -1;
885 if (s.decided == loc)
886 return NULL;
888 s.decided = raw;
890 /* The C locale has no era information, so use the
891 normal representation. */
892 goto match_century;
893 case 'y':
894 if (s.decided != raw)
896 get_number(0, 9999, 4);
897 tm->tm_year = val;
898 s.want_era = 1;
899 s.want_xday = 1;
900 s.want_century = 1;
902 if (s.era_cnt >= 0)
904 assert (s.decided == loc);
906 era = _nl_select_era_entry (s.era_cnt HELPER_LOCALE_ARG);
907 bool match = false;
908 if (era != NULL)
910 int delta = ((tm->tm_year - era->offset)
911 * era->absolute_direction);
912 /* The difference between two sets of years
913 does not include the final year itself,
914 therefore add 1 to the difference to
915 account for that final year. */
916 match = (delta >= 0
917 && delta < (((int64_t) era->stop_date[0]
918 - (int64_t) era->start_date[0])
919 * era->absolute_direction
920 + 1));
922 if (! match)
923 return NULL;
925 break;
928 num_eras = _NL_CURRENT_WORD (LC_TIME,
929 _NL_TIME_ERA_NUM_ENTRIES);
930 for (s.era_cnt = 0; s.era_cnt < (int) num_eras; ++s.era_cnt)
932 era = _nl_select_era_entry (s.era_cnt
933 HELPER_LOCALE_ARG);
934 if (era != NULL)
936 int delta = ((tm->tm_year - era->offset)
937 * era->absolute_direction);
938 /* See comment above about year difference + 1. */
939 if (delta >= 0
940 && delta < (((int64_t) era->stop_date[0]
941 - (int64_t) era->start_date[0])
942 * era->absolute_direction
943 + 1))
945 s.decided = loc;
946 break;
950 if (s.era_cnt != (int) num_eras)
951 break;
953 s.era_cnt = -1;
954 if (s.decided == loc)
955 return NULL;
957 s.decided = raw;
960 goto match_year_in_century;
961 case 'Y':
962 if (s.decided != raw)
964 num_eras = _NL_CURRENT_WORD (LC_TIME,
965 _NL_TIME_ERA_NUM_ENTRIES);
966 for (s.era_cnt = 0; s.era_cnt < (int) num_eras;
967 ++s.era_cnt, rp = rp_backup)
969 era = _nl_select_era_entry (s.era_cnt HELPER_LOCALE_ARG);
970 if (era != NULL && recursive (era->era_format))
971 break;
973 if (s.era_cnt == (int) num_eras)
975 s.era_cnt = -1;
976 if (s.decided == loc)
977 return NULL;
978 else
979 rp = rp_backup;
981 else
983 s.decided = loc;
984 break;
987 s.decided = raw;
989 get_number (0, 9999, 4);
990 tm->tm_year = val - 1900;
991 s.want_century = 0;
992 s.want_xday = 1;
993 break;
994 case 'x':
995 if (s.decided != raw)
997 const char *fmt = _NL_CURRENT (LC_TIME, ERA_D_FMT);
999 if (*fmt == '\0')
1000 fmt = _NL_CURRENT (LC_TIME, D_FMT);
1002 if (!recursive (fmt))
1004 if (s.decided == loc)
1005 return NULL;
1006 else
1007 rp = rp_backup;
1009 else
1011 if (strcmp (fmt, HERE_D_FMT))
1012 s.decided = loc;
1013 break;
1015 s.decided = raw;
1017 if (!recursive (HERE_D_FMT))
1018 return NULL;
1019 break;
1020 case 'X':
1021 if (s.decided != raw)
1023 const char *fmt = _NL_CURRENT (LC_TIME, ERA_T_FMT);
1025 if (*fmt == '\0')
1026 fmt = _NL_CURRENT (LC_TIME, T_FMT);
1028 if (!recursive (fmt))
1030 if (s.decided == loc)
1031 return NULL;
1032 else
1033 rp = rp_backup;
1035 else
1037 if (strcmp (fmt, HERE_T_FMT))
1038 s.decided = loc;
1039 break;
1041 s.decided = raw;
1043 if (!recursive (HERE_T_FMT))
1044 return NULL;
1045 break;
1046 default:
1047 return NULL;
1049 break;
1050 #else
1051 /* We have no information about the era format. Just use
1052 the normal format. */
1053 if (*fmt != 'c' && *fmt != 'C' && *fmt != 'y' && *fmt != 'Y'
1054 && *fmt != 'x' && *fmt != 'X')
1055 /* This is an illegal format. */
1056 return NULL;
1058 goto start_over;
1059 #endif
1060 case 'O':
1061 switch (*fmt++)
1063 case 'b':
1064 case 'B':
1065 case 'h':
1066 /* Match month name. Reprocess as plain 'B'. */
1067 fmt--;
1068 goto start_over;
1069 case 'd':
1070 case 'e':
1071 /* Match day of month using alternate numeric symbols. */
1072 get_alt_number (1, 31, 2);
1073 tm->tm_mday = val;
1074 s.have_mday = 1;
1075 s.want_xday = 1;
1076 break;
1077 case 'H':
1078 /* Match hour in 24-hour clock using alternate numeric
1079 symbols. */
1080 get_alt_number (0, 23, 2);
1081 tm->tm_hour = val;
1082 s.have_I = 0;
1083 break;
1084 case 'I':
1085 /* Match hour in 12-hour clock using alternate numeric
1086 symbols. */
1087 get_alt_number (1, 12, 2);
1088 tm->tm_hour = val % 12;
1089 s.have_I = 1;
1090 break;
1091 case 'm':
1092 /* Match month using alternate numeric symbols. */
1093 get_alt_number (1, 12, 2);
1094 tm->tm_mon = val - 1;
1095 s.have_mon = 1;
1096 s.want_xday = 1;
1097 break;
1098 case 'M':
1099 /* Match minutes using alternate numeric symbols. */
1100 get_alt_number (0, 59, 2);
1101 tm->tm_min = val;
1102 break;
1103 case 'S':
1104 /* Match seconds using alternate numeric symbols. */
1105 get_alt_number (0, 61, 2);
1106 tm->tm_sec = val;
1107 break;
1108 case 'U':
1109 get_alt_number (0, 53, 2);
1110 s.week_no = val;
1111 s.have_uweek = 1;
1112 break;
1113 case 'W':
1114 get_alt_number (0, 53, 2);
1115 s.week_no = val;
1116 s.have_wweek = 1;
1117 break;
1118 case 'V':
1119 get_alt_number (0, 53, 2);
1120 /* XXX This cannot determine any field in TM without
1121 further information. */
1122 break;
1123 case 'w':
1124 /* Match number of weekday using alternate numeric symbols. */
1125 get_alt_number (0, 6, 1);
1126 tm->tm_wday = val;
1127 s.have_wday = 1;
1128 break;
1129 case 'y':
1130 /* Match year within century using alternate numeric symbols. */
1131 get_alt_number (0, 99, 2);
1132 tm->tm_year = val >= 69 ? val : val + 100;
1133 s.want_xday = 1;
1134 break;
1135 default:
1136 return NULL;
1138 break;
1139 default:
1140 return NULL;
1144 if (statep != NULL)
1146 /* Recursive invocation, returning success, so
1147 update parent's struct tm and state. */
1148 *(struct __strptime_state *) statep = s;
1149 *tmp = tmb;
1150 return (char *) rp;
1153 if (s.have_I && s.is_pm)
1154 tm->tm_hour += 12;
1156 if (s.century != -1)
1158 if (s.want_century)
1159 tm->tm_year = tm->tm_year % 100 + (s.century - 19) * 100;
1160 else
1161 /* Only the century, but not the year. Strange, but so be it. */
1162 tm->tm_year = (s.century - 19) * 100;
1165 if (s.era_cnt != -1)
1167 era = _nl_select_era_entry (s.era_cnt HELPER_LOCALE_ARG);
1168 if (era == NULL)
1169 return NULL;
1170 if (s.want_era)
1171 tm->tm_year = (era->start_date[0]
1172 + ((tm->tm_year - era->offset)
1173 * era->absolute_direction));
1174 else
1175 /* Era start year assumed. */
1176 tm->tm_year = era->start_date[0];
1178 else
1179 if (s.want_era)
1181 /* No era found but we have seen an E modifier. Rectify some
1182 values. */
1183 if (s.want_century && s.century == -1 && tm->tm_year < 69)
1184 tm->tm_year += 100;
1187 if (s.want_xday && !s.have_wday)
1189 if ( !(s.have_mon && s.have_mday) && s.have_yday)
1191 /* We don't have tm_mon and/or tm_mday, compute them. */
1192 int t_mon = 0;
1193 while (__mon_yday[__isleap(1900 + tm->tm_year)][t_mon] <= tm->tm_yday)
1194 t_mon++;
1195 if (!s.have_mon)
1196 tm->tm_mon = t_mon - 1;
1197 if (!s.have_mday)
1198 tm->tm_mday =
1199 (tm->tm_yday
1200 - __mon_yday[__isleap(1900 + tm->tm_year)][t_mon - 1] + 1);
1201 s.have_mon = 1;
1202 s.have_mday = 1;
1204 /* Don't crash in day_of_the_week if tm_mon is uninitialized. */
1205 if (s.have_mon || (unsigned) tm->tm_mon <= 11)
1206 day_of_the_week (tm);
1209 if (s.want_xday && !s.have_yday && (s.have_mon || (unsigned) tm->tm_mon <= 11))
1210 day_of_the_year (tm);
1212 if ((s.have_uweek || s.have_wweek) && s.have_wday)
1214 int save_wday = tm->tm_wday;
1215 int save_mday = tm->tm_mday;
1216 int save_mon = tm->tm_mon;
1217 int w_offset = s.have_uweek ? 0 : 1;
1219 tm->tm_mday = 1;
1220 tm->tm_mon = 0;
1221 day_of_the_week (tm);
1222 if (s.have_mday)
1223 tm->tm_mday = save_mday;
1224 if (s.have_mon)
1225 tm->tm_mon = save_mon;
1227 if (!s.have_yday)
1228 tm->tm_yday = ((7 - (tm->tm_wday - w_offset)) % 7
1229 + (s.week_no - 1) * 7
1230 + (save_wday - w_offset + 7) % 7);
1232 if (!s.have_mday || !s.have_mon)
1234 int t_mon = 0;
1235 while (__mon_yday[__isleap(1900 + tm->tm_year)][t_mon]
1236 <= tm->tm_yday)
1237 t_mon++;
1238 if (!s.have_mon)
1239 tm->tm_mon = t_mon - 1;
1240 if (!s.have_mday)
1241 tm->tm_mday =
1242 (tm->tm_yday
1243 - __mon_yday[__isleap(1900 + tm->tm_year)][t_mon - 1] + 1);
1246 tm->tm_wday = save_wday;
1249 return (char *) rp;
1253 char *
1254 strptime (const char *buf, const char *format, struct tm *tm LOCALE_PARAM)
1256 return __strptime_internal (buf, format, tm, NULL LOCALE_ARG);
1259 #ifdef _LIBC
1260 weak_alias (__strptime_l, strptime_l)
1261 #endif