1 /* Copyright (C) 2002, 2004-2005, 2007, 2009-2011 Free Software Foundation,
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)
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. */
28 # include <langinfo.h>
35 # include "../locale/localeinfo.h"
39 enum ptime_locale_status { not, loc, raw };
44 #define match_char(ch1, ch2) if (ch1 != ch2) return NULL
45 #if defined _LIBC && defined __GNUC__ && __GNUC__ >= 2
46 # define match_string(cs1, s2) \
47 ({ size_t len = strlen (cs1); \
48 int result = __strncasecmp_l ((cs1), (s2), len, locale) == 0; \
49 if (result) (s2) += len; \
52 /* Oh come on. Get a reasonable compiler. */
53 # define match_string(cs1, s2) \
54 (strncasecmp ((cs1), (s2), strlen (cs1)) ? 0 : ((s2) += strlen (cs1), 1))
56 /* We intentionally do not use isdigit() for testing because this will
57 lead to problems with the wide character version. */
58 #define get_number(from, to, n) \
64 if (*rp < '0' || *rp > '9') \
69 } while (--__n > 0 && val * 10 <= to && *rp >= '0' && *rp <= '9'); \
70 if (val < from || val > to) \
74 # define get_alt_number(from, to, n) \
76 __label__ do_normal; \
78 if (*decided != raw) \
80 val = _nl_parse_alt_digit (&rp HELPER_LOCALE_ARG); \
81 if (val == -1 && *decided != loc) \
86 if (val < from || val > to) \
92 get_number (from, to, n); \
97 # define get_alt_number(from, to, n) \
98 /* We don't have the alternate representation. */ \
99 get_number(from, to, n)
101 #define recursive(new_fmt) \
102 (*(new_fmt) != '\0' \
103 && (rp = __strptime_internal (rp, (new_fmt), tm, \
104 decided, era_cnt LOCALE_ARG)) != NULL)
108 /* This is defined in locale/C-time.c in the GNU libc. */
109 extern const struct locale_data _nl_C_LC_TIME attribute_hidden;
111 # define weekday_name (&_nl_C_LC_TIME.values[_NL_ITEM_INDEX (DAY_1)].string)
112 # define ab_weekday_name \
113 (&_nl_C_LC_TIME.values[_NL_ITEM_INDEX (ABDAY_1)].string)
114 # define month_name (&_nl_C_LC_TIME.values[_NL_ITEM_INDEX (MON_1)].string)
115 # define ab_month_name (&_nl_C_LC_TIME.values[_NL_ITEM_INDEX (ABMON_1)].string)
116 # define HERE_D_T_FMT (_nl_C_LC_TIME.values[_NL_ITEM_INDEX (D_T_FMT)].string)
117 # define HERE_D_FMT (_nl_C_LC_TIME.values[_NL_ITEM_INDEX (D_FMT)].string)
118 # define HERE_AM_STR (_nl_C_LC_TIME.values[_NL_ITEM_INDEX (AM_STR)].string)
119 # define HERE_PM_STR (_nl_C_LC_TIME.values[_NL_ITEM_INDEX (PM_STR)].string)
120 # define HERE_T_FMT_AMPM \
121 (_nl_C_LC_TIME.values[_NL_ITEM_INDEX (T_FMT_AMPM)].string)
122 # define HERE_T_FMT (_nl_C_LC_TIME.values[_NL_ITEM_INDEX (T_FMT)].string)
124 # define strncasecmp(s1, s2, n) __strncasecmp (s1, s2, n)
126 static char const weekday_name[][10] =
128 "Sunday", "Monday", "Tuesday", "Wednesday",
129 "Thursday", "Friday", "Saturday"
131 static char const ab_weekday_name[][4] =
133 "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
135 static char const month_name[][10] =
137 "January", "February", "March", "April", "May", "June",
138 "July", "August", "September", "October", "November", "December"
140 static char const ab_month_name[][4] =
142 "Jan", "Feb", "Mar", "Apr", "May", "Jun",
143 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
145 # define HERE_D_T_FMT "%a %b %e %H:%M:%S %Y"
146 # define HERE_D_FMT "%m/%d/%y"
147 # define HERE_AM_STR "AM"
148 # define HERE_PM_STR "PM"
149 # define HERE_T_FMT_AMPM "%I:%M:%S %p"
150 # define HERE_T_FMT "%H:%M:%S"
152 static const unsigned short int __mon_yday[2][13] =
155 { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 },
157 { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 }
162 /* We use this code also for the extended locale handling where the
163 function gets as an additional argument the locale which has to be
164 used. To access the values we have to redefine the _NL_CURRENT
166 # define strptime __strptime_l
168 # define _NL_CURRENT(category, item) \
169 (current->values[_NL_ITEM_INDEX (item)].string)
170 # undef _NL_CURRENT_WORD
171 # define _NL_CURRENT_WORD(category, item) \
172 (current->values[_NL_ITEM_INDEX (item)].word)
173 # define LOCALE_PARAM , locale
174 # define LOCALE_ARG , locale
175 # define LOCALE_PARAM_PROTO , __locale_t locale
176 # define LOCALE_PARAM_DECL __locale_t locale;
177 # define HELPER_LOCALE_ARG , current
178 # define ISSPACE(Ch) __isspace_l (Ch, locale)
180 # define LOCALE_PARAM
182 # define LOCALE_PARAM_DECL
183 # define LOCALE_PARAM_PROTO
184 # define HELPER_LOCALE_ARG
185 # define ISSPACE(Ch) isspace (Ch)
192 /* Nonzero if YEAR is a leap year (every 4 years,
193 except every 100th isn't, and every 400th is). */
194 # define __isleap(year) \
195 ((year) % 4 == 0 && ((year) % 100 != 0 || (year) % 400 == 0))
198 /* Compute the day of the week. */
200 day_of_the_week (struct tm *tm)
202 /* We know that January 1st 1970 was a Thursday (= 4). Compute the
203 difference between this data in the one on TM and so determine
205 int corr_year = 1900 + tm->tm_year - (tm->tm_mon < 2);
207 + (365 * (tm->tm_year - 70))
209 - ((corr_year / 4) / 25) + ((corr_year / 4) % 25 < 0)
210 + (((corr_year / 4) / 25) / 4)
211 + __mon_yday[0][tm->tm_mon]
213 tm->tm_wday = ((wday % 7) + 7) % 7;
216 /* Compute the day of the year. */
218 day_of_the_year (struct tm *tm)
220 tm->tm_yday = (__mon_yday[__isleap (1900 + tm->tm_year)][tm->tm_mon]
221 + (tm->tm_mday - 1));
231 __strptime_internal (rp, fmt, tm, decided, era_cnt LOCALE_PARAM)
235 enum ptime_locale_status *decided;
240 struct locale_data *const current = locale->__locales[LC_TIME];
243 const char *rp_backup;
247 int century, want_century;
249 int have_wday, want_xday;
251 int have_mon, have_mday;
252 int have_uweek, have_wweek;
257 struct era_entry *era;
266 have_wday = want_xday = have_yday = have_mon = have_mday = have_uweek = 0;
271 /* A white space in the format string matches 0 more or white
272 space in the input string. */
275 while (ISSPACE (*rp))
281 /* Any character but `%' must be matched by the same character
282 in the iput string. */
285 match_char (*fmt++, *rp++);
291 /* We need this for handling the `E' modifier. */
295 /* Make back up of current processing pointer. */
301 /* Match the `%' character itself. */
302 match_char ('%', *rp++);
306 /* Match day of week. */
307 for (cnt = 0; cnt < 7; ++cnt)
312 if (match_string (_NL_CURRENT (LC_TIME, DAY_1 + cnt), rp))
315 && strcmp (_NL_CURRENT (LC_TIME, DAY_1 + cnt),
320 if (match_string (_NL_CURRENT (LC_TIME, ABDAY_1 + cnt), rp))
323 && strcmp (_NL_CURRENT (LC_TIME, ABDAY_1 + cnt),
324 ab_weekday_name[cnt]))
331 && (match_string (weekday_name[cnt], rp)
332 || match_string (ab_weekday_name[cnt], rp)))
339 /* Does not match a weekday name. */
347 /* Match month name. */
348 for (cnt = 0; cnt < 12; ++cnt)
353 if (match_string (_NL_CURRENT (LC_TIME, MON_1 + cnt), rp))
356 && strcmp (_NL_CURRENT (LC_TIME, MON_1 + cnt),
361 if (match_string (_NL_CURRENT (LC_TIME, ABMON_1 + cnt), rp))
364 && strcmp (_NL_CURRENT (LC_TIME, ABMON_1 + cnt),
371 if (match_string (month_name[cnt], rp)
372 || match_string (ab_month_name[cnt], rp))
379 /* Does not match a month name. */
385 /* Match locale's date and time format. */
389 if (!recursive (_NL_CURRENT (LC_TIME, D_T_FMT)))
398 if (*decided == not &&
399 strcmp (_NL_CURRENT (LC_TIME, D_T_FMT), HERE_D_T_FMT))
407 if (!recursive (HERE_D_T_FMT))
412 /* Match century number. */
416 get_number (0, 99, 2);
422 /* Match day of month. */
423 get_number (1, 31, 2);
429 if (!recursive ("%Y-%m-%d"))
437 if (!recursive (_NL_CURRENT (LC_TIME, D_FMT)))
447 && strcmp (_NL_CURRENT (LC_TIME, D_FMT), HERE_D_FMT))
457 /* Match standard day format. */
458 if (!recursive (HERE_D_FMT))
464 /* Match hour in 24-hour clock. */
465 get_number (0, 23, 2);
470 /* Match hour in 12-hour clock. GNU extension. */
472 /* Match hour in 12-hour clock. */
473 get_number (1, 12, 2);
474 tm->tm_hour = val % 12;
478 /* Match day number of year. */
479 get_number (1, 366, 3);
480 tm->tm_yday = val - 1;
484 /* Match number of month. */
485 get_number (1, 12, 2);
486 tm->tm_mon = val - 1;
492 get_number (0, 59, 2);
497 /* Match any white space. */
498 while (ISSPACE (*rp))
502 /* Match locale's equivalent of AM/PM. */
506 if (match_string (_NL_CURRENT (LC_TIME, AM_STR), rp))
508 if (strcmp (_NL_CURRENT (LC_TIME, AM_STR), HERE_AM_STR))
512 if (match_string (_NL_CURRENT (LC_TIME, PM_STR), rp))
514 if (strcmp (_NL_CURRENT (LC_TIME, PM_STR), HERE_PM_STR))
522 if (!match_string (HERE_AM_STR, rp))
524 if (match_string (HERE_PM_STR, rp))
534 if (!recursive (_NL_CURRENT (LC_TIME, T_FMT_AMPM)))
543 if (*decided == not &&
544 strcmp (_NL_CURRENT (LC_TIME, T_FMT_AMPM),
552 if (!recursive (HERE_T_FMT_AMPM))
556 if (!recursive ("%H:%M"))
561 /* The number of seconds may be very high so we cannot use
562 the `get_number' macro. Instead read the number
563 character for character and construct the result while
566 if (*rp < '0' || *rp > '9')
567 /* We need at least one digit. */
575 while (*rp >= '0' && *rp <= '9');
577 if (localtime_r (&secs, tm) == NULL)
578 /* Error in function. */
583 get_number (0, 61, 2);
590 if (!recursive (_NL_CURRENT (LC_TIME, T_FMT)))
599 if (strcmp (_NL_CURRENT (LC_TIME, T_FMT), HERE_T_FMT))
608 if (!recursive (HERE_T_FMT))
612 get_number (1, 7, 1);
613 tm->tm_wday = val % 7;
617 get_number (0, 99, 2);
618 /* XXX This cannot determine any field in TM. */
621 if (*rp < '0' || *rp > '9')
623 /* XXX Ignore the number since we would need some more
624 information to compute a real date. */
627 while (*rp >= '0' && *rp <= '9');
630 get_number (0, 53, 2);
635 get_number (0, 53, 2);
640 get_number (0, 53, 2);
641 /* XXX This cannot determine any field in TM without some
645 /* Match number of weekday. */
646 get_number (0, 6, 1);
652 match_year_in_century:
654 /* Match year within century. */
655 get_number (0, 99, 2);
656 /* The "Year 2000: The Millennium Rollover" paper suggests that
657 values in the range 69-99 refer to the twentieth century. */
658 tm->tm_year = val >= 69 ? val : val + 100;
659 /* Indicate that we want to use the century, if specified. */
664 /* Match year including century number. */
665 get_number (0, 9999, 4);
666 tm->tm_year = val - 1900;
671 /* XXX How to handle this? */
674 /* We recognize two formats: if two digits are given, these
675 specify hours. If fours digits are used, minutes are
684 if (*rp != '+' && *rp != '-')
688 while (n < 4 && *rp >= '0' && *rp <= '9')
690 val = val * 10 + *rp++ - '0';
696 /* Only two or four digits recognized. */
700 /* We have to convert the minutes into decimal. */
703 val = (val / 100) * 100 + ((val % 100) * 50) / 30;
707 #if defined _LIBC || HAVE_TM_GMTOFF
708 tm->tm_gmtoff = (val * 3600) / 100;
710 tm->tm_gmtoff = -tm->tm_gmtoff;
719 /* Match locale's alternate date and time format. */
722 const char *fmt = _NL_CURRENT (LC_TIME, ERA_D_T_FMT);
725 fmt = _NL_CURRENT (LC_TIME, D_T_FMT);
727 if (!recursive (fmt))
736 if (strcmp (fmt, HERE_D_T_FMT))
743 /* The C locale has no era information, so use the
744 normal representation. */
745 if (!recursive (HERE_D_T_FMT))
754 era = _nl_select_era_entry (era_cnt HELPER_LOCALE_ARG);
755 if (era != NULL && match_string (era->era_name, rp))
764 num_eras = _NL_CURRENT_WORD (LC_TIME,
765 _NL_TIME_ERA_NUM_ENTRIES);
766 for (era_cnt = 0; era_cnt < (int) num_eras;
767 ++era_cnt, rp = rp_backup)
769 era = _nl_select_era_entry (era_cnt
771 if (era != NULL && match_string (era->era_name, rp))
777 if (era_cnt != (int) num_eras)
786 /* The C locale has no era information, so use the
787 normal representation. */
792 get_number(0, 9999, 4);
800 assert (*decided == loc);
802 era = _nl_select_era_entry (era_cnt HELPER_LOCALE_ARG);
806 int delta = ((tm->tm_year - era->offset)
807 * era->absolute_direction);
809 && delta < (((int64_t) era->stop_date[0]
810 - (int64_t) era->start_date[0])
811 * era->absolute_direction));
819 num_eras = _NL_CURRENT_WORD (LC_TIME,
820 _NL_TIME_ERA_NUM_ENTRIES);
821 for (era_cnt = 0; era_cnt < (int) num_eras; ++era_cnt)
823 era = _nl_select_era_entry (era_cnt
827 int delta = ((tm->tm_year - era->offset)
828 * era->absolute_direction);
830 && delta < (((int64_t) era->stop_date[0]
831 - (int64_t) era->start_date[0])
832 * era->absolute_direction))
839 if (era_cnt != (int) num_eras)
849 goto match_year_in_century;
853 num_eras = _NL_CURRENT_WORD (LC_TIME,
854 _NL_TIME_ERA_NUM_ENTRIES);
855 for (era_cnt = 0; era_cnt < (int) num_eras;
856 ++era_cnt, rp = rp_backup)
858 era = _nl_select_era_entry (era_cnt HELPER_LOCALE_ARG);
859 if (era != NULL && recursive (era->era_format))
862 if (era_cnt == (int) num_eras)
879 get_number (0, 9999, 4);
880 tm->tm_year = val - 1900;
887 const char *fmt = _NL_CURRENT (LC_TIME, ERA_D_FMT);
890 fmt = _NL_CURRENT (LC_TIME, D_FMT);
892 if (!recursive (fmt))
901 if (strcmp (fmt, HERE_D_FMT))
907 if (!recursive (HERE_D_FMT))
913 const char *fmt = _NL_CURRENT (LC_TIME, ERA_T_FMT);
916 fmt = _NL_CURRENT (LC_TIME, T_FMT);
918 if (!recursive (fmt))
927 if (strcmp (fmt, HERE_T_FMT))
933 if (!recursive (HERE_T_FMT))
941 /* We have no information about the era format. Just use
942 the normal format. */
943 if (*fmt != 'c' && *fmt != 'C' && *fmt != 'y' && *fmt != 'Y'
944 && *fmt != 'x' && *fmt != 'X')
945 /* This is an illegal format. */
955 /* Match day of month using alternate numeric symbols. */
956 get_alt_number (1, 31, 2);
962 /* Match hour in 24-hour clock using alternate numeric
964 get_alt_number (0, 23, 2);
969 /* Match hour in 12-hour clock using alternate numeric
971 get_alt_number (1, 12, 2);
972 tm->tm_hour = val % 12;
976 /* Match month using alternate numeric symbols. */
977 get_alt_number (1, 12, 2);
978 tm->tm_mon = val - 1;
983 /* Match minutes using alternate numeric symbols. */
984 get_alt_number (0, 59, 2);
988 /* Match seconds using alternate numeric symbols. */
989 get_alt_number (0, 61, 2);
993 get_alt_number (0, 53, 2);
998 get_alt_number (0, 53, 2);
1003 get_alt_number (0, 53, 2);
1004 /* XXX This cannot determine any field in TM without
1005 further information. */
1008 /* Match number of weekday using alternate numeric symbols. */
1009 get_alt_number (0, 6, 1);
1014 /* Match year within century using alternate numeric symbols. */
1015 get_alt_number (0, 99, 2);
1016 tm->tm_year = val >= 69 ? val : val + 100;
1028 if (have_I && is_pm)
1034 tm->tm_year = tm->tm_year % 100 + (century - 19) * 100;
1036 /* Only the century, but not the year. Strange, but so be it. */
1037 tm->tm_year = (century - 19) * 100;
1043 era = _nl_select_era_entry (era_cnt HELPER_LOCALE_ARG);
1047 tm->tm_year = (era->start_date[0]
1048 + ((tm->tm_year - era->offset)
1049 * era->absolute_direction));
1051 /* Era start year assumed. */
1052 tm->tm_year = era->start_date[0];
1058 /* No era found but we have seen an E modifier. Rectify some
1060 if (want_century && century == -1 && tm->tm_year < 69)
1064 if (want_xday && !have_wday)
1066 if ( !(have_mon && have_mday) && have_yday)
1068 /* We don't have tm_mon and/or tm_mday, compute them. */
1070 while (__mon_yday[__isleap(1900 + tm->tm_year)][t_mon] <= tm->tm_yday)
1073 tm->tm_mon = t_mon - 1;
1077 - __mon_yday[__isleap(1900 + tm->tm_year)][t_mon - 1] + 1);
1079 day_of_the_week (tm);
1082 if (want_xday && !have_yday)
1083 day_of_the_year (tm);
1085 if ((have_uweek || have_wweek) && have_wday)
1087 int save_wday = tm->tm_wday;
1088 int save_mday = tm->tm_mday;
1089 int save_mon = tm->tm_mon;
1090 int w_offset = have_uweek ? 0 : 1;
1094 day_of_the_week (tm);
1096 tm->tm_mday = save_mday;
1098 tm->tm_mon = save_mon;
1101 tm->tm_yday = ((7 - (tm->tm_wday - w_offset)) % 7
1103 + save_wday - w_offset);
1105 if (!have_mday || !have_mon)
1108 while (__mon_yday[__isleap(1900 + tm->tm_year)][t_mon]
1112 tm->tm_mon = t_mon - 1;
1116 - __mon_yday[__isleap(1900 + tm->tm_year)][t_mon - 1] + 1);
1119 tm->tm_wday = save_wday;
1127 strptime (buf, format, tm LOCALE_PARAM)
1128 const char *restrict buf;
1129 const char *restrict format;
1130 struct tm *restrict tm;
1133 enum ptime_locale_status decided;
1140 return __strptime_internal (buf, format, tm, &decided, -1 LOCALE_ARG);
1144 weak_alias (__strptime_l, strptime_l)