]> 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.
50304ef0 2 Copyright (C) 1996, 1997, 1998 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
UD
6 The GNU C Library is free software; you can redistribute it and/or
7 modify it under the terms of the GNU Library General Public License as
8 published by the Free Software Foundation; either version 2 of the
9 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
14 Library General Public License for more details.
f8adc70c 15
8a4b65b4
UD
16 You should have received a copy of the GNU Library General Public
17 License along with the GNU C Library; see the file COPYING.LIB. If not,
18 write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19 Boston, MA 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
42# if defined (__GNUC__) || (defined (__STDC__) && __STDC__)
43# define __P(args) args
44# else
45# define __P(args) ()
46# endif /* GCC. */
47#endif /* Not __P. */
48
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. */
5290baf0 86#define get_number(from, to) \
f8adc70c
RM
87 do { \
88 val = 0; \
e9dcb080
UD
89 while (*rp == ' ') \
90 ++rp; \
f8adc70c
RM
91 if (*rp < '0' || *rp > '9') \
92 return NULL; \
93 do { \
94 val *= 10; \
95 val += *rp++ - '0'; \
96 } while (val * 10 <= to && *rp >= '0' && *rp <= '9'); \
97 if (val < from || val > to) \
98 return NULL; \
99 } while (0)
5290baf0
UD
100#ifdef _NL_CURRENT
101# define get_alt_number(from, to) \
f8adc70c 102 do { \
5290baf0 103 if (*decided != raw) \
f8adc70c 104 { \
5290baf0
UD
105 const char *alts = _NL_CURRENT (LC_TIME, ALT_DIGITS); \
106 val = 0; \
107 while (*alts != '\0') \
108 { \
109 size_t len = strlen (alts); \
110 if (strncasecmp (alts, rp, len) == 0) \
111 break; \
112 alts = strchr (alts, '\0') + 1; \
113 ++val; \
114 } \
115 if (*alts == '\0') \
116 { \
117 if (*decided == loc && val != 0) \
118 return NULL; \
119 } \
120 else \
121 { \
122 *decided = loc; \
123 break; \
124 } \
f8adc70c 125 } \
5290baf0 126 get_number (from, to); \
f8adc70c 127 } while (0)
5290baf0
UD
128#else
129# define get_alt_number(from, to) \
130 /* We don't have the alternate representation. */ \
131 get_number(from, to)
132#endif
133#define recursive(new_fmt) \
134 (*(new_fmt) != '\0' \
0d8733c4 135 && (rp = strptime_internal (rp, (new_fmt), tm, decided)) != NULL)
d41c6f61 136
f8adc70c 137
5290baf0
UD
138#ifdef _LIBC
139/* This is defined in locale/C-time.c in the GNU libc. */
140extern const struct locale_data _nl_C_LC_TIME;
c2cfb512 141extern const unsigned short int __mon_yday[2][13];
5290baf0
UD
142
143# define weekday_name (&_nl_C_LC_TIME.values[_NL_ITEM_INDEX (DAY_1)].string)
144# define ab_weekday_name \
145 (&_nl_C_LC_TIME.values[_NL_ITEM_INDEX (ABDAY_1)].string)
146# define month_name (&_nl_C_LC_TIME.values[_NL_ITEM_INDEX (MON_1)].string)
147# define ab_month_name (&_nl_C_LC_TIME.values[_NL_ITEM_INDEX (ABMON_1)].string)
148# define HERE_D_T_FMT (_nl_C_LC_TIME.values[_NL_ITEM_INDEX (D_T_FMT)].string)
655b26bb 149# define HERE_D_FMT (_nl_C_LC_TIME.values[_NL_ITEM_INDEX (D_FMT)].string)
5290baf0
UD
150# define HERE_AM_STR (_nl_C_LC_TIME.values[_NL_ITEM_INDEX (AM_STR)].string)
151# define HERE_PM_STR (_nl_C_LC_TIME.values[_NL_ITEM_INDEX (PM_STR)].string)
152# define HERE_T_FMT_AMPM \
153 (_nl_C_LC_TIME.values[_NL_ITEM_INDEX (T_FMT_AMPM)].string)
154# define HERE_T_FMT (_nl_C_LC_TIME.values[_NL_ITEM_INDEX (T_FMT)].string)
50304ef0
UD
155
156# define strncasecmp(s1, s2, n) __strncasecmp (s1, s2, n)
5290baf0
UD
157#else
158static char const weekday_name[][10] =
159 {
160 "Sunday", "Monday", "Tuesday", "Wednesday",
161 "Thursday", "Friday", "Saturday"
162 };
163static char const ab_weekday_name[][4] =
164 {
165 "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
166 };
167static char const month_name[][10] =
168 {
169 "January", "February", "March", "April", "May", "June",
170 "July", "August", "September", "October", "November", "December"
171 };
172static char const ab_month_name[][4] =
173 {
174 "Jan", "Feb", "Mar", "Apr", "May", "Jun",
175 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
176 };
177# define HERE_D_T_FMT "%a %b %e %H:%M:%S %Y"
178# define HERE_D_FMT "%m/%d/%y"
179# define HERE_AM_STR "AM"
180# define HERE_PM_STR "PM"
181# define HERE_T_FMT_AMPM "%I:%M:%S %p"
182# define HERE_T_FMT "%H:%M:%S"
c2cfb512
UD
183
184const unsigned short int __mon_yday[1][13] =
185 {
186 /* Normal years. */
187 { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 },
188 /* Leap years. */
189 { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 }
190 };
5290baf0
UD
191#endif
192
193/* Status of lookup: do we use the locale data or the raw data? */
194enum locale_status { not, loc, raw };
195
c2cfb512
UD
196
197#ifndef __isleap
198/* Nonzero if YEAR is a leap year (every 4 years,
199 except every 100th isn't, and every 400th is). */
200# define __isleap(year) \
201 ((year) % 4 == 0 && ((year) % 100 != 0 || (year) % 400 == 0))
202#endif
203
204/* Compute the day of the week. */
205static void
206day_of_the_week (struct tm *tm)
207{
208 /* We know that January 1st 1970 was a Thursday (= 4). Compute the
209 the difference between this data in the one on TM and so determine
210 the weekday. */
6269e521 211 int corr_year = 1900 + tm->tm_year - (tm->tm_mon < 2);
c2cfb512 212 int wday = (-473
6269e521 213 + (365 * (tm->tm_year - 70))
c2cfb512
UD
214 + (corr_year / 4)
215 - ((corr_year / 4) / 25) + ((corr_year / 4) % 25 < 0)
216 + (((corr_year / 4) / 25) / 4)
217 + __mon_yday[0][tm->tm_mon]
6269e521
UD
218 + tm->tm_mday - 1);
219 tm->tm_wday = wday % 7;
c2cfb512
UD
220}
221
6269e521 222/* Compute the day of the year. */
c2cfb512
UD
223static void
224day_of_the_year (struct tm *tm)
225{
6269e521
UD
226 tm->tm_yday = (__mon_yday[__isleap (1900 + tm->tm_year)][tm->tm_mon]
227 + (tm->tm_mday - 1));
c2cfb512
UD
228}
229
0c5ecdc4 230static char *
dfd2257a
UD
231#ifdef _LIBC
232internal_function
233#endif
0c5ecdc4
UD
234strptime_internal __P ((const char *buf, const char *format, struct tm *tm,
235 enum locale_status *decided));
5290baf0
UD
236
237static char *
dfd2257a
UD
238#ifdef _LIBC
239internal_function
240#endif
5290baf0
UD
241strptime_internal (buf, format, tm, decided)
242 const char *buf;
243 const char *format;
244 struct tm *tm;
245 enum locale_status *decided;
f8adc70c
RM
246{
247 const char *rp;
248 const char *fmt;
249 int cnt;
250 size_t val;
251 int have_I, is_pm;
a6ff34d7 252 int century, want_century;
c2cfb512
UD
253 int have_wday, want_xday;
254 int have_yday;
f8adc70c
RM
255
256 rp = buf;
257 fmt = format;
258 have_I = is_pm = 0;
a6ff34d7
UD
259 century = -1;
260 want_century = 0;
c2cfb512 261 have_wday = want_xday = have_yday = 0;
f8adc70c
RM
262
263 while (*fmt != '\0')
264 {
265 /* A white space in the format string matches 0 more or white
266 space in the input string. */
267 if (isspace (*fmt))
268 {
269 while (isspace (*rp))
270 ++rp;
271 ++fmt;
272 continue;
273 }
274
275 /* Any character but `%' must be matched by the same character
276 in the iput string. */
277 if (*fmt != '%')
278 {
279 match_char (*fmt++, *rp++);
280 continue;
281 }
282
283 ++fmt;
5290baf0
UD
284#ifndef _NL_CURRENT
285 /* We need this for handling the `E' modifier. */
286 start_over:
287#endif
f8adc70c
RM
288 switch (*fmt++)
289 {
290 case '%':
291 /* Match the `%' character itself. */
292 match_char ('%', *rp++);
293 break;
294 case 'a':
295 case 'A':
296 /* Match day of week. */
297 for (cnt = 0; cnt < 7; ++cnt)
298 {
5290baf0
UD
299#ifdef _NL_CURRENT
300 if (*decided !=raw)
301 {
0d8733c4 302 if (match_string (_NL_CURRENT (LC_TIME, DAY_1 + cnt), rp))
5290baf0
UD
303 {
304 if (*decided == not
0d8733c4
UD
305 && strcmp (_NL_CURRENT (LC_TIME, DAY_1 + cnt),
306 weekday_name[cnt]))
5290baf0
UD
307 *decided = loc;
308 break;
309 }
0d8733c4 310 if (match_string (_NL_CURRENT (LC_TIME, ABDAY_1 + cnt), rp))
5290baf0
UD
311 {
312 if (*decided == not
0d8733c4
UD
313 && strcmp (_NL_CURRENT (LC_TIME, ABDAY_1 + cnt),
314 ab_weekday_name[cnt]))
5290baf0
UD
315 *decided = loc;
316 break;
317 }
318 }
319#endif
320 if (*decided != loc
0d8733c4
UD
321 && (match_string (weekday_name[cnt], rp)
322 || match_string (ab_weekday_name[cnt], rp)))
5290baf0
UD
323 {
324 *decided = raw;
325 break;
326 }
f8adc70c
RM
327 }
328 if (cnt == 7)
329 /* Does not match a weekday name. */
330 return NULL;
331 tm->tm_wday = cnt;
c2cfb512 332 have_wday = 1;
f8adc70c
RM
333 break;
334 case 'b':
335 case 'B':
336 case 'h':
337 /* Match month name. */
338 for (cnt = 0; cnt < 12; ++cnt)
339 {
5290baf0
UD
340#ifdef _NL_CURRENT
341 if (*decided !=raw)
342 {
0d8733c4 343 if (match_string (_NL_CURRENT (LC_TIME, MON_1 + cnt), rp))
5290baf0
UD
344 {
345 if (*decided == not
0d8733c4
UD
346 && strcmp (_NL_CURRENT (LC_TIME, MON_1 + cnt),
347 month_name[cnt]))
5290baf0
UD
348 *decided = loc;
349 break;
350 }
0d8733c4 351 if (match_string (_NL_CURRENT (LC_TIME, ABMON_1 + cnt), rp))
5290baf0
UD
352 {
353 if (*decided == not
0d8733c4
UD
354 && strcmp (_NL_CURRENT (LC_TIME, ABMON_1 + cnt),
355 ab_month_name[cnt]))
5290baf0
UD
356 *decided = loc;
357 break;
358 }
359 }
360#endif
0d8733c4
UD
361 if (match_string (month_name[cnt], rp)
362 || match_string (ab_month_name[cnt], rp))
5290baf0
UD
363 {
364 *decided = raw;
365 break;
366 }
f8adc70c
RM
367 }
368 if (cnt == 12)
369 /* Does not match a month name. */
370 return NULL;
371 tm->tm_mon = cnt;
c2cfb512 372 want_xday = 1;
f8adc70c
RM
373 break;
374 case 'c':
375 /* Match locale's date and time format. */
5290baf0
UD
376#ifdef _NL_CURRENT
377 if (*decided != raw)
378 {
379 if (!recursive (_NL_CURRENT (LC_TIME, D_T_FMT)))
380 {
381 if (*decided == loc)
382 return NULL;
383 }
384 else
385 {
386 if (*decided == not &&
387 strcmp (_NL_CURRENT (LC_TIME, D_T_FMT), HERE_D_T_FMT))
388 *decided = loc;
c2cfb512 389 want_xday = 1;
5290baf0
UD
390 break;
391 }
392 *decided = raw;
393 }
394#endif
395 if (!recursive (HERE_D_T_FMT))
396 return NULL;
c2cfb512 397 want_xday = 1;
f8adc70c
RM
398 break;
399 case 'C':
400 /* Match century number. */
401 get_number (0, 99);
a6ff34d7 402 century = val;
c2cfb512 403 want_xday = 1;
f8adc70c
RM
404 break;
405 case 'd':
406 case 'e':
407 /* Match day of month. */
408 get_number (1, 31);
409 tm->tm_mday = val;
e9dcb080
UD
410 want_xday = 1;
411 break;
412 case 'F':
413 if (!recursive ("%Y-%m-%d"))
414 return NULL;
415 want_xday = 1;
f8adc70c 416 break;
5290baf0
UD
417 case 'x':
418#ifdef _NL_CURRENT
419 if (*decided != raw)
420 {
421 if (!recursive (_NL_CURRENT (LC_TIME, D_FMT)))
422 {
423 if (*decided == loc)
424 return NULL;
425 }
426 else
427 {
428 if (decided == not
429 && strcmp (_NL_CURRENT (LC_TIME, D_FMT), HERE_D_FMT))
430 *decided = loc;
c2cfb512 431 want_xday = 1;
5290baf0
UD
432 break;
433 }
434 *decided = raw;
435 }
436#endif
437 /* Fall through. */
f8adc70c
RM
438 case 'D':
439 /* Match standard day format. */
5290baf0
UD
440 if (!recursive (HERE_D_FMT))
441 return NULL;
c2cfb512 442 want_xday = 1;
f8adc70c 443 break;
e9dcb080 444 case 'k':
f8adc70c
RM
445 case 'H':
446 /* Match hour in 24-hour clock. */
447 get_number (0, 23);
448 tm->tm_hour = val;
449 have_I = 0;
450 break;
451 case 'I':
452 /* Match hour in 12-hour clock. */
453 get_number (1, 12);
779ae82e 454 tm->tm_hour = val % 12;
f8adc70c
RM
455 have_I = 1;
456 break;
457 case 'j':
458 /* Match day number of year. */
459 get_number (1, 366);
460 tm->tm_yday = val - 1;
c2cfb512 461 have_yday = 1;
f8adc70c
RM
462 break;
463 case 'm':
464 /* Match number of month. */
465 get_number (1, 12);
466 tm->tm_mon = val - 1;
c2cfb512 467 want_xday = 1;
f8adc70c
RM
468 break;
469 case 'M':
470 /* Match minute. */
471 get_number (0, 59);
472 tm->tm_min = val;
473 break;
474 case 'n':
475 case 't':
476 /* Match any white space. */
477 while (isspace (*rp))
478 ++rp;
479 break;
480 case 'p':
481 /* Match locale's equivalent of AM/PM. */
5290baf0
UD
482#ifdef _NL_CURRENT
483 if (*decided != raw)
f8adc70c 484 {
5290baf0
UD
485 if (match_string (_NL_CURRENT (LC_TIME, AM_STR), rp))
486 {
487 if (strcmp (_NL_CURRENT (LC_TIME, AM_STR), HERE_AM_STR))
488 *decided = loc;
489 break;
490 }
491 if (match_string (_NL_CURRENT (LC_TIME, PM_STR), rp))
492 {
493 if (strcmp (_NL_CURRENT (LC_TIME, PM_STR), HERE_PM_STR))
494 *decided = loc;
495 is_pm = 1;
496 break;
497 }
498 *decided = raw;
f8adc70c 499 }
5290baf0
UD
500#endif
501 if (!match_string (HERE_AM_STR, rp))
502 if (match_string (HERE_PM_STR, rp))
503 is_pm = 1;
504 else
505 return NULL;
506 break;
f8adc70c 507 case 'r':
5290baf0
UD
508#ifdef _NL_CURRENT
509 if (*decided != raw)
510 {
511 if (!recursive (_NL_CURRENT (LC_TIME, T_FMT_AMPM)))
512 {
513 if (*decided == loc)
514 return NULL;
515 }
516 else
517 {
518 if (*decided == not &&
519 strcmp (_NL_CURRENT (LC_TIME, T_FMT_AMPM),
520 HERE_T_FMT_AMPM))
521 *decided = loc;
522 break;
523 }
524 *decided = raw;
525 }
526#endif
527 if (!recursive (HERE_T_FMT_AMPM))
528 return NULL;
f8adc70c
RM
529 break;
530 case 'R':
5290baf0
UD
531 if (!recursive ("%H:%M"))
532 return NULL;
f8adc70c 533 break;
d41c6f61
UD
534 case 's':
535 {
536 /* The number of seconds may be very high so we cannot use
537 the `get_number' macro. Instead read the number
538 character for character and construct the result while
539 doing this. */
540 time_t secs;
541 if (*rp < '0' || *rp > '9')
542 /* We need at least one digit. */
543 return NULL;
544
545 do
546 {
547 secs *= 10;
548 secs += *rp++ - '0';
549 }
550 while (*rp >= '0' && *rp <= '9');
551
5290baf0 552 if (localtime_r (&secs, tm) == NULL)
d41c6f61
UD
553 /* Error in function. */
554 return NULL;
555 }
556 break;
f8adc70c
RM
557 case 'S':
558 get_number (0, 61);
559 tm->tm_sec = val;
560 break;
5290baf0
UD
561 case 'X':
562#ifdef _NL_CURRENT
563 if (*decided != raw)
564 {
565 if (!recursive (_NL_CURRENT (LC_TIME, T_FMT)))
566 {
567 if (*decided == loc)
568 return NULL;
569 }
570 else
571 {
572 if (strcmp (_NL_CURRENT (LC_TIME, T_FMT), HERE_T_FMT))
573 *decided = loc;
574 break;
575 }
576 *decided = raw;
577 }
578#endif
579 /* Fall through. */
f8adc70c 580 case 'T':
5290baf0
UD
581 if (!recursive (HERE_T_FMT))
582 return NULL;
f8adc70c 583 break;
d41c6f61
UD
584 case 'u':
585 get_number (1, 7);
586 tm->tm_wday = val % 7;
c2cfb512 587 have_wday = 1;
d41c6f61
UD
588 break;
589 case 'g':
590 get_number (0, 99);
591 /* XXX This cannot determine any field in TM. */
592 break;
593 case 'G':
594 if (*rp < '0' || *rp > '9')
595 return NULL;
596 /* XXX Ignore the number since we would need some more
597 information to compute a real date. */
598 do
599 ++rp;
600 while (*rp >= '0' && *rp <= '9');
601 break;
f8adc70c
RM
602 case 'U':
603 case 'V':
604 case 'W':
605 get_number (0, 53);
5290baf0
UD
606 /* XXX This cannot determine any field in TM without some
607 information. */
f8adc70c
RM
608 break;
609 case 'w':
610 /* Match number of weekday. */
611 get_number (0, 6);
612 tm->tm_wday = val;
c2cfb512 613 have_wday = 1;
f8adc70c 614 break;
f8adc70c
RM
615 case 'y':
616 /* Match year within century. */
617 get_number (0, 99);
685c3086 618 /* The "Year 2000: The Millennium Rollover" paper suggests that
1cab5444
UD
619 values in the range 69-99 refer to the twentieth century. */
620 tm->tm_year = val >= 69 ? val : val + 100;
685c3086 621 /* Indicate that we want to use the century, if specified. */
a6ff34d7 622 want_century = 1;
c2cfb512 623 want_xday = 1;
f8adc70c
RM
624 break;
625 case 'Y':
626 /* Match year including century number. */
1618c590 627 get_number (0, 9999);
0d8733c4 628 tm->tm_year = val - 1900;
a6ff34d7 629 want_century = 0;
c2cfb512 630 want_xday = 1;
f8adc70c
RM
631 break;
632 case 'Z':
633 /* XXX How to handle this? */
634 break;
635 case 'E':
5290baf0 636#ifdef _NL_CURRENT
f8adc70c
RM
637 switch (*fmt++)
638 {
639 case 'c':
640 /* Match locale's alternate date and time format. */
5290baf0
UD
641 if (*decided != raw)
642 {
643 const char *fmt = _NL_CURRENT (LC_TIME, ERA_D_T_FMT);
644
645 if (*fmt == '\0')
646 fmt = _NL_CURRENT (LC_TIME, D_T_FMT);
647
648 if (!recursive (fmt))
649 {
650 if (*decided == loc)
651 return NULL;
652 }
653 else
654 {
655 if (strcmp (fmt, HERE_D_T_FMT))
656 *decided = loc;
c2cfb512 657 want_xday = 1;
5290baf0
UD
658 break;
659 }
660 *decided = raw;
661 }
662 /* The C locale has no era information, so use the
663 normal representation. */
664 if (!recursive (HERE_D_T_FMT))
665 return NULL;
c2cfb512 666 want_xday = 1;
f8adc70c
RM
667 break;
668 case 'C':
669 case 'y':
670 case 'Y':
671 /* Match name of base year in locale's alternate
672 representation. */
673 /* XXX This is currently not implemented. It should
5290baf0 674 use the value _NL_CURRENT (LC_TIME, ERA). */
f8adc70c
RM
675 break;
676 case 'x':
5290baf0
UD
677 if (*decided != raw)
678 {
679 const char *fmt = _NL_CURRENT (LC_TIME, ERA_D_FMT);
680
681 if (*fmt == '\0')
682 fmt = _NL_CURRENT (LC_TIME, D_FMT);
683
684 if (!recursive (fmt))
685 {
686 if (*decided == loc)
687 return NULL;
688 }
689 else
690 {
691 if (strcmp (fmt, HERE_D_FMT))
692 *decided = loc;
693 break;
694 }
695 *decided = raw;
696 }
697 if (!recursive (HERE_D_FMT))
698 return NULL;
f8adc70c
RM
699 break;
700 case 'X':
5290baf0
UD
701 if (*decided != raw)
702 {
703 const char *fmt = _NL_CURRENT (LC_TIME, ERA_T_FMT);
704
705 if (*fmt == '\0')
706 fmt = _NL_CURRENT (LC_TIME, T_FMT);
707
708 if (!recursive (fmt))
709 {
710 if (*decided == loc)
711 return NULL;
712 }
713 else
714 {
715 if (strcmp (fmt, HERE_T_FMT))
716 *decided = loc;
717 break;
718 }
719 *decided = raw;
720 }
721 if (!recursive (HERE_T_FMT))
722 return NULL;
f8adc70c
RM
723 break;
724 default:
725 return NULL;
726 }
727 break;
5290baf0
UD
728#else
729 /* We have no information about the era format. Just use
730 the normal format. */
731 if (*fmt != 'c' && *fmt != 'C' && *fmt != 'y' && *fmt != 'Y'
732 && *fmt != 'x' && *fmt != 'X')
733 /* This is an illegal format. */
734 return NULL;
735
736 goto start_over;
737#endif
f8adc70c
RM
738 case 'O':
739 switch (*fmt++)
740 {
741 case 'd':
742 case 'e':
743 /* Match day of month using alternate numeric symbols. */
744 get_alt_number (1, 31);
745 tm->tm_mday = val;
c2cfb512 746 want_xday = 1;
f8adc70c
RM
747 break;
748 case 'H':
749 /* Match hour in 24-hour clock using alternate numeric
750 symbols. */
751 get_alt_number (0, 23);
752 tm->tm_hour = val;
753 have_I = 0;
754 break;
755 case 'I':
756 /* Match hour in 12-hour clock using alternate numeric
757 symbols. */
758 get_alt_number (1, 12);
759 tm->tm_hour = val - 1;
760 have_I = 1;
761 break;
762 case 'm':
763 /* Match month using alternate numeric symbols. */
764 get_alt_number (1, 12);
765 tm->tm_mon = val - 1;
c2cfb512 766 want_xday = 1;
f8adc70c
RM
767 break;
768 case 'M':
769 /* Match minutes using alternate numeric symbols. */
770 get_alt_number (0, 59);
771 tm->tm_min = val;
772 break;
773 case 'S':
774 /* Match seconds using alternate numeric symbols. */
775 get_alt_number (0, 61);
776 tm->tm_sec = val;
777 break;
778 case 'U':
779 case 'V':
780 case 'W':
781 get_alt_number (0, 53);
5290baf0
UD
782 /* XXX This cannot determine any field in TM without
783 further information. */
f8adc70c
RM
784 break;
785 case 'w':
786 /* Match number of weekday using alternate numeric symbols. */
787 get_alt_number (0, 6);
788 tm->tm_wday = val;
c2cfb512 789 have_wday = 1;
f8adc70c
RM
790 break;
791 case 'y':
792 /* Match year within century using alternate numeric symbols. */
793 get_alt_number (0, 99);
1618c590 794 tm->tm_year = val >= 69 ? val : val + 100;
c2cfb512 795 want_xday = 1;
f8adc70c
RM
796 break;
797 default:
798 return NULL;
799 }
800 break;
801 default:
802 return NULL;
803 }
804 }
805
806 if (have_I && is_pm)
807 tm->tm_hour += 12;
d41c6f61 808
a6ff34d7
UD
809 if (want_century && century != -1)
810 tm->tm_year = tm->tm_year % 100 + (century - 19) * 100;
811
c2cfb512
UD
812 if (want_xday && !have_wday)
813 day_of_the_week (tm);
814 if (want_xday && !have_yday)
815 day_of_the_year (tm);
816
f8adc70c
RM
817 return (char *) rp;
818}
5290baf0
UD
819
820
821char *
822strptime (buf, format, tm)
823 const char *buf;
824 const char *format;
825 struct tm *tm;
826{
827 enum locale_status decided;
828#ifdef _NL_CURRENT
829 decided = not;
830#else
831 decided = raw;
832#endif
833 return strptime_internal (buf, format, tm, &decided);
834}