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