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
674 if (*rp != '+' && *rp != '-')
676 bool neg = *rp++ == '-';
678 while (n < 4 && *rp >= '0' && *rp <= '9')
680 val = val * 10 + *rp++ - '0';
686 /* Only two or four digits recognized. */
690 /* We have to convert the minutes into decimal. */
693 val = (val / 100) * 100 + ((val % 100) * 50) / 30;
697 #if defined _LIBC || HAVE_TM_GMTOFF
698 tm->tm_gmtoff = (val * 3600) / 100;
700 tm->tm_gmtoff = -tm->tm_gmtoff;
709 /* Match locale's alternate date and time format. */
712 const char *fmt = _NL_CURRENT (LC_TIME, ERA_D_T_FMT);
715 fmt = _NL_CURRENT (LC_TIME, D_T_FMT);
717 if (!recursive (fmt))
726 if (strcmp (fmt, HERE_D_T_FMT))
733 /* The C locale has no era information, so use the
734 normal representation. */
735 if (!recursive (HERE_D_T_FMT))
744 era = _nl_select_era_entry (era_cnt HELPER_LOCALE_ARG);
745 if (era != NULL && match_string (era->era_name, rp))
754 num_eras = _NL_CURRENT_WORD (LC_TIME,
755 _NL_TIME_ERA_NUM_ENTRIES);
756 for (era_cnt = 0; era_cnt < (int) num_eras;
757 ++era_cnt, rp = rp_backup)
759 era = _nl_select_era_entry (era_cnt
761 if (era != NULL && match_string (era->era_name, rp))
767 if (era_cnt != (int) num_eras)
776 /* The C locale has no era information, so use the
777 normal representation. */
782 get_number(0, 9999, 4);
790 assert (*decided == loc);
792 era = _nl_select_era_entry (era_cnt HELPER_LOCALE_ARG);
796 int delta = ((tm->tm_year - era->offset)
797 * era->absolute_direction);
799 && delta < (((int64_t) era->stop_date[0]
800 - (int64_t) era->start_date[0])
801 * era->absolute_direction));
809 num_eras = _NL_CURRENT_WORD (LC_TIME,
810 _NL_TIME_ERA_NUM_ENTRIES);
811 for (era_cnt = 0; era_cnt < (int) num_eras; ++era_cnt)
813 era = _nl_select_era_entry (era_cnt
817 int delta = ((tm->tm_year - era->offset)
818 * era->absolute_direction);
820 && delta < (((int64_t) era->stop_date[0]
821 - (int64_t) era->start_date[0])
822 * era->absolute_direction))
829 if (era_cnt != (int) num_eras)
839 goto match_year_in_century;
843 num_eras = _NL_CURRENT_WORD (LC_TIME,
844 _NL_TIME_ERA_NUM_ENTRIES);
845 for (era_cnt = 0; era_cnt < (int) num_eras;
846 ++era_cnt, rp = rp_backup)
848 era = _nl_select_era_entry (era_cnt HELPER_LOCALE_ARG);
849 if (era != NULL && recursive (era->era_format))
852 if (era_cnt == (int) num_eras)
869 get_number (0, 9999, 4);
870 tm->tm_year = val - 1900;
877 const char *fmt = _NL_CURRENT (LC_TIME, ERA_D_FMT);
880 fmt = _NL_CURRENT (LC_TIME, D_FMT);
882 if (!recursive (fmt))
891 if (strcmp (fmt, HERE_D_FMT))
897 if (!recursive (HERE_D_FMT))
903 const char *fmt = _NL_CURRENT (LC_TIME, ERA_T_FMT);
906 fmt = _NL_CURRENT (LC_TIME, T_FMT);
908 if (!recursive (fmt))
917 if (strcmp (fmt, HERE_T_FMT))
923 if (!recursive (HERE_T_FMT))
931 /* We have no information about the era format. Just use
932 the normal format. */
933 if (*fmt != 'c' && *fmt != 'C' && *fmt != 'y' && *fmt != 'Y'
934 && *fmt != 'x' && *fmt != 'X')
935 /* This is an illegal format. */
945 /* Match day of month using alternate numeric symbols. */
946 get_alt_number (1, 31, 2);
952 /* Match hour in 24-hour clock using alternate numeric
954 get_alt_number (0, 23, 2);
959 /* Match hour in 12-hour clock using alternate numeric
961 get_alt_number (1, 12, 2);
962 tm->tm_hour = val % 12;
966 /* Match month using alternate numeric symbols. */
967 get_alt_number (1, 12, 2);
968 tm->tm_mon = val - 1;
973 /* Match minutes using alternate numeric symbols. */
974 get_alt_number (0, 59, 2);
978 /* Match seconds using alternate numeric symbols. */
979 get_alt_number (0, 61, 2);
983 get_alt_number (0, 53, 2);
988 get_alt_number (0, 53, 2);
993 get_alt_number (0, 53, 2);
994 /* XXX This cannot determine any field in TM without
995 further information. */
998 /* Match number of weekday using alternate numeric symbols. */
999 get_alt_number (0, 6, 1);
1004 /* Match year within century using alternate numeric symbols. */
1005 get_alt_number (0, 99, 2);
1006 tm->tm_year = val >= 69 ? val : val + 100;
1018 if (have_I && is_pm)
1024 tm->tm_year = tm->tm_year % 100 + (century - 19) * 100;
1026 /* Only the century, but not the year. Strange, but so be it. */
1027 tm->tm_year = (century - 19) * 100;
1033 era = _nl_select_era_entry (era_cnt HELPER_LOCALE_ARG);
1037 tm->tm_year = (era->start_date[0]
1038 + ((tm->tm_year - era->offset)
1039 * era->absolute_direction));
1041 /* Era start year assumed. */
1042 tm->tm_year = era->start_date[0];
1048 /* No era found but we have seen an E modifier. Rectify some
1050 if (want_century && century == -1 && tm->tm_year < 69)
1054 if (want_xday && !have_wday)
1056 if ( !(have_mon && have_mday) && have_yday)
1058 /* We don't have tm_mon and/or tm_mday, compute them. */
1060 while (__mon_yday[__isleap(1900 + tm->tm_year)][t_mon] <= tm->tm_yday)
1063 tm->tm_mon = t_mon - 1;
1067 - __mon_yday[__isleap(1900 + tm->tm_year)][t_mon - 1] + 1);
1069 day_of_the_week (tm);
1072 if (want_xday && !have_yday)
1073 day_of_the_year (tm);
1075 if ((have_uweek || have_wweek) && have_wday)
1077 int save_wday = tm->tm_wday;
1078 int save_mday = tm->tm_mday;
1079 int save_mon = tm->tm_mon;
1080 int w_offset = have_uweek ? 0 : 1;
1084 day_of_the_week (tm);
1086 tm->tm_mday = save_mday;
1088 tm->tm_mon = save_mon;
1091 tm->tm_yday = ((7 - (tm->tm_wday - w_offset)) % 7
1093 + save_wday - w_offset);
1095 if (!have_mday || !have_mon)
1098 while (__mon_yday[__isleap(1900 + tm->tm_year)][t_mon]
1102 tm->tm_mon = t_mon - 1;
1106 - __mon_yday[__isleap(1900 + tm->tm_year)][t_mon - 1] + 1);
1109 tm->tm_wday = save_wday;
1117 strptime (buf, format, tm LOCALE_PARAM)
1118 const char *restrict buf;
1119 const char *restrict format;
1120 struct tm *restrict tm;
1123 enum ptime_locale_status decided;
1130 return __strptime_internal (buf, format, tm, &decided, -1 LOCALE_ARG);
1134 weak_alias (__strptime_l, strptime_l)