1 /* Copyright (C) 2002, 2004, 2005, 2007 Free Software Foundation, Inc.
2 This file is part of the GNU C Library.
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2, or (at your option)
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License along
15 with this program; if not, write to the Free Software Foundation,
16 Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
27 # include <langinfo.h>
34 # include "../locale/localeinfo.h"
38 enum ptime_locale_status { not, loc, raw };
43 #define match_char(ch1, ch2) if (ch1 != ch2) return NULL
44 #if defined _LIBC && defined __GNUC__ && __GNUC__ >= 2
45 # define match_string(cs1, s2) \
46 ({ size_t len = strlen (cs1); \
47 int result = __strncasecmp_l ((cs1), (s2), len, locale) == 0; \
48 if (result) (s2) += len; \
51 /* Oh come on. Get a reasonable compiler. */
52 # define match_string(cs1, s2) \
53 (strncasecmp ((cs1), (s2), strlen (cs1)) ? 0 : ((s2) += strlen (cs1), 1))
55 /* We intentionally do not use isdigit() for testing because this will
56 lead to problems with the wide character version. */
57 #define get_number(from, to, n) \
63 if (*rp < '0' || *rp > '9') \
68 } while (--__n > 0 && val * 10 <= to && *rp >= '0' && *rp <= '9'); \
69 if (val < from || val > to) \
73 # define get_alt_number(from, to, n) \
75 __label__ do_normal; \
77 if (*decided != raw) \
79 val = _nl_parse_alt_digit (&rp HELPER_LOCALE_ARG); \
80 if (val == -1 && *decided != loc) \
85 if (val < from || val > to) \
91 get_number (from, to, n); \
96 # define get_alt_number(from, to, n) \
97 /* We don't have the alternate representation. */ \
98 get_number(from, to, n)
100 #define recursive(new_fmt) \
101 (*(new_fmt) != '\0' \
102 && (rp = __strptime_internal (rp, (new_fmt), tm, \
103 decided, era_cnt LOCALE_ARG)) != NULL)
107 /* This is defined in locale/C-time.c in the GNU libc. */
108 extern const struct locale_data _nl_C_LC_TIME attribute_hidden;
110 # define weekday_name (&_nl_C_LC_TIME.values[_NL_ITEM_INDEX (DAY_1)].string)
111 # define ab_weekday_name \
112 (&_nl_C_LC_TIME.values[_NL_ITEM_INDEX (ABDAY_1)].string)
113 # define month_name (&_nl_C_LC_TIME.values[_NL_ITEM_INDEX (MON_1)].string)
114 # define ab_month_name (&_nl_C_LC_TIME.values[_NL_ITEM_INDEX (ABMON_1)].string)
115 # define HERE_D_T_FMT (_nl_C_LC_TIME.values[_NL_ITEM_INDEX (D_T_FMT)].string)
116 # define HERE_D_FMT (_nl_C_LC_TIME.values[_NL_ITEM_INDEX (D_FMT)].string)
117 # define HERE_AM_STR (_nl_C_LC_TIME.values[_NL_ITEM_INDEX (AM_STR)].string)
118 # define HERE_PM_STR (_nl_C_LC_TIME.values[_NL_ITEM_INDEX (PM_STR)].string)
119 # define HERE_T_FMT_AMPM \
120 (_nl_C_LC_TIME.values[_NL_ITEM_INDEX (T_FMT_AMPM)].string)
121 # define HERE_T_FMT (_nl_C_LC_TIME.values[_NL_ITEM_INDEX (T_FMT)].string)
123 # define strncasecmp(s1, s2, n) __strncasecmp (s1, s2, n)
125 static char const weekday_name[][10] =
127 "Sunday", "Monday", "Tuesday", "Wednesday",
128 "Thursday", "Friday", "Saturday"
130 static char const ab_weekday_name[][4] =
132 "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
134 static char const month_name[][10] =
136 "January", "February", "March", "April", "May", "June",
137 "July", "August", "September", "October", "November", "December"
139 static char const ab_month_name[][4] =
141 "Jan", "Feb", "Mar", "Apr", "May", "Jun",
142 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
144 # define HERE_D_T_FMT "%a %b %e %H:%M:%S %Y"
145 # define HERE_D_FMT "%m/%d/%y"
146 # define HERE_AM_STR "AM"
147 # define HERE_PM_STR "PM"
148 # define HERE_T_FMT_AMPM "%I:%M:%S %p"
149 # define HERE_T_FMT "%H:%M:%S"
151 static const unsigned short int __mon_yday[2][13] =
154 { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 },
156 { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 }
161 /* We use this code also for the extended locale handling where the
162 function gets as an additional argument the locale which has to be
163 used. To access the values we have to redefine the _NL_CURRENT
165 # define strptime __strptime_l
167 # define _NL_CURRENT(category, item) \
168 (current->values[_NL_ITEM_INDEX (item)].string)
169 # undef _NL_CURRENT_WORD
170 # define _NL_CURRENT_WORD(category, item) \
171 (current->values[_NL_ITEM_INDEX (item)].word)
172 # define LOCALE_PARAM , locale
173 # define LOCALE_ARG , locale
174 # define LOCALE_PARAM_PROTO , __locale_t locale
175 # define LOCALE_PARAM_DECL __locale_t locale;
176 # define HELPER_LOCALE_ARG , current
177 # define ISSPACE(Ch) __isspace_l (Ch, locale)
179 # define LOCALE_PARAM
181 # define LOCALE_PARAM_DECL
182 # define LOCALE_PARAM_PROTO
183 # define HELPER_LOCALE_ARG
184 # define ISSPACE(Ch) isspace (Ch)
191 /* Nonzero if YEAR is a leap year (every 4 years,
192 except every 100th isn't, and every 400th is). */
193 # define __isleap(year) \
194 ((year) % 4 == 0 && ((year) % 100 != 0 || (year) % 400 == 0))
197 /* Compute the day of the week. */
199 day_of_the_week (struct tm *tm)
201 /* We know that January 1st 1970 was a Thursday (= 4). Compute the
202 the difference between this data in the one on TM and so determine
204 int corr_year = 1900 + tm->tm_year - (tm->tm_mon < 2);
206 + (365 * (tm->tm_year - 70))
208 - ((corr_year / 4) / 25) + ((corr_year / 4) % 25 < 0)
209 + (((corr_year / 4) / 25) / 4)
210 + __mon_yday[0][tm->tm_mon]
212 tm->tm_wday = ((wday % 7) + 7) % 7;
215 /* Compute the day of the year. */
217 day_of_the_year (struct tm *tm)
219 tm->tm_yday = (__mon_yday[__isleap (1900 + tm->tm_year)][tm->tm_mon]
220 + (tm->tm_mday - 1));
230 __strptime_internal (rp, fmt, tm, decided, era_cnt LOCALE_PARAM)
234 enum ptime_locale_status *decided;
239 struct locale_data *const current = locale->__locales[LC_TIME];
242 const char *rp_backup;
246 int century, want_century;
248 int have_wday, want_xday;
250 int have_mon, have_mday;
251 int have_uweek, have_wweek;
254 struct era_entry *era;
263 have_wday = want_xday = have_yday = have_mon = have_mday = have_uweek = 0;
268 /* A white space in the format string matches 0 more or white
269 space in the input string. */
272 while (ISSPACE (*rp))
278 /* Any character but `%' must be matched by the same character
279 in the iput string. */
282 match_char (*fmt++, *rp++);
288 /* We need this for handling the `E' modifier. */
292 /* Make back up of current processing pointer. */
298 /* Match the `%' character itself. */
299 match_char ('%', *rp++);
303 /* Match day of week. */
304 for (cnt = 0; cnt < 7; ++cnt)
309 if (match_string (_NL_CURRENT (LC_TIME, DAY_1 + cnt), rp))
312 && strcmp (_NL_CURRENT (LC_TIME, DAY_1 + cnt),
317 if (match_string (_NL_CURRENT (LC_TIME, ABDAY_1 + cnt), rp))
320 && strcmp (_NL_CURRENT (LC_TIME, ABDAY_1 + cnt),
321 ab_weekday_name[cnt]))
328 && (match_string (weekday_name[cnt], rp)
329 || match_string (ab_weekday_name[cnt], rp)))
336 /* Does not match a weekday name. */
344 /* Match month name. */
345 for (cnt = 0; cnt < 12; ++cnt)
350 if (match_string (_NL_CURRENT (LC_TIME, MON_1 + cnt), rp))
353 && strcmp (_NL_CURRENT (LC_TIME, MON_1 + cnt),
358 if (match_string (_NL_CURRENT (LC_TIME, ABMON_1 + cnt), rp))
361 && strcmp (_NL_CURRENT (LC_TIME, ABMON_1 + cnt),
368 if (match_string (month_name[cnt], rp)
369 || match_string (ab_month_name[cnt], rp))
376 /* Does not match a month name. */
382 /* Match locale's date and time format. */
386 if (!recursive (_NL_CURRENT (LC_TIME, D_T_FMT)))
395 if (*decided == not &&
396 strcmp (_NL_CURRENT (LC_TIME, D_T_FMT), HERE_D_T_FMT))
404 if (!recursive (HERE_D_T_FMT))
409 /* Match century number. */
411 get_number (0, 99, 2);
417 /* Match day of month. */
418 get_number (1, 31, 2);
424 if (!recursive ("%Y-%m-%d"))
432 if (!recursive (_NL_CURRENT (LC_TIME, D_FMT)))
442 && strcmp (_NL_CURRENT (LC_TIME, D_FMT), HERE_D_FMT))
452 /* Match standard day format. */
453 if (!recursive (HERE_D_FMT))
459 /* Match hour in 24-hour clock. */
460 get_number (0, 23, 2);
465 /* Match hour in 12-hour clock. GNU extension. */
467 /* Match hour in 12-hour clock. */
468 get_number (1, 12, 2);
469 tm->tm_hour = val % 12;
473 /* Match day number of year. */
474 get_number (1, 366, 3);
475 tm->tm_yday = val - 1;
479 /* Match number of month. */
480 get_number (1, 12, 2);
481 tm->tm_mon = val - 1;
487 get_number (0, 59, 2);
492 /* Match any white space. */
493 while (ISSPACE (*rp))
497 /* Match locale's equivalent of AM/PM. */
501 if (match_string (_NL_CURRENT (LC_TIME, AM_STR), rp))
503 if (strcmp (_NL_CURRENT (LC_TIME, AM_STR), HERE_AM_STR))
507 if (match_string (_NL_CURRENT (LC_TIME, PM_STR), rp))
509 if (strcmp (_NL_CURRENT (LC_TIME, PM_STR), HERE_PM_STR))
517 if (!match_string (HERE_AM_STR, rp))
519 if (match_string (HERE_PM_STR, rp))
529 if (!recursive (_NL_CURRENT (LC_TIME, T_FMT_AMPM)))
538 if (*decided == not &&
539 strcmp (_NL_CURRENT (LC_TIME, T_FMT_AMPM),
547 if (!recursive (HERE_T_FMT_AMPM))
551 if (!recursive ("%H:%M"))
556 /* The number of seconds may be very high so we cannot use
557 the `get_number' macro. Instead read the number
558 character for character and construct the result while
561 if (*rp < '0' || *rp > '9')
562 /* We need at least one digit. */
570 while (*rp >= '0' && *rp <= '9');
572 if (localtime_r (&secs, tm) == NULL)
573 /* Error in function. */
578 get_number (0, 61, 2);
585 if (!recursive (_NL_CURRENT (LC_TIME, T_FMT)))
594 if (strcmp (_NL_CURRENT (LC_TIME, T_FMT), HERE_T_FMT))
603 if (!recursive (HERE_T_FMT))
607 get_number (1, 7, 1);
608 tm->tm_wday = val % 7;
612 get_number (0, 99, 2);
613 /* XXX This cannot determine any field in TM. */
616 if (*rp < '0' || *rp > '9')
618 /* XXX Ignore the number since we would need some more
619 information to compute a real date. */
622 while (*rp >= '0' && *rp <= '9');
625 get_number (0, 53, 2);
630 get_number (0, 53, 2);
635 get_number (0, 53, 2);
636 /* XXX This cannot determine any field in TM without some
640 /* Match number of weekday. */
641 get_number (0, 6, 1);
646 match_year_in_century:
647 /* Match year within century. */
648 get_number (0, 99, 2);
649 /* The "Year 2000: The Millennium Rollover" paper suggests that
650 values in the range 69-99 refer to the twentieth century. */
651 tm->tm_year = val >= 69 ? val : val + 100;
652 /* Indicate that we want to use the century, if specified. */
657 /* Match year including century number. */
658 get_number (0, 9999, 4);
659 tm->tm_year = val - 1900;
664 /* XXX How to handle this? */
667 /* We recognize two formats: if two digits are given, these
668 specify hours. If fours digits are used, minutes are
677 if (*rp != '+' && *rp != '-')
681 while (n < 4 && *rp >= '0' && *rp <= '9')
683 val = val * 10 + *rp++ - '0';
689 /* Only two or four digits recognized. */
693 /* We have to convert the minutes into decimal. */
696 val = (val / 100) * 100 + ((val % 100) * 50) / 30;
700 #if defined _LIBC || HAVE_TM_GMTOFF
701 tm->tm_gmtoff = (val * 3600) / 100;
703 tm->tm_gmtoff = -tm->tm_gmtoff;
712 /* Match locale's alternate date and time format. */
715 const char *fmt = _NL_CURRENT (LC_TIME, ERA_D_T_FMT);
718 fmt = _NL_CURRENT (LC_TIME, D_T_FMT);
720 if (!recursive (fmt))
729 if (strcmp (fmt, HERE_D_T_FMT))
736 /* The C locale has no era information, so use the
737 normal representation. */
738 if (!recursive (HERE_D_T_FMT))
747 era = _nl_select_era_entry (era_cnt HELPER_LOCALE_ARG);
748 if (era != NULL && match_string (era->era_name, rp))
757 num_eras = _NL_CURRENT_WORD (LC_TIME,
758 _NL_TIME_ERA_NUM_ENTRIES);
759 for (era_cnt = 0; era_cnt < (int) num_eras;
760 ++era_cnt, rp = rp_backup)
762 era = _nl_select_era_entry (era_cnt
764 if (era != NULL && match_string (era->era_name, rp))
770 if (era_cnt != (int) num_eras)
779 /* The C locale has no era information, so use the
780 normal representation. */
785 get_number(0, 9999, 4);
793 assert (*decided == loc);
795 era = _nl_select_era_entry (era_cnt HELPER_LOCALE_ARG);
799 int delta = ((tm->tm_year - era->offset)
800 * era->absolute_direction);
802 && delta < (((int64_t) era->stop_date[0]
803 - (int64_t) era->start_date[0])
804 * era->absolute_direction));
812 num_eras = _NL_CURRENT_WORD (LC_TIME,
813 _NL_TIME_ERA_NUM_ENTRIES);
814 for (era_cnt = 0; era_cnt < (int) num_eras; ++era_cnt)
816 era = _nl_select_era_entry (era_cnt
820 int delta = ((tm->tm_year - era->offset)
821 * era->absolute_direction);
823 && delta < (((int64_t) era->stop_date[0]
824 - (int64_t) era->start_date[0])
825 * era->absolute_direction))
832 if (era_cnt != (int) num_eras)
842 goto match_year_in_century;
846 num_eras = _NL_CURRENT_WORD (LC_TIME,
847 _NL_TIME_ERA_NUM_ENTRIES);
848 for (era_cnt = 0; era_cnt < (int) num_eras;
849 ++era_cnt, rp = rp_backup)
851 era = _nl_select_era_entry (era_cnt HELPER_LOCALE_ARG);
852 if (era != NULL && recursive (era->era_format))
855 if (era_cnt == (int) num_eras)
872 get_number (0, 9999, 4);
873 tm->tm_year = val - 1900;
880 const char *fmt = _NL_CURRENT (LC_TIME, ERA_D_FMT);
883 fmt = _NL_CURRENT (LC_TIME, D_FMT);
885 if (!recursive (fmt))
894 if (strcmp (fmt, HERE_D_FMT))
900 if (!recursive (HERE_D_FMT))
906 const char *fmt = _NL_CURRENT (LC_TIME, ERA_T_FMT);
909 fmt = _NL_CURRENT (LC_TIME, T_FMT);
911 if (!recursive (fmt))
920 if (strcmp (fmt, HERE_T_FMT))
926 if (!recursive (HERE_T_FMT))
934 /* We have no information about the era format. Just use
935 the normal format. */
936 if (*fmt != 'c' && *fmt != 'C' && *fmt != 'y' && *fmt != 'Y'
937 && *fmt != 'x' && *fmt != 'X')
938 /* This is an illegal format. */
948 /* Match day of month using alternate numeric symbols. */
949 get_alt_number (1, 31, 2);
955 /* Match hour in 24-hour clock using alternate numeric
957 get_alt_number (0, 23, 2);
962 /* Match hour in 12-hour clock using alternate numeric
964 get_alt_number (1, 12, 2);
965 tm->tm_hour = val % 12;
969 /* Match month using alternate numeric symbols. */
970 get_alt_number (1, 12, 2);
971 tm->tm_mon = val - 1;
976 /* Match minutes using alternate numeric symbols. */
977 get_alt_number (0, 59, 2);
981 /* Match seconds using alternate numeric symbols. */
982 get_alt_number (0, 61, 2);
986 get_alt_number (0, 53, 2);
991 get_alt_number (0, 53, 2);
996 get_alt_number (0, 53, 2);
997 /* XXX This cannot determine any field in TM without
998 further information. */
1001 /* Match number of weekday using alternate numeric symbols. */
1002 get_alt_number (0, 6, 1);
1007 /* Match year within century using alternate numeric symbols. */
1008 get_alt_number (0, 99, 2);
1009 tm->tm_year = val >= 69 ? val : val + 100;
1021 if (have_I && is_pm)
1027 tm->tm_year = tm->tm_year % 100 + (century - 19) * 100;
1029 /* Only the century, but not the year. Strange, but so be it. */
1030 tm->tm_year = (century - 19) * 100;
1036 era = _nl_select_era_entry (era_cnt HELPER_LOCALE_ARG);
1040 tm->tm_year = (era->start_date[0]
1041 + ((tm->tm_year - era->offset)
1042 * era->absolute_direction));
1044 /* Era start year assumed. */
1045 tm->tm_year = era->start_date[0];
1051 /* No era found but we have seen an E modifier. Rectify some
1053 if (want_century && century == -1 && tm->tm_year < 69)
1057 if (want_xday && !have_wday)
1059 if ( !(have_mon && have_mday) && have_yday)
1061 /* We don't have tm_mon and/or tm_mday, compute them. */
1063 while (__mon_yday[__isleap(1900 + tm->tm_year)][t_mon] <= tm->tm_yday)
1066 tm->tm_mon = t_mon - 1;
1070 - __mon_yday[__isleap(1900 + tm->tm_year)][t_mon - 1] + 1);
1072 day_of_the_week (tm);
1075 if (want_xday && !have_yday)
1076 day_of_the_year (tm);
1078 if ((have_uweek || have_wweek) && have_wday)
1080 int save_wday = tm->tm_wday;
1081 int save_mday = tm->tm_mday;
1082 int save_mon = tm->tm_mon;
1083 int w_offset = have_uweek ? 0 : 1;
1087 day_of_the_week (tm);
1089 tm->tm_mday = save_mday;
1091 tm->tm_mon = save_mon;
1094 tm->tm_yday = ((7 - (tm->tm_wday - w_offset)) % 7
1096 + save_wday - w_offset);
1098 if (!have_mday || !have_mon)
1101 while (__mon_yday[__isleap(1900 + tm->tm_year)][t_mon]
1105 tm->tm_mon = t_mon - 1;
1109 - __mon_yday[__isleap(1900 + tm->tm_year)][t_mon - 1] + 1);
1112 tm->tm_wday = save_wday;
1120 strptime (buf, format, tm LOCALE_PARAM)
1121 const char *restrict buf;
1122 const char *restrict format;
1123 struct tm *restrict tm;
1126 enum ptime_locale_status decided;
1133 return __strptime_internal (buf, format, tm, &decided, -1 LOCALE_ARG);
1137 weak_alias (__strptime_l, strptime_l)