Remove mtime from mpd_Song
[libmpd.git] / src / lib_strptime.c
blob61beadba5e0c1438d0b1c5d4cad202a761899ef1
1 #ifdef WIN32
2 /* Copyright (C) 2002, 2004, 2005, 2007 Free Software Foundation, Inc.
3 This file is part of the GNU C Library.
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2, or (at your option)
8 any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License along
16 with this program; if not, write to the Free Software Foundation,
17 Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
19 #ifndef _LIBC
20 # include <config.h>
21 #endif
23 #include <time.h>
25 #include <assert.h>
26 #include <ctype.h>
27 #ifdef _LIBC
28 # include <langinfo.h>
29 #endif
30 #include <limits.h>
31 #include <string.h>
32 #include <stdbool.h>
34 #ifdef _LIBC
35 # include "../locale/localeinfo.h"
36 #endif
38 /**
39 * Windows does not have a locatime_r,
40 * it doesn't need it as localtime() is thread safe.
41 * This wrapper should 'fake' localtime_r
43 struct tm *
44 localtime_r (const time_t *timer, struct tm *result)
46 struct tm *local_result;
47 /* This should work as it is thread safe on winblows */
48 local_result = localtime (timer);
50 if (local_result == NULL || result == NULL)
51 return NULL;
53 memcpy (result, local_result, sizeof (result));
54 return result;
57 #ifndef _LIBC
58 enum ptime_locale_status { not, loc, raw };
59 #endif
63 #define match_char(ch1, ch2) if (ch1 != ch2) return NULL
64 #if defined _LIBC && defined __GNUC__ && __GNUC__ >= 2
65 # define match_string(cs1, s2) \
66 ({ size_t len = strlen (cs1); \
67 int result = __strncasecmp_l ((cs1), (s2), len, locale) == 0; \
68 if (result) (s2) += len; \
69 result; })
70 #else
71 /* Oh come on. Get a reasonable compiler. */
72 # define match_string(cs1, s2) \
73 (strncasecmp ((cs1), (s2), strlen (cs1)) ? 0 : ((s2) += strlen (cs1), 1))
74 #endif
75 /* We intentionally do not use isdigit() for testing because this will
76 lead to problems with the wide character version. */
77 #define get_number(from, to, n) \
78 do { \
79 int __n = n; \
80 val = 0; \
81 while (*rp == ' ') \
82 ++rp; \
83 if (*rp < '0' || *rp > '9') \
84 return NULL; \
85 do { \
86 val *= 10; \
87 val += *rp++ - '0'; \
88 } while (--__n > 0 && val * 10 <= to && *rp >= '0' && *rp <= '9'); \
89 if (val < from || val > to) \
90 return NULL; \
91 } while (0)
92 #ifdef _NL_CURRENT
93 # define get_alt_number(from, to, n) \
94 ({ \
95 __label__ do_normal; \
97 if (*decided != raw) \
98 { \
99 val = _nl_parse_alt_digit (&rp HELPER_LOCALE_ARG); \
100 if (val == -1 && *decided != loc) \
102 *decided = loc; \
103 goto do_normal; \
105 if (val < from || val > to) \
106 return NULL; \
108 else \
110 do_normal: \
111 get_number (from, to, n); \
113 0; \
115 #else
116 # define get_alt_number(from, to, n) \
117 /* We don't have the alternate representation. */ \
118 get_number(from, to, n)
119 #endif
120 #define recursive(new_fmt) \
121 (*(new_fmt) != '\0' \
122 && (rp = __strptime_internal (rp, (new_fmt), tm, \
123 decided, era_cnt LOCALE_ARG)) != NULL)
126 #ifdef _LIBC
127 /* This is defined in locale/C-time.c in the GNU libc. */
128 extern const struct locale_data _nl_C_LC_TIME attribute_hidden;
130 # define weekday_name (&_nl_C_LC_TIME.values[_NL_ITEM_INDEX (DAY_1)].string)
131 # define ab_weekday_name \
132 (&_nl_C_LC_TIME.values[_NL_ITEM_INDEX (ABDAY_1)].string)
133 # define month_name (&_nl_C_LC_TIME.values[_NL_ITEM_INDEX (MON_1)].string)
134 # define ab_month_name (&_nl_C_LC_TIME.values[_NL_ITEM_INDEX (ABMON_1)].string)
135 # define HERE_D_T_FMT (_nl_C_LC_TIME.values[_NL_ITEM_INDEX (D_T_FMT)].string)
136 # define HERE_D_FMT (_nl_C_LC_TIME.values[_NL_ITEM_INDEX (D_FMT)].string)
137 # define HERE_AM_STR (_nl_C_LC_TIME.values[_NL_ITEM_INDEX (AM_STR)].string)
138 # define HERE_PM_STR (_nl_C_LC_TIME.values[_NL_ITEM_INDEX (PM_STR)].string)
139 # define HERE_T_FMT_AMPM \
140 (_nl_C_LC_TIME.values[_NL_ITEM_INDEX (T_FMT_AMPM)].string)
141 # define HERE_T_FMT (_nl_C_LC_TIME.values[_NL_ITEM_INDEX (T_FMT)].string)
143 # define strncasecmp(s1, s2, n) __strncasecmp (s1, s2, n)
144 #else
145 static char const weekday_name[][10] =
147 "Sunday", "Monday", "Tuesday", "Wednesday",
148 "Thursday", "Friday", "Saturday"
150 static char const ab_weekday_name[][4] =
152 "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
154 static char const month_name[][10] =
156 "January", "February", "March", "April", "May", "June",
157 "July", "August", "September", "October", "November", "December"
159 static char const ab_month_name[][4] =
161 "Jan", "Feb", "Mar", "Apr", "May", "Jun",
162 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
164 # define HERE_D_T_FMT "%a %b %e %H:%M:%S %Y"
165 # define HERE_D_FMT "%m/%d/%y"
166 # define HERE_AM_STR "AM"
167 # define HERE_PM_STR "PM"
168 # define HERE_T_FMT_AMPM "%I:%M:%S %p"
169 # define HERE_T_FMT "%H:%M:%S"
171 static const unsigned short int __mon_yday[2][13] =
173 /* Normal years. */
174 { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 },
175 /* Leap years. */
176 { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 }
178 #endif
180 #if defined _LIBC
181 /* We use this code also for the extended locale handling where the
182 function gets as an additional argument the locale which has to be
183 used. To access the values we have to redefine the _NL_CURRENT
184 macro. */
185 # define strptime __strptime_l
186 # undef _NL_CURRENT
187 # define _NL_CURRENT(category, item) \
188 (current->values[_NL_ITEM_INDEX (item)].string)
189 # undef _NL_CURRENT_WORD
190 # define _NL_CURRENT_WORD(category, item) \
191 (current->values[_NL_ITEM_INDEX (item)].word)
192 # define LOCALE_PARAM , locale
193 # define LOCALE_ARG , locale
194 # define LOCALE_PARAM_PROTO , __locale_t locale
195 # define LOCALE_PARAM_DECL __locale_t locale;
196 # define HELPER_LOCALE_ARG , current
197 # define ISSPACE(Ch) __isspace_l (Ch, locale)
198 #else
199 # define LOCALE_PARAM
200 # define LOCALE_ARG
201 # define LOCALE_PARAM_DECL
202 # define LOCALE_PARAM_PROTO
203 # define HELPER_LOCALE_ARG
204 # define ISSPACE(Ch) isspace (Ch)
205 #endif
210 #ifndef __isleap
211 /* Nonzero if YEAR is a leap year (every 4 years,
212 except every 100th isn't, and every 400th is). */
213 # define __isleap(year) \
214 ((year) % 4 == 0 && ((year) % 100 != 0 || (year) % 400 == 0))
215 #endif
217 /* Compute the day of the week. */
218 static void
219 day_of_the_week (struct tm *tm)
221 /* We know that January 1st 1970 was a Thursday (= 4). Compute the
222 the difference between this data in the one on TM and so determine
223 the weekday. */
224 int corr_year = 1900 + tm->tm_year - (tm->tm_mon < 2);
225 int wday = (-473
226 + (365 * (tm->tm_year - 70))
227 + (corr_year / 4)
228 - ((corr_year / 4) / 25) + ((corr_year / 4) % 25 < 0)
229 + (((corr_year / 4) / 25) / 4)
230 + __mon_yday[0][tm->tm_mon]
231 + tm->tm_mday - 1);
232 tm->tm_wday = ((wday % 7) + 7) % 7;
235 /* Compute the day of the year. */
236 static void
237 day_of_the_year (struct tm *tm)
239 tm->tm_yday = (__mon_yday[__isleap (1900 + tm->tm_year)][tm->tm_mon]
240 + (tm->tm_mday - 1));
244 #ifdef _LIBC
245 char *
246 internal_function
247 #else
248 static char *
249 #endif
250 __strptime_internal (rp, fmt, tm, decided, era_cnt LOCALE_PARAM)
251 const char *rp;
252 const char *fmt;
253 struct tm *tm;
254 enum ptime_locale_status *decided;
255 int era_cnt;
256 LOCALE_PARAM_DECL
258 #ifdef _LIBC
259 struct locale_data *const current = locale->__locales[LC_TIME];
260 #endif
262 const char *rp_backup;
263 int cnt;
264 size_t val;
265 int have_I, is_pm;
266 int century, want_century;
267 int want_era;
268 int have_wday, want_xday;
269 int have_yday;
270 int have_mon, have_mday;
271 int have_uweek, have_wweek;
272 int week_no;
273 size_t num_eras;
274 struct era_entry *era;
276 have_I = is_pm = 0;
277 century = -1;
278 want_century = 0;
279 want_era = 0;
280 era = NULL;
281 week_no = 0;
283 have_wday = want_xday = have_yday = have_mon = have_mday = have_uweek = 0;
284 have_wweek = 0;
286 while (*fmt != '\0')
288 /* A white space in the format string matches 0 more or white
289 space in the input string. */
290 if (ISSPACE (*fmt))
292 while (ISSPACE (*rp))
293 ++rp;
294 ++fmt;
295 continue;
298 /* Any character but `%' must be matched by the same character
299 in the iput string. */
300 if (*fmt != '%')
302 match_char (*fmt++, *rp++);
303 continue;
306 ++fmt;
307 #ifndef _NL_CURRENT
308 /* We need this for handling the `E' modifier. */
309 start_over:
310 #endif
312 /* Make back up of current processing pointer. */
313 rp_backup = rp;
315 switch (*fmt++)
317 case '%':
318 /* Match the `%' character itself. */
319 match_char ('%', *rp++);
320 break;
321 case 'a':
322 case 'A':
323 /* Match day of week. */
324 for (cnt = 0; cnt < 7; ++cnt)
326 #ifdef _NL_CURRENT
327 if (*decided !=raw)
329 if (match_string (_NL_CURRENT (LC_TIME, DAY_1 + cnt), rp))
331 if (*decided == not
332 && strcmp (_NL_CURRENT (LC_TIME, DAY_1 + cnt),
333 weekday_name[cnt]))
334 *decided = loc;
335 break;
337 if (match_string (_NL_CURRENT (LC_TIME, ABDAY_1 + cnt), rp))
339 if (*decided == not
340 && strcmp (_NL_CURRENT (LC_TIME, ABDAY_1 + cnt),
341 ab_weekday_name[cnt]))
342 *decided = loc;
343 break;
346 #endif
347 if (*decided != loc
348 && (match_string (weekday_name[cnt], rp)
349 || match_string (ab_weekday_name[cnt], rp)))
351 *decided = raw;
352 break;
355 if (cnt == 7)
356 /* Does not match a weekday name. */
357 return NULL;
358 tm->tm_wday = cnt;
359 have_wday = 1;
360 break;
361 case 'b':
362 case 'B':
363 case 'h':
364 /* Match month name. */
365 for (cnt = 0; cnt < 12; ++cnt)
367 #ifdef _NL_CURRENT
368 if (*decided !=raw)
370 if (match_string (_NL_CURRENT (LC_TIME, MON_1 + cnt), rp))
372 if (*decided == not
373 && strcmp (_NL_CURRENT (LC_TIME, MON_1 + cnt),
374 month_name[cnt]))
375 *decided = loc;
376 break;
378 if (match_string (_NL_CURRENT (LC_TIME, ABMON_1 + cnt), rp))
380 if (*decided == not
381 && strcmp (_NL_CURRENT (LC_TIME, ABMON_1 + cnt),
382 ab_month_name[cnt]))
383 *decided = loc;
384 break;
387 #endif
388 if (match_string (month_name[cnt], rp)
389 || match_string (ab_month_name[cnt], rp))
391 *decided = raw;
392 break;
395 if (cnt == 12)
396 /* Does not match a month name. */
397 return NULL;
398 tm->tm_mon = cnt;
399 want_xday = 1;
400 break;
401 case 'c':
402 /* Match locale's date and time format. */
403 #ifdef _NL_CURRENT
404 if (*decided != raw)
406 if (!recursive (_NL_CURRENT (LC_TIME, D_T_FMT)))
408 if (*decided == loc)
409 return NULL;
410 else
411 rp = rp_backup;
413 else
415 if (*decided == not &&
416 strcmp (_NL_CURRENT (LC_TIME, D_T_FMT), HERE_D_T_FMT))
417 *decided = loc;
418 want_xday = 1;
419 break;
421 *decided = raw;
423 #endif
424 if (!recursive (HERE_D_T_FMT))
425 return NULL;
426 want_xday = 1;
427 break;
428 case 'C':
429 /* Match century number. */
430 match_century:
431 get_number (0, 99, 2);
432 century = val;
433 want_xday = 1;
434 break;
435 case 'd':
436 case 'e':
437 /* Match day of month. */
438 get_number (1, 31, 2);
439 tm->tm_mday = val;
440 have_mday = 1;
441 want_xday = 1;
442 break;
443 case 'F':
444 if (!recursive ("%Y-%m-%d"))
445 return NULL;
446 want_xday = 1;
447 break;
448 case 'x':
449 #ifdef _NL_CURRENT
450 if (*decided != raw)
452 if (!recursive (_NL_CURRENT (LC_TIME, D_FMT)))
454 if (*decided == loc)
455 return NULL;
456 else
457 rp = rp_backup;
459 else
461 if (*decided == not
462 && strcmp (_NL_CURRENT (LC_TIME, D_FMT), HERE_D_FMT))
463 *decided = loc;
464 want_xday = 1;
465 break;
467 *decided = raw;
469 #endif
470 /* Fall through. */
471 case 'D':
472 /* Match standard day format. */
473 if (!recursive (HERE_D_FMT))
474 return NULL;
475 want_xday = 1;
476 break;
477 case 'k':
478 case 'H':
479 /* Match hour in 24-hour clock. */
480 get_number (0, 23, 2);
481 tm->tm_hour = val;
482 have_I = 0;
483 break;
484 case 'l':
485 /* Match hour in 12-hour clock. GNU extension. */
486 case 'I':
487 /* Match hour in 12-hour clock. */
488 get_number (1, 12, 2);
489 tm->tm_hour = val % 12;
490 have_I = 1;
491 break;
492 case 'j':
493 /* Match day number of year. */
494 get_number (1, 366, 3);
495 tm->tm_yday = val - 1;
496 have_yday = 1;
497 break;
498 case 'm':
499 /* Match number of month. */
500 get_number (1, 12, 2);
501 tm->tm_mon = val - 1;
502 have_mon = 1;
503 want_xday = 1;
504 break;
505 case 'M':
506 /* Match minute. */
507 get_number (0, 59, 2);
508 tm->tm_min = val;
509 break;
510 case 'n':
511 case 't':
512 /* Match any white space. */
513 while (ISSPACE (*rp))
514 ++rp;
515 break;
516 case 'p':
517 /* Match locale's equivalent of AM/PM. */
518 #ifdef _NL_CURRENT
519 if (*decided != raw)
521 if (match_string (_NL_CURRENT (LC_TIME, AM_STR), rp))
523 if (strcmp (_NL_CURRENT (LC_TIME, AM_STR), HERE_AM_STR))
524 *decided = loc;
525 break;
527 if (match_string (_NL_CURRENT (LC_TIME, PM_STR), rp))
529 if (strcmp (_NL_CURRENT (LC_TIME, PM_STR), HERE_PM_STR))
530 *decided = loc;
531 is_pm = 1;
532 break;
534 *decided = raw;
536 #endif
537 if (!match_string (HERE_AM_STR, rp))
539 if (match_string (HERE_PM_STR, rp))
540 is_pm = 1;
541 else
542 return NULL;
544 break;
545 case 'r':
546 #ifdef _NL_CURRENT
547 if (*decided != raw)
549 if (!recursive (_NL_CURRENT (LC_TIME, T_FMT_AMPM)))
551 if (*decided == loc)
552 return NULL;
553 else
554 rp = rp_backup;
556 else
558 if (*decided == not &&
559 strcmp (_NL_CURRENT (LC_TIME, T_FMT_AMPM),
560 HERE_T_FMT_AMPM))
561 *decided = loc;
562 break;
564 *decided = raw;
566 #endif
567 if (!recursive (HERE_T_FMT_AMPM))
568 return NULL;
569 break;
570 case 'R':
571 if (!recursive ("%H:%M"))
572 return NULL;
573 break;
574 case 's':
576 /* The number of seconds may be very high so we cannot use
577 the `get_number' macro. Instead read the number
578 character for character and construct the result while
579 doing this. */
580 time_t secs = 0;
581 if (*rp < '0' || *rp > '9')
582 /* We need at least one digit. */
583 return NULL;
587 secs *= 10;
588 secs += *rp++ - '0';
590 while (*rp >= '0' && *rp <= '9');
591 if (localtime_r (&secs, tm) == NULL)
592 /* Error in function. */
593 return NULL;
595 break;
596 case 'S':
597 get_number (0, 61, 2);
598 tm->tm_sec = val;
599 break;
600 case 'X':
601 #ifdef _NL_CURRENT
602 if (*decided != raw)
604 if (!recursive (_NL_CURRENT (LC_TIME, T_FMT)))
606 if (*decided == loc)
607 return NULL;
608 else
609 rp = rp_backup;
611 else
613 if (strcmp (_NL_CURRENT (LC_TIME, T_FMT), HERE_T_FMT))
614 *decided = loc;
615 break;
617 *decided = raw;
619 #endif
620 /* Fall through. */
621 case 'T':
622 if (!recursive (HERE_T_FMT))
623 return NULL;
624 break;
625 case 'u':
626 get_number (1, 7, 1);
627 tm->tm_wday = val % 7;
628 have_wday = 1;
629 break;
630 case 'g':
631 get_number (0, 99, 2);
632 /* XXX This cannot determine any field in TM. */
633 break;
634 case 'G':
635 if (*rp < '0' || *rp > '9')
636 return NULL;
637 /* XXX Ignore the number since we would need some more
638 information to compute a real date. */
640 ++rp;
641 while (*rp >= '0' && *rp <= '9');
642 break;
643 case 'U':
644 get_number (0, 53, 2);
645 week_no = val;
646 have_uweek = 1;
647 break;
648 case 'W':
649 get_number (0, 53, 2);
650 week_no = val;
651 have_wweek = 1;
652 break;
653 case 'V':
654 get_number (0, 53, 2);
655 /* XXX This cannot determine any field in TM without some
656 information. */
657 break;
658 case 'w':
659 /* Match number of weekday. */
660 get_number (0, 6, 1);
661 tm->tm_wday = val;
662 have_wday = 1;
663 break;
664 case 'y':
665 match_year_in_century:
666 /* Match year within century. */
667 get_number (0, 99, 2);
668 /* The "Year 2000: The Millennium Rollover" paper suggests that
669 values in the range 69-99 refer to the twentieth century. */
670 tm->tm_year = val >= 69 ? val : val + 100;
671 /* Indicate that we want to use the century, if specified. */
672 want_century = 1;
673 want_xday = 1;
674 break;
675 case 'Y':
676 /* Match year including century number. */
677 get_number (0, 9999, 4);
678 tm->tm_year = val - 1900;
679 want_century = 0;
680 want_xday = 1;
681 break;
682 case 'Z':
683 /* XXX How to handle this? */
684 break;
685 case 'z':
686 /* We recognize two formats: if two digits are given, these
687 specify hours. If fours digits are used, minutes are
688 also specified. */
690 bool neg;
691 int n;
693 val = 0;
694 while (*rp == ' ')
695 ++rp;
696 if (*rp != '+' && *rp != '-')
697 return NULL;
698 neg = *rp++ == '-';
699 n = 0;
700 while (n < 4 && *rp >= '0' && *rp <= '9')
702 val = val * 10 + *rp++ - '0';
703 ++n;
705 if (n == 2)
706 val *= 100;
707 else if (n != 4)
708 /* Only two or four digits recognized. */
709 return NULL;
710 else
712 /* We have to convert the minutes into decimal. */
713 if (val % 100 >= 60)
714 return NULL;
715 val = (val / 100) * 100 + ((val % 100) * 50) / 30;
717 if (val > 1200)
718 return NULL;
719 #if defined _LIBC || HAVE_TM_GMTOFF
720 tm->tm_gmtoff = (val * 3600) / 100;
721 if (neg)
722 tm->tm_gmtoff = -tm->tm_gmtoff;
723 #endif
725 break;
726 case 'E':
727 #ifdef _NL_CURRENT
728 switch (*fmt++)
730 case 'c':
731 /* Match locale's alternate date and time format. */
732 if (*decided != raw)
734 const char *fmt = _NL_CURRENT (LC_TIME, ERA_D_T_FMT);
736 if (*fmt == '\0')
737 fmt = _NL_CURRENT (LC_TIME, D_T_FMT);
739 if (!recursive (fmt))
741 if (*decided == loc)
742 return NULL;
743 else
744 rp = rp_backup;
746 else
748 if (strcmp (fmt, HERE_D_T_FMT))
749 *decided = loc;
750 want_xday = 1;
751 break;
753 *decided = raw;
755 /* The C locale has no era information, so use the
756 normal representation. */
757 if (!recursive (HERE_D_T_FMT))
758 return NULL;
759 want_xday = 1;
760 break;
761 case 'C':
762 if (*decided != raw)
764 if (era_cnt >= 0)
766 era = _nl_select_era_entry (era_cnt HELPER_LOCALE_ARG);
767 if (era != NULL && match_string (era->era_name, rp))
769 *decided = loc;
770 break;
772 else
773 return NULL;
776 num_eras = _NL_CURRENT_WORD (LC_TIME,
777 _NL_TIME_ERA_NUM_ENTRIES);
778 for (era_cnt = 0; era_cnt < (int) num_eras;
779 ++era_cnt, rp = rp_backup)
781 era = _nl_select_era_entry (era_cnt
782 HELPER_LOCALE_ARG);
783 if (era != NULL && match_string (era->era_name, rp))
785 *decided = loc;
786 break;
789 if (era_cnt != (int) num_eras)
790 break;
792 era_cnt = -1;
793 if (*decided == loc)
794 return NULL;
796 *decided = raw;
798 /* The C locale has no era information, so use the
799 normal representation. */
800 goto match_century;
801 case 'y':
802 if (*decided != raw)
804 get_number(0, 9999, 4);
805 tm->tm_year = val;
806 want_era = 1;
807 want_xday = 1;
808 want_century = 1;
810 if (era_cnt >= 0)
812 assert (*decided == loc);
814 era = _nl_select_era_entry (era_cnt HELPER_LOCALE_ARG);
815 bool match = false;
816 if (era != NULL)
818 int delta = ((tm->tm_year - era->offset)
819 * era->absolute_direction);
820 match = (delta >= 0
821 && delta < (((int64_t) era->stop_date[0]
822 - (int64_t) era->start_date[0])
823 * era->absolute_direction));
825 if (! match)
826 return NULL;
828 break;
831 num_eras = _NL_CURRENT_WORD (LC_TIME,
832 _NL_TIME_ERA_NUM_ENTRIES);
833 for (era_cnt = 0; era_cnt < (int) num_eras; ++era_cnt)
835 era = _nl_select_era_entry (era_cnt
836 HELPER_LOCALE_ARG);
837 if (era != NULL)
839 int delta = ((tm->tm_year - era->offset)
840 * era->absolute_direction);
841 if (delta >= 0
842 && delta < (((int64_t) era->stop_date[0]
843 - (int64_t) era->start_date[0])
844 * era->absolute_direction))
846 *decided = loc;
847 break;
851 if (era_cnt != (int) num_eras)
852 break;
854 era_cnt = -1;
855 if (*decided == loc)
856 return NULL;
858 *decided = raw;
861 goto match_year_in_century;
862 case 'Y':
863 if (*decided != raw)
865 num_eras = _NL_CURRENT_WORD (LC_TIME,
866 _NL_TIME_ERA_NUM_ENTRIES);
867 for (era_cnt = 0; era_cnt < (int) num_eras;
868 ++era_cnt, rp = rp_backup)
870 era = _nl_select_era_entry (era_cnt HELPER_LOCALE_ARG);
871 if (era != NULL && recursive (era->era_format))
872 break;
874 if (era_cnt == (int) num_eras)
876 era_cnt = -1;
877 if (*decided == loc)
878 return NULL;
879 else
880 rp = rp_backup;
882 else
884 *decided = loc;
885 era_cnt = -1;
886 break;
889 *decided = raw;
891 get_number (0, 9999, 4);
892 tm->tm_year = val - 1900;
893 want_century = 0;
894 want_xday = 1;
895 break;
896 case 'x':
897 if (*decided != raw)
899 const char *fmt = _NL_CURRENT (LC_TIME, ERA_D_FMT);
901 if (*fmt == '\0')
902 fmt = _NL_CURRENT (LC_TIME, D_FMT);
904 if (!recursive (fmt))
906 if (*decided == loc)
907 return NULL;
908 else
909 rp = rp_backup;
911 else
913 if (strcmp (fmt, HERE_D_FMT))
914 *decided = loc;
915 break;
917 *decided = raw;
919 if (!recursive (HERE_D_FMT))
920 return NULL;
921 break;
922 case 'X':
923 if (*decided != raw)
925 const char *fmt = _NL_CURRENT (LC_TIME, ERA_T_FMT);
927 if (*fmt == '\0')
928 fmt = _NL_CURRENT (LC_TIME, T_FMT);
930 if (!recursive (fmt))
932 if (*decided == loc)
933 return NULL;
934 else
935 rp = rp_backup;
937 else
939 if (strcmp (fmt, HERE_T_FMT))
940 *decided = loc;
941 break;
943 *decided = raw;
945 if (!recursive (HERE_T_FMT))
946 return NULL;
947 break;
948 default:
949 return NULL;
951 break;
952 #else
953 /* We have no information about the era format. Just use
954 the normal format. */
955 if (*fmt != 'c' && *fmt != 'C' && *fmt != 'y' && *fmt != 'Y'
956 && *fmt != 'x' && *fmt != 'X')
957 /* This is an illegal format. */
958 return NULL;
960 goto start_over;
961 #endif
962 case 'O':
963 switch (*fmt++)
965 case 'd':
966 case 'e':
967 /* Match day of month using alternate numeric symbols. */
968 get_alt_number (1, 31, 2);
969 tm->tm_mday = val;
970 have_mday = 1;
971 want_xday = 1;
972 break;
973 case 'H':
974 /* Match hour in 24-hour clock using alternate numeric
975 symbols. */
976 get_alt_number (0, 23, 2);
977 tm->tm_hour = val;
978 have_I = 0;
979 break;
980 case 'I':
981 /* Match hour in 12-hour clock using alternate numeric
982 symbols. */
983 get_alt_number (1, 12, 2);
984 tm->tm_hour = val % 12;
985 have_I = 1;
986 break;
987 case 'm':
988 /* Match month using alternate numeric symbols. */
989 get_alt_number (1, 12, 2);
990 tm->tm_mon = val - 1;
991 have_mon = 1;
992 want_xday = 1;
993 break;
994 case 'M':
995 /* Match minutes using alternate numeric symbols. */
996 get_alt_number (0, 59, 2);
997 tm->tm_min = val;
998 break;
999 case 'S':
1000 /* Match seconds using alternate numeric symbols. */
1001 get_alt_number (0, 61, 2);
1002 tm->tm_sec = val;
1003 break;
1004 case 'U':
1005 get_alt_number (0, 53, 2);
1006 week_no = val;
1007 have_uweek = 1;
1008 break;
1009 case 'W':
1010 get_alt_number (0, 53, 2);
1011 week_no = val;
1012 have_wweek = 1;
1013 break;
1014 case 'V':
1015 get_alt_number (0, 53, 2);
1016 /* XXX This cannot determine any field in TM without
1017 further information. */
1018 break;
1019 case 'w':
1020 /* Match number of weekday using alternate numeric symbols. */
1021 get_alt_number (0, 6, 1);
1022 tm->tm_wday = val;
1023 have_wday = 1;
1024 break;
1025 case 'y':
1026 /* Match year within century using alternate numeric symbols. */
1027 get_alt_number (0, 99, 2);
1028 tm->tm_year = val >= 69 ? val : val + 100;
1029 want_xday = 1;
1030 break;
1031 default:
1032 return NULL;
1034 break;
1035 default:
1036 return NULL;
1040 if (have_I && is_pm)
1041 tm->tm_hour += 12;
1043 if (century != -1)
1045 if (want_century)
1046 tm->tm_year = tm->tm_year % 100 + (century - 19) * 100;
1047 else
1048 /* Only the century, but not the year. Strange, but so be it. */
1049 tm->tm_year = (century - 19) * 100;
1052 if (era_cnt != -1)
1054 #ifdef _NL_CURRENT
1055 era = _nl_select_era_entry (era_cnt HELPER_LOCALE_ARG);
1056 if (era == NULL)
1057 return NULL;
1058 if (want_era)
1059 tm->tm_year = (era->start_date[0]
1060 + ((tm->tm_year - era->offset)
1061 * era->absolute_direction));
1062 else
1063 /* Era start year assumed. */
1064 tm->tm_year = era->start_date[0];
1065 #endif
1067 else
1068 if (want_era)
1070 /* No era found but we have seen an E modifier. Rectify some
1071 values. */
1072 if (want_century && century == -1 && tm->tm_year < 69)
1073 tm->tm_year += 100;
1076 if (want_xday && !have_wday)
1078 if ( !(have_mon && have_mday) && have_yday)
1080 /* We don't have tm_mon and/or tm_mday, compute them. */
1081 int t_mon = 0;
1082 while (__mon_yday[__isleap(1900 + tm->tm_year)][t_mon] <= tm->tm_yday)
1083 t_mon++;
1084 if (!have_mon)
1085 tm->tm_mon = t_mon - 1;
1086 if (!have_mday)
1087 tm->tm_mday =
1088 (tm->tm_yday
1089 - __mon_yday[__isleap(1900 + tm->tm_year)][t_mon - 1] + 1);
1091 day_of_the_week (tm);
1094 if (want_xday && !have_yday)
1095 day_of_the_year (tm);
1097 if ((have_uweek || have_wweek) && have_wday)
1099 int save_wday = tm->tm_wday;
1100 int save_mday = tm->tm_mday;
1101 int save_mon = tm->tm_mon;
1102 int w_offset = have_uweek ? 0 : 1;
1104 tm->tm_mday = 1;
1105 tm->tm_mon = 0;
1106 day_of_the_week (tm);
1107 if (have_mday)
1108 tm->tm_mday = save_mday;
1109 if (have_mon)
1110 tm->tm_mon = save_mon;
1112 if (!have_yday)
1113 tm->tm_yday = ((7 - (tm->tm_wday - w_offset)) % 7
1114 + (week_no - 1) *7
1115 + save_wday - w_offset);
1117 if (!have_mday || !have_mon)
1119 int t_mon = 0;
1120 while (__mon_yday[__isleap(1900 + tm->tm_year)][t_mon]
1121 <= tm->tm_yday)
1122 t_mon++;
1123 if (!have_mon)
1124 tm->tm_mon = t_mon - 1;
1125 if (!have_mday)
1126 tm->tm_mday =
1127 (tm->tm_yday
1128 - __mon_yday[__isleap(1900 + tm->tm_year)][t_mon - 1] + 1);
1131 tm->tm_wday = save_wday;
1134 return (char *) rp;
1138 char *
1139 strptime (buf, format, tm LOCALE_PARAM)
1140 const char *buf;
1141 const char *format;
1142 struct tm *tm;
1143 LOCALE_PARAM_DECL
1145 enum ptime_locale_status decided;
1147 #ifdef _NL_CURRENT
1148 decided = not;
1149 #else
1150 decided = raw;
1151 #endif
1152 return __strptime_internal (buf, format, tm, &decided, -1 LOCALE_ARG);
1154 #ifdef _LIBC
1155 weak_alias (__strptime_l, strptime_l)
1156 #endif
1157 #endif