Update.
[glibc.git] / time / strptime.c
blob5f8271e2504b52816fe094be1fbea2d6e7192c70
1 /* Convert a string representation of time to a time value.
2 Copyright (C) 1996, 1997, 1998, 1999 Free Software Foundation, Inc.
3 This file is part of the GNU C Library.
4 Contributed by Ulrich Drepper <drepper@cygnus.com>, 1996.
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.
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.
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. */
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. */
26 #ifdef HAVE_CONFIG_H
27 # include <config.h>
28 #endif
30 #include <ctype.h>
31 #include <langinfo.h>
32 #include <limits.h>
33 #include <string.h>
34 #include <time.h>
36 #ifdef _LIBC
37 # include "../locale/localeinfo.h"
38 #endif
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. */
50 #if ! HAVE_LOCALTIME_R && ! defined localtime_r
51 # ifdef _LIBC
52 # define localtime_r __localtime_r
53 # else
54 /* Approximate localtime_r as best we can in its absence. */
55 # define localtime_r my_localtime_r
56 static struct tm *localtime_r __P ((const time_t *, struct tm *));
57 static struct tm *
58 localtime_r (t, tp)
59 const time_t *t;
60 struct tm *tp;
62 struct tm *l = localtime (t);
63 if (! l)
64 return 0;
65 *tp = *l;
66 return tp;
68 # endif /* ! _LIBC */
69 #endif /* ! HAVE_LOCALTIME_R && ! defined (localtime_r) */
72 #define match_char(ch1, ch2) if (ch1 != ch2) return NULL
73 #if defined __GNUC__ && __GNUC__ >= 2
74 # define match_string(cs1, s2) \
75 ({ size_t len = strlen (cs1); \
76 int result = strncasecmp ((cs1), (s2), len) == 0; \
77 if (result) (s2) += len; \
78 result; })
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
84 /* We intentionally do not use isdigit() for testing because this will
85 lead to problems with the wide character version. */
86 #define get_number(from, to, n) \
87 do { \
88 int __n = n; \
89 val = 0; \
90 while (*rp == ' ') \
91 ++rp; \
92 if (*rp < '0' || *rp > '9') \
93 return NULL; \
94 do { \
95 val *= 10; \
96 val += *rp++ - '0'; \
97 } while (--__n > 0 && val * 10 <= to && *rp >= '0' && *rp <= '9'); \
98 if (val < from || val > to) \
99 return NULL; \
100 } while (0)
101 #ifdef _NL_CURRENT
102 # define get_alt_number(from, to, n) \
103 ({ \
104 __label__ do_normal; \
105 if (*decided != raw) \
107 const char *alts = _NL_CURRENT (LC_TIME, ALT_DIGITS); \
108 int __n = n; \
109 int any = 0; \
110 while (*rp == ' ') \
111 ++rp; \
112 val = 0; \
113 do { \
114 val *= 10; \
115 while (*alts != '\0') \
117 size_t len = strlen (alts); \
118 if (strncasecmp (alts, rp, len) == 0) \
119 break; \
120 alts += len + 1; \
121 ++val; \
123 if (*alts == '\0') \
125 if (*decided == not && ! any) \
126 goto do_normal; \
127 /* If we haven't read anything it's an error. */ \
128 if (! any) \
129 return NULL; \
130 /* Correct the premature multiplication. */ \
131 val /= 10; \
132 break; \
134 else \
135 *decided = loc; \
136 } while (--__n > 0 && val * 10 <= to); \
137 if (val < from || val > to) \
138 return NULL; \
140 else \
142 do_normal: \
143 get_number (from, to, n); \
145 0; \
147 #else
148 # define get_alt_number(from, to, n) \
149 /* We don't have the alternate representation. */ \
150 get_number(from, to, n)
151 #endif
152 #define recursive(new_fmt) \
153 (*(new_fmt) != '\0' \
154 && (rp = strptime_internal (rp, (new_fmt), tm, decided)) != NULL)
157 #ifdef _LIBC
158 /* This is defined in locale/C-time.c in the GNU libc. */
159 extern const struct locale_data _nl_C_LC_TIME;
160 extern const unsigned short int __mon_yday[2][13];
162 # define weekday_name (&_nl_C_LC_TIME.values[_NL_ITEM_INDEX (DAY_1)].string)
163 # define ab_weekday_name \
164 (&_nl_C_LC_TIME.values[_NL_ITEM_INDEX (ABDAY_1)].string)
165 # define month_name (&_nl_C_LC_TIME.values[_NL_ITEM_INDEX (MON_1)].string)
166 # define ab_month_name (&_nl_C_LC_TIME.values[_NL_ITEM_INDEX (ABMON_1)].string)
167 # define HERE_D_T_FMT (_nl_C_LC_TIME.values[_NL_ITEM_INDEX (D_T_FMT)].string)
168 # define HERE_D_FMT (_nl_C_LC_TIME.values[_NL_ITEM_INDEX (D_FMT)].string)
169 # define HERE_AM_STR (_nl_C_LC_TIME.values[_NL_ITEM_INDEX (AM_STR)].string)
170 # define HERE_PM_STR (_nl_C_LC_TIME.values[_NL_ITEM_INDEX (PM_STR)].string)
171 # define HERE_T_FMT_AMPM \
172 (_nl_C_LC_TIME.values[_NL_ITEM_INDEX (T_FMT_AMPM)].string)
173 # define HERE_T_FMT (_nl_C_LC_TIME.values[_NL_ITEM_INDEX (T_FMT)].string)
175 # define strncasecmp(s1, s2, n) __strncasecmp (s1, s2, n)
176 #else
177 static char const weekday_name[][10] =
179 "Sunday", "Monday", "Tuesday", "Wednesday",
180 "Thursday", "Friday", "Saturday"
182 static char const ab_weekday_name[][4] =
184 "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
186 static char const month_name[][10] =
188 "January", "February", "March", "April", "May", "June",
189 "July", "August", "September", "October", "November", "December"
191 static char const ab_month_name[][4] =
193 "Jan", "Feb", "Mar", "Apr", "May", "Jun",
194 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
196 # define HERE_D_T_FMT "%a %b %e %H:%M:%S %Y"
197 # define HERE_D_FMT "%m/%d/%y"
198 # define HERE_AM_STR "AM"
199 # define HERE_PM_STR "PM"
200 # define HERE_T_FMT_AMPM "%I:%M:%S %p"
201 # define HERE_T_FMT "%H:%M:%S"
203 const unsigned short int __mon_yday[2][13] =
205 /* Normal years. */
206 { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 },
207 /* Leap years. */
208 { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 }
210 #endif
212 /* Status of lookup: do we use the locale data or the raw data? */
213 enum locale_status { not, loc, raw };
216 #ifndef __isleap
217 /* Nonzero if YEAR is a leap year (every 4 years,
218 except every 100th isn't, and every 400th is). */
219 # define __isleap(year) \
220 ((year) % 4 == 0 && ((year) % 100 != 0 || (year) % 400 == 0))
221 #endif
223 /* Compute the day of the week. */
224 static void
225 day_of_the_week (struct tm *tm)
227 /* We know that January 1st 1970 was a Thursday (= 4). Compute the
228 the difference between this data in the one on TM and so determine
229 the weekday. */
230 int corr_year = 1900 + tm->tm_year - (tm->tm_mon < 2);
231 int wday = (-473
232 + (365 * (tm->tm_year - 70))
233 + (corr_year / 4)
234 - ((corr_year / 4) / 25) + ((corr_year / 4) % 25 < 0)
235 + (((corr_year / 4) / 25) / 4)
236 + __mon_yday[0][tm->tm_mon]
237 + tm->tm_mday - 1);
238 tm->tm_wday = wday % 7;
241 /* Compute the day of the year. */
242 static void
243 day_of_the_year (struct tm *tm)
245 tm->tm_yday = (__mon_yday[__isleap (1900 + tm->tm_year)][tm->tm_mon]
246 + (tm->tm_mday - 1));
249 static char *
250 #ifdef _LIBC
251 internal_function
252 #endif
253 strptime_internal __P ((const char *buf, const char *format, struct tm *tm,
254 enum locale_status *decided));
256 static char *
257 #ifdef _LIBC
258 internal_function
259 #endif
260 strptime_internal (buf, format, tm, decided)
261 const char *buf;
262 const char *format;
263 struct tm *tm;
264 enum locale_status *decided;
266 const char *rp;
267 const char *fmt;
268 int cnt;
269 size_t val;
270 int have_I, is_pm;
271 int century, want_century;
272 int have_wday, want_xday;
273 int have_yday;
274 int have_mon, have_mday;
276 rp = buf;
277 fmt = format;
278 have_I = is_pm = 0;
279 century = -1;
280 want_century = 0;
281 have_wday = want_xday = have_yday = have_mon = have_mday = 0;
283 while (*fmt != '\0')
285 /* A white space in the format string matches 0 more or white
286 space in the input string. */
287 if (isspace (*fmt))
289 while (isspace (*rp))
290 ++rp;
291 ++fmt;
292 continue;
295 /* Any character but `%' must be matched by the same character
296 in the iput string. */
297 if (*fmt != '%')
299 match_char (*fmt++, *rp++);
300 continue;
303 ++fmt;
304 #ifndef _NL_CURRENT
305 /* We need this for handling the `E' modifier. */
306 start_over:
307 #endif
308 switch (*fmt++)
310 case '%':
311 /* Match the `%' character itself. */
312 match_char ('%', *rp++);
313 break;
314 case 'a':
315 case 'A':
316 /* Match day of week. */
317 for (cnt = 0; cnt < 7; ++cnt)
319 #ifdef _NL_CURRENT
320 if (*decided !=raw)
322 if (match_string (_NL_CURRENT (LC_TIME, DAY_1 + cnt), rp))
324 if (*decided == not
325 && strcmp (_NL_CURRENT (LC_TIME, DAY_1 + cnt),
326 weekday_name[cnt]))
327 *decided = loc;
328 break;
330 if (match_string (_NL_CURRENT (LC_TIME, ABDAY_1 + cnt), rp))
332 if (*decided == not
333 && strcmp (_NL_CURRENT (LC_TIME, ABDAY_1 + cnt),
334 ab_weekday_name[cnt]))
335 *decided = loc;
336 break;
339 #endif
340 if (*decided != loc
341 && (match_string (weekday_name[cnt], rp)
342 || match_string (ab_weekday_name[cnt], rp)))
344 *decided = raw;
345 break;
348 if (cnt == 7)
349 /* Does not match a weekday name. */
350 return NULL;
351 tm->tm_wday = cnt;
352 have_wday = 1;
353 break;
354 case 'b':
355 case 'B':
356 case 'h':
357 /* Match month name. */
358 for (cnt = 0; cnt < 12; ++cnt)
360 #ifdef _NL_CURRENT
361 if (*decided !=raw)
363 if (match_string (_NL_CURRENT (LC_TIME, MON_1 + cnt), rp))
365 if (*decided == not
366 && strcmp (_NL_CURRENT (LC_TIME, MON_1 + cnt),
367 month_name[cnt]))
368 *decided = loc;
369 break;
371 if (match_string (_NL_CURRENT (LC_TIME, ABMON_1 + cnt), rp))
373 if (*decided == not
374 && strcmp (_NL_CURRENT (LC_TIME, ABMON_1 + cnt),
375 ab_month_name[cnt]))
376 *decided = loc;
377 break;
380 #endif
381 if (match_string (month_name[cnt], rp)
382 || match_string (ab_month_name[cnt], rp))
384 *decided = raw;
385 break;
388 if (cnt == 12)
389 /* Does not match a month name. */
390 return NULL;
391 tm->tm_mon = cnt;
392 want_xday = 1;
393 break;
394 case 'c':
395 /* Match locale's date and time format. */
396 #ifdef _NL_CURRENT
397 if (*decided != raw)
399 if (!recursive (_NL_CURRENT (LC_TIME, D_T_FMT)))
401 if (*decided == loc)
402 return NULL;
404 else
406 if (*decided == not &&
407 strcmp (_NL_CURRENT (LC_TIME, D_T_FMT), HERE_D_T_FMT))
408 *decided = loc;
409 want_xday = 1;
410 break;
412 *decided = raw;
414 #endif
415 if (!recursive (HERE_D_T_FMT))
416 return NULL;
417 want_xday = 1;
418 break;
419 case 'C':
420 /* Match century number. */
421 get_number (0, 99, 2);
422 century = val;
423 want_xday = 1;
424 break;
425 case 'd':
426 case 'e':
427 /* Match day of month. */
428 get_number (1, 31, 2);
429 tm->tm_mday = val;
430 have_mday = 1;
431 want_xday = 1;
432 break;
433 case 'F':
434 if (!recursive ("%Y-%m-%d"))
435 return NULL;
436 want_xday = 1;
437 break;
438 case 'x':
439 #ifdef _NL_CURRENT
440 if (*decided != raw)
442 if (!recursive (_NL_CURRENT (LC_TIME, D_FMT)))
444 if (*decided == loc)
445 return NULL;
447 else
449 if (decided == not
450 && strcmp (_NL_CURRENT (LC_TIME, D_FMT), HERE_D_FMT))
451 *decided = loc;
452 want_xday = 1;
453 break;
455 *decided = raw;
457 #endif
458 /* Fall through. */
459 case 'D':
460 /* Match standard day format. */
461 if (!recursive (HERE_D_FMT))
462 return NULL;
463 want_xday = 1;
464 break;
465 case 'k':
466 case 'H':
467 /* Match hour in 24-hour clock. */
468 get_number (0, 23, 2);
469 tm->tm_hour = val;
470 have_I = 0;
471 break;
472 case 'I':
473 /* Match hour in 12-hour clock. */
474 get_number (1, 12, 2);
475 tm->tm_hour = val % 12;
476 have_I = 1;
477 break;
478 case 'j':
479 /* Match day number of year. */
480 get_number (1, 366, 3);
481 tm->tm_yday = val - 1;
482 have_yday = 1;
483 break;
484 case 'm':
485 /* Match number of month. */
486 get_number (1, 12, 2);
487 tm->tm_mon = val - 1;
488 have_mon = 1;
489 want_xday = 1;
490 break;
491 case 'M':
492 /* Match minute. */
493 get_number (0, 59, 2);
494 tm->tm_min = val;
495 break;
496 case 'n':
497 case 't':
498 /* Match any white space. */
499 while (isspace (*rp))
500 ++rp;
501 break;
502 case 'p':
503 /* Match locale's equivalent of AM/PM. */
504 #ifdef _NL_CURRENT
505 if (*decided != raw)
507 if (match_string (_NL_CURRENT (LC_TIME, AM_STR), rp))
509 if (strcmp (_NL_CURRENT (LC_TIME, AM_STR), HERE_AM_STR))
510 *decided = loc;
511 break;
513 if (match_string (_NL_CURRENT (LC_TIME, PM_STR), rp))
515 if (strcmp (_NL_CURRENT (LC_TIME, PM_STR), HERE_PM_STR))
516 *decided = loc;
517 is_pm = 1;
518 break;
520 *decided = raw;
522 #endif
523 if (!match_string (HERE_AM_STR, rp))
524 if (match_string (HERE_PM_STR, rp))
525 is_pm = 1;
526 else
527 return NULL;
528 break;
529 case 'r':
530 #ifdef _NL_CURRENT
531 if (*decided != raw)
533 if (!recursive (_NL_CURRENT (LC_TIME, T_FMT_AMPM)))
535 if (*decided == loc)
536 return NULL;
538 else
540 if (*decided == not &&
541 strcmp (_NL_CURRENT (LC_TIME, T_FMT_AMPM),
542 HERE_T_FMT_AMPM))
543 *decided = loc;
544 break;
546 *decided = raw;
548 #endif
549 if (!recursive (HERE_T_FMT_AMPM))
550 return NULL;
551 break;
552 case 'R':
553 if (!recursive ("%H:%M"))
554 return NULL;
555 break;
556 case 's':
558 /* The number of seconds may be very high so we cannot use
559 the `get_number' macro. Instead read the number
560 character for character and construct the result while
561 doing this. */
562 time_t secs = 0;
563 if (*rp < '0' || *rp > '9')
564 /* We need at least one digit. */
565 return NULL;
569 secs *= 10;
570 secs += *rp++ - '0';
572 while (*rp >= '0' && *rp <= '9');
574 if (localtime_r (&secs, tm) == NULL)
575 /* Error in function. */
576 return NULL;
578 break;
579 case 'S':
580 get_number (0, 61, 2);
581 tm->tm_sec = val;
582 break;
583 case 'X':
584 #ifdef _NL_CURRENT
585 if (*decided != raw)
587 if (!recursive (_NL_CURRENT (LC_TIME, T_FMT)))
589 if (*decided == loc)
590 return NULL;
592 else
594 if (strcmp (_NL_CURRENT (LC_TIME, T_FMT), HERE_T_FMT))
595 *decided = loc;
596 break;
598 *decided = raw;
600 #endif
601 /* Fall through. */
602 case 'T':
603 if (!recursive (HERE_T_FMT))
604 return NULL;
605 break;
606 case 'u':
607 get_number (1, 7, 1);
608 tm->tm_wday = val % 7;
609 have_wday = 1;
610 break;
611 case 'g':
612 get_number (0, 99, 2);
613 /* XXX This cannot determine any field in TM. */
614 break;
615 case 'G':
616 if (*rp < '0' || *rp > '9')
617 return NULL;
618 /* XXX Ignore the number since we would need some more
619 information to compute a real date. */
621 ++rp;
622 while (*rp >= '0' && *rp <= '9');
623 break;
624 case 'U':
625 case 'V':
626 case 'W':
627 get_number (0, 53, 2);
628 /* XXX This cannot determine any field in TM without some
629 information. */
630 break;
631 case 'w':
632 /* Match number of weekday. */
633 get_number (0, 6, 1);
634 tm->tm_wday = val;
635 have_wday = 1;
636 break;
637 case 'y':
638 /* Match year within century. */
639 get_number (0, 99, 2);
640 /* The "Year 2000: The Millennium Rollover" paper suggests that
641 values in the range 69-99 refer to the twentieth century. */
642 tm->tm_year = val >= 69 ? val : val + 100;
643 /* Indicate that we want to use the century, if specified. */
644 want_century = 1;
645 want_xday = 1;
646 break;
647 case 'Y':
648 /* Match year including century number. */
649 get_number (0, 9999, 4);
650 tm->tm_year = val - 1900;
651 want_century = 0;
652 want_xday = 1;
653 break;
654 case 'Z':
655 /* XXX How to handle this? */
656 break;
657 case 'E':
658 #ifdef _NL_CURRENT
659 switch (*fmt++)
661 case 'c':
662 /* Match locale's alternate date and time format. */
663 if (*decided != raw)
665 const char *fmt = _NL_CURRENT (LC_TIME, ERA_D_T_FMT);
667 if (*fmt == '\0')
668 fmt = _NL_CURRENT (LC_TIME, D_T_FMT);
670 if (!recursive (fmt))
672 if (*decided == loc)
673 return NULL;
675 else
677 if (strcmp (fmt, HERE_D_T_FMT))
678 *decided = loc;
679 want_xday = 1;
680 break;
682 *decided = raw;
684 /* The C locale has no era information, so use the
685 normal representation. */
686 if (!recursive (HERE_D_T_FMT))
687 return NULL;
688 want_xday = 1;
689 break;
690 case 'C':
691 case 'y':
692 case 'Y':
693 /* Match name of base year in locale's alternate
694 representation. */
695 /* XXX This is currently not implemented. It should
696 use the value _NL_CURRENT (LC_TIME, ERA). */
697 break;
698 case 'x':
699 if (*decided != raw)
701 const char *fmt = _NL_CURRENT (LC_TIME, ERA_D_FMT);
703 if (*fmt == '\0')
704 fmt = _NL_CURRENT (LC_TIME, D_FMT);
706 if (!recursive (fmt))
708 if (*decided == loc)
709 return NULL;
711 else
713 if (strcmp (fmt, HERE_D_FMT))
714 *decided = loc;
715 break;
717 *decided = raw;
719 if (!recursive (HERE_D_FMT))
720 return NULL;
721 break;
722 case 'X':
723 if (*decided != raw)
725 const char *fmt = _NL_CURRENT (LC_TIME, ERA_T_FMT);
727 if (*fmt == '\0')
728 fmt = _NL_CURRENT (LC_TIME, T_FMT);
730 if (!recursive (fmt))
732 if (*decided == loc)
733 return NULL;
735 else
737 if (strcmp (fmt, HERE_T_FMT))
738 *decided = loc;
739 break;
741 *decided = raw;
743 if (!recursive (HERE_T_FMT))
744 return NULL;
745 break;
746 default:
747 return NULL;
749 break;
750 #else
751 /* We have no information about the era format. Just use
752 the normal format. */
753 if (*fmt != 'c' && *fmt != 'C' && *fmt != 'y' && *fmt != 'Y'
754 && *fmt != 'x' && *fmt != 'X')
755 /* This is an illegal format. */
756 return NULL;
758 goto start_over;
759 #endif
760 case 'O':
761 switch (*fmt++)
763 case 'd':
764 case 'e':
765 /* Match day of month using alternate numeric symbols. */
766 get_alt_number (1, 31, 2);
767 tm->tm_mday = val;
768 have_mday = 1;
769 want_xday = 1;
770 break;
771 case 'H':
772 /* Match hour in 24-hour clock using alternate numeric
773 symbols. */
774 get_alt_number (0, 23, 2);
775 tm->tm_hour = val;
776 have_I = 0;
777 break;
778 case 'I':
779 /* Match hour in 12-hour clock using alternate numeric
780 symbols. */
781 get_alt_number (1, 12, 2);
782 tm->tm_hour = val - 1;
783 have_I = 1;
784 break;
785 case 'm':
786 /* Match month using alternate numeric symbols. */
787 get_alt_number (1, 12, 2);
788 tm->tm_mon = val - 1;
789 have_mon = 1;
790 want_xday = 1;
791 break;
792 case 'M':
793 /* Match minutes using alternate numeric symbols. */
794 get_alt_number (0, 59, 2);
795 tm->tm_min = val;
796 break;
797 case 'S':
798 /* Match seconds using alternate numeric symbols. */
799 get_alt_number (0, 61, 2);
800 tm->tm_sec = val;
801 break;
802 case 'U':
803 case 'V':
804 case 'W':
805 get_alt_number (0, 53, 2);
806 /* XXX This cannot determine any field in TM without
807 further information. */
808 break;
809 case 'w':
810 /* Match number of weekday using alternate numeric symbols. */
811 get_alt_number (0, 6, 1);
812 tm->tm_wday = val;
813 have_wday = 1;
814 break;
815 case 'y':
816 /* Match year within century using alternate numeric symbols. */
817 get_alt_number (0, 99, 2);
818 tm->tm_year = val >= 69 ? val : val + 100;
819 want_xday = 1;
820 break;
821 default:
822 return NULL;
824 break;
825 default:
826 return NULL;
830 if (have_I && is_pm)
831 tm->tm_hour += 12;
833 if (want_century && century != -1)
834 tm->tm_year = tm->tm_year % 100 + (century - 19) * 100;
836 if (want_xday && !have_wday) {
837 if ( !(have_mon && have_mday) && have_yday) {
838 /* we don't have tm_mon and/or tm_mday, compute them */
839 int t_mon = 0;
840 while (__mon_yday[__isleap(1900 + tm->tm_year)][t_mon] <= tm->tm_yday)
841 t_mon++;
842 if (!have_mon)
843 tm->tm_mon = t_mon - 1;
844 if (!have_mday)
845 tm->tm_mday = tm->tm_yday - __mon_yday[__isleap(1900 + tm->tm_year)][t_mon - 1] + 1;
847 day_of_the_week (tm);
849 if (want_xday && !have_yday)
850 day_of_the_year (tm);
852 return (char *) rp;
856 char *
857 strptime (buf, format, tm)
858 const char *buf;
859 const char *format;
860 struct tm *tm;
862 enum locale_status decided;
863 #ifdef _NL_CURRENT
864 decided = not;
865 #else
866 decided = raw;
867 #endif
868 return strptime_internal (buf, format, tm, &decided);