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