1 /* Convert a string representation of time to a time value.
2 Copyright (C) 1996-2000, 2001, 2002 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 Lesser General Public
8 License as published by the Free Software Foundation; either
9 version 2.1 of the 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 Lesser General Public License for more details.
16 You should have received a copy of the GNU Lesser General Public
17 License along with the GNU C Library; if not, write to the Free
18 Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
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. */
37 # include "../locale/localeinfo.h"
42 # if defined __GNUC__ || (defined __STDC__ && __STDC__)
43 # define __P(args) args
50 #if ! HAVE_LOCALTIME_R && ! defined localtime_r
52 # define localtime_r __localtime_r
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
*));
62 struct tm
*l
= localtime (t
);
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; \
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))
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) \
92 if (*rp < '0' || *rp > '9') \
97 } while (--__n > 0 && val * 10 <= to && *rp >= '0' && *rp <= '9'); \
98 if (val < from || val > to) \
102 # define get_alt_number(from, to, n) \
104 __label__ do_normal; \
106 if (*decided != raw) \
108 val = _nl_parse_alt_digit (&rp); \
109 if (val == -1 && *decided != loc) \
114 if (val < from || val > to) \
120 get_number (from, to, n); \
125 # define get_alt_number(from, to, n) \
126 /* We don't have the alternate representation. */ \
127 get_number(from, to, n)
129 #define recursive(new_fmt) \
130 (*(new_fmt) != '\0' \
131 && (rp = strptime_internal (rp, (new_fmt), tm, \
132 decided, era_cnt LOCALE_ARG)) != NULL)
136 /* This is defined in locale/C-time.c in the GNU libc. */
137 extern const struct locale_data _nl_C_LC_TIME attribute_hidden
;
139 # define weekday_name (&_nl_C_LC_TIME.values[_NL_ITEM_INDEX (DAY_1)].string)
140 # define ab_weekday_name \
141 (&_nl_C_LC_TIME.values[_NL_ITEM_INDEX (ABDAY_1)].string)
142 # define month_name (&_nl_C_LC_TIME.values[_NL_ITEM_INDEX (MON_1)].string)
143 # define ab_month_name (&_nl_C_LC_TIME.values[_NL_ITEM_INDEX (ABMON_1)].string)
144 # define HERE_D_T_FMT (_nl_C_LC_TIME.values[_NL_ITEM_INDEX (D_T_FMT)].string)
145 # define HERE_D_FMT (_nl_C_LC_TIME.values[_NL_ITEM_INDEX (D_FMT)].string)
146 # define HERE_AM_STR (_nl_C_LC_TIME.values[_NL_ITEM_INDEX (AM_STR)].string)
147 # define HERE_PM_STR (_nl_C_LC_TIME.values[_NL_ITEM_INDEX (PM_STR)].string)
148 # define HERE_T_FMT_AMPM \
149 (_nl_C_LC_TIME.values[_NL_ITEM_INDEX (T_FMT_AMPM)].string)
150 # define HERE_T_FMT (_nl_C_LC_TIME.values[_NL_ITEM_INDEX (T_FMT)].string)
152 # define strncasecmp(s1, s2, n) __strncasecmp (s1, s2, n)
154 static char const weekday_name
[][10] =
156 "Sunday", "Monday", "Tuesday", "Wednesday",
157 "Thursday", "Friday", "Saturday"
159 static char const ab_weekday_name
[][4] =
161 "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
163 static char const month_name
[][10] =
165 "January", "February", "March", "April", "May", "June",
166 "July", "August", "September", "October", "November", "December"
168 static char const ab_month_name
[][4] =
170 "Jan", "Feb", "Mar", "Apr", "May", "Jun",
171 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
173 # define HERE_D_T_FMT "%a %b %e %H:%M:%S %Y"
174 # define HERE_D_FMT "%m/%d/%y"
175 # define HERE_AM_STR "AM"
176 # define HERE_PM_STR "PM"
177 # define HERE_T_FMT_AMPM "%I:%M:%S %p"
178 # define HERE_T_FMT "%H:%M:%S"
180 const unsigned short int __mon_yday
[2][13] =
183 { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 },
185 { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 }
189 #if defined _LIBC && defined USE_IN_EXTENDED_LOCALE_MODEL
190 /* We use this code also for the extended locale handling where the
191 function gets as an additional argument the locale which has to be
192 used. To access the values we have to redefine the _NL_CURRENT
194 # define strptime __strptime_l
196 # define _NL_CURRENT(category, item) \
197 (current->values[_NL_ITEM_INDEX (item)].string)
198 # define LOCALE_PARAM , locale
199 # define LOCALE_ARG , locale
200 # define LOCALE_PARAM_PROTO , __locale_t locale
201 # define LOCALE_PARAM_DECL __locale_t locale;
203 # define LOCALE_PARAM
205 # define LOCALE_PARAM_DECL
206 # define LOCALE_PARAM_PROTO
210 /* Status of lookup: do we use the locale data or the raw data? */
211 enum locale_status
{ not, loc
, raw
};
215 /* Nonzero if YEAR is a leap year (every 4 years,
216 except every 100th isn't, and every 400th is). */
217 # define __isleap(year) \
218 ((year) % 4 == 0 && ((year) % 100 != 0 || (year) % 400 == 0))
221 /* Compute the day of the week. */
223 day_of_the_week (struct tm
*tm
)
225 /* We know that January 1st 1970 was a Thursday (= 4). Compute the
226 the difference between this data in the one on TM and so determine
228 int corr_year
= 1900 + tm
->tm_year
- (tm
->tm_mon
< 2);
230 + (365 * (tm
->tm_year
- 70))
232 - ((corr_year
/ 4) / 25) + ((corr_year
/ 4) % 25 < 0)
233 + (((corr_year
/ 4) / 25) / 4)
234 + __mon_yday
[0][tm
->tm_mon
]
236 tm
->tm_wday
= ((wday
% 7) + 7) % 7;
239 /* Compute the day of the year. */
241 day_of_the_year (struct tm
*tm
)
243 tm
->tm_yday
= (__mon_yday
[__isleap (1900 + tm
->tm_year
)][tm
->tm_mon
]
244 + (tm
->tm_mday
- 1));
252 strptime_internal
__P ((const char *rp
, const char *fmt
, struct tm
*tm
,
253 enum locale_status
*decided
, int era_cnt
254 LOCALE_PARAM_PROTO
));
260 strptime_internal (rp
, fmt
, tm
, decided
, era_cnt LOCALE_PARAM
)
264 enum locale_status
*decided
;
268 #if defined _LIBC && defined USE_IN_EXTENDED_LOCALE_MODEL
269 const struct locale_data
*const current
= locale
->__locales
[LC_TIME
];
272 const char *rp_backup
;
276 int century
, want_century
;
278 int have_wday
, want_xday
;
280 int have_mon
, have_mday
;
281 int have_uweek
, have_wweek
;
284 struct era_entry
*era
;
293 have_wday
= want_xday
= have_yday
= have_mon
= have_mday
= have_uweek
= 0;
298 /* A white space in the format string matches 0 more or white
299 space in the input string. */
302 while (isspace (*rp
))
308 /* Any character but `%' must be matched by the same character
309 in the iput string. */
312 match_char (*fmt
++, *rp
++);
318 /* We need this for handling the `E' modifier. */
322 /* Make back up of current processing pointer. */
328 /* Match the `%' character itself. */
329 match_char ('%', *rp
++);
333 /* Match day of week. */
334 for (cnt
= 0; cnt
< 7; ++cnt
)
339 if (match_string (_NL_CURRENT (LC_TIME
, DAY_1
+ cnt
), rp
))
342 && strcmp (_NL_CURRENT (LC_TIME
, DAY_1
+ cnt
),
347 if (match_string (_NL_CURRENT (LC_TIME
, ABDAY_1
+ cnt
), rp
))
350 && strcmp (_NL_CURRENT (LC_TIME
, ABDAY_1
+ cnt
),
351 ab_weekday_name
[cnt
]))
358 && (match_string (weekday_name
[cnt
], rp
)
359 || match_string (ab_weekday_name
[cnt
], rp
)))
366 /* Does not match a weekday name. */
374 /* Match month name. */
375 for (cnt
= 0; cnt
< 12; ++cnt
)
380 if (match_string (_NL_CURRENT (LC_TIME
, MON_1
+ cnt
), rp
))
383 && strcmp (_NL_CURRENT (LC_TIME
, MON_1
+ cnt
),
388 if (match_string (_NL_CURRENT (LC_TIME
, ABMON_1
+ cnt
), rp
))
391 && strcmp (_NL_CURRENT (LC_TIME
, ABMON_1
+ cnt
),
398 if (match_string (month_name
[cnt
], rp
)
399 || match_string (ab_month_name
[cnt
], rp
))
406 /* Does not match a month name. */
412 /* Match locale's date and time format. */
416 if (!recursive (_NL_CURRENT (LC_TIME
, D_T_FMT
)))
425 if (*decided
== not &&
426 strcmp (_NL_CURRENT (LC_TIME
, D_T_FMT
), HERE_D_T_FMT
))
434 if (!recursive (HERE_D_T_FMT
))
439 /* Match century number. */
441 get_number (0, 99, 2);
447 /* Match day of month. */
448 get_number (1, 31, 2);
454 if (!recursive ("%Y-%m-%d"))
462 if (!recursive (_NL_CURRENT (LC_TIME
, D_FMT
)))
472 && strcmp (_NL_CURRENT (LC_TIME
, D_FMT
), HERE_D_FMT
))
482 /* Match standard day format. */
483 if (!recursive (HERE_D_FMT
))
489 /* Match hour in 24-hour clock. */
490 get_number (0, 23, 2);
495 /* Match hour in 12-hour clock. GNU extension. */
497 /* Match hour in 12-hour clock. */
498 get_number (1, 12, 2);
499 tm
->tm_hour
= val
% 12;
503 /* Match day number of year. */
504 get_number (1, 366, 3);
505 tm
->tm_yday
= val
- 1;
509 /* Match number of month. */
510 get_number (1, 12, 2);
511 tm
->tm_mon
= val
- 1;
517 get_number (0, 59, 2);
522 /* Match any white space. */
523 while (isspace (*rp
))
527 /* Match locale's equivalent of AM/PM. */
531 if (match_string (_NL_CURRENT (LC_TIME
, AM_STR
), rp
))
533 if (strcmp (_NL_CURRENT (LC_TIME
, AM_STR
), HERE_AM_STR
))
537 if (match_string (_NL_CURRENT (LC_TIME
, PM_STR
), rp
))
539 if (strcmp (_NL_CURRENT (LC_TIME
, PM_STR
), HERE_PM_STR
))
547 if (!match_string (HERE_AM_STR
, rp
))
548 if (match_string (HERE_PM_STR
, rp
))
557 if (!recursive (_NL_CURRENT (LC_TIME
, T_FMT_AMPM
)))
566 if (*decided
== not &&
567 strcmp (_NL_CURRENT (LC_TIME
, T_FMT_AMPM
),
575 if (!recursive (HERE_T_FMT_AMPM
))
579 if (!recursive ("%H:%M"))
584 /* The number of seconds may be very high so we cannot use
585 the `get_number' macro. Instead read the number
586 character for character and construct the result while
589 if (*rp
< '0' || *rp
> '9')
590 /* We need at least one digit. */
598 while (*rp
>= '0' && *rp
<= '9');
600 if (localtime_r (&secs
, tm
) == NULL
)
601 /* Error in function. */
606 get_number (0, 61, 2);
613 if (!recursive (_NL_CURRENT (LC_TIME
, T_FMT
)))
622 if (strcmp (_NL_CURRENT (LC_TIME
, T_FMT
), HERE_T_FMT
))
631 if (!recursive (HERE_T_FMT
))
635 get_number (1, 7, 1);
636 tm
->tm_wday
= val
% 7;
640 get_number (0, 99, 2);
641 /* XXX This cannot determine any field in TM. */
644 if (*rp
< '0' || *rp
> '9')
646 /* XXX Ignore the number since we would need some more
647 information to compute a real date. */
650 while (*rp
>= '0' && *rp
<= '9');
653 get_number (0, 53, 2);
658 get_number (0, 53, 2);
663 get_number (0, 53, 2);
664 /* XXX This cannot determine any field in TM without some
668 /* Match number of weekday. */
669 get_number (0, 6, 1);
674 match_year_in_century
:
675 /* Match year within century. */
676 get_number (0, 99, 2);
677 /* The "Year 2000: The Millennium Rollover" paper suggests that
678 values in the range 69-99 refer to the twentieth century. */
679 tm
->tm_year
= val
>= 69 ? val
: val
+ 100;
680 /* Indicate that we want to use the century, if specified. */
685 /* Match year including century number. */
686 get_number (0, 9999, 4);
687 tm
->tm_year
= val
- 1900;
692 /* XXX How to handle this? */
699 /* Match locale's alternate date and time format. */
702 const char *fmt
= _NL_CURRENT (LC_TIME
, ERA_D_T_FMT
);
705 fmt
= _NL_CURRENT (LC_TIME
, D_T_FMT
);
707 if (!recursive (fmt
))
716 if (strcmp (fmt
, HERE_D_T_FMT
))
723 /* The C locale has no era information, so use the
724 normal representation. */
725 if (!recursive (HERE_D_T_FMT
))
734 era
= _nl_select_era_entry (era_cnt
);
735 if (match_string (era
->era_name
, rp
))
745 num_eras
= _NL_CURRENT_WORD (LC_TIME
,
746 _NL_TIME_ERA_NUM_ENTRIES
);
747 for (era_cnt
= 0; era_cnt
< (int) num_eras
;
748 ++era_cnt
, rp
= rp_backup
)
750 era
= _nl_select_era_entry (era_cnt
);
751 if (match_string (era
->era_name
, rp
))
757 if (era_cnt
== (int) num_eras
)
769 /* The C locale has no era information, so use the
770 normal representation. */
774 goto match_year_in_century
;
776 get_number(0, 9999, 4);
785 num_eras
= _NL_CURRENT_WORD (LC_TIME
,
786 _NL_TIME_ERA_NUM_ENTRIES
);
787 for (era_cnt
= 0; era_cnt
< (int) num_eras
;
788 ++era_cnt
, rp
= rp_backup
)
790 era
= _nl_select_era_entry (era_cnt
);
791 if (recursive (era
->era_format
))
794 if (era_cnt
== (int) num_eras
)
811 get_number (0, 9999, 4);
812 tm
->tm_year
= val
- 1900;
819 const char *fmt
= _NL_CURRENT (LC_TIME
, ERA_D_FMT
);
822 fmt
= _NL_CURRENT (LC_TIME
, D_FMT
);
824 if (!recursive (fmt
))
833 if (strcmp (fmt
, HERE_D_FMT
))
839 if (!recursive (HERE_D_FMT
))
845 const char *fmt
= _NL_CURRENT (LC_TIME
, ERA_T_FMT
);
848 fmt
= _NL_CURRENT (LC_TIME
, T_FMT
);
850 if (!recursive (fmt
))
859 if (strcmp (fmt
, HERE_T_FMT
))
865 if (!recursive (HERE_T_FMT
))
873 /* We have no information about the era format. Just use
874 the normal format. */
875 if (*fmt
!= 'c' && *fmt
!= 'C' && *fmt
!= 'y' && *fmt
!= 'Y'
876 && *fmt
!= 'x' && *fmt
!= 'X')
877 /* This is an illegal format. */
887 /* Match day of month using alternate numeric symbols. */
888 get_alt_number (1, 31, 2);
894 /* Match hour in 24-hour clock using alternate numeric
896 get_alt_number (0, 23, 2);
901 /* Match hour in 12-hour clock using alternate numeric
903 get_alt_number (1, 12, 2);
904 tm
->tm_hour
= val
% 12;
908 /* Match month using alternate numeric symbols. */
909 get_alt_number (1, 12, 2);
910 tm
->tm_mon
= val
- 1;
915 /* Match minutes using alternate numeric symbols. */
916 get_alt_number (0, 59, 2);
920 /* Match seconds using alternate numeric symbols. */
921 get_alt_number (0, 61, 2);
925 get_alt_number (0, 53, 2);
930 get_alt_number (0, 53, 2);
935 get_alt_number (0, 53, 2);
936 /* XXX This cannot determine any field in TM without
937 further information. */
940 /* Match number of weekday using alternate numeric symbols. */
941 get_alt_number (0, 6, 1);
946 /* Match year within century using alternate numeric symbols. */
947 get_alt_number (0, 99, 2);
948 tm
->tm_year
= val
>= 69 ? val
: val
+ 100;
966 tm
->tm_year
= tm
->tm_year
% 100 + (century
- 19) * 100;
968 /* Only the century, but not the year. Strange, but so be it. */
969 tm
->tm_year
= (century
- 19) * 100;
974 era
= _nl_select_era_entry (era_cnt
);
976 tm
->tm_year
= (era
->start_date
[0]
977 + ((tm
->tm_year
- era
->offset
)
978 * era
->absolute_direction
));
980 /* Era start year assumed. */
981 tm
->tm_year
= era
->start_date
[0];
986 /* No era found but we have seen an E modifier. Rectify some
988 if (want_century
&& century
== -1 && tm
->tm_year
< 69)
992 if (want_xday
&& !have_wday
)
994 if ( !(have_mon
&& have_mday
) && have_yday
)
996 /* We don't have tm_mon and/or tm_mday, compute them. */
998 while (__mon_yday
[__isleap(1900 + tm
->tm_year
)][t_mon
] <= tm
->tm_yday
)
1001 tm
->tm_mon
= t_mon
- 1;
1005 - __mon_yday
[__isleap(1900 + tm
->tm_year
)][t_mon
- 1] + 1);
1007 day_of_the_week (tm
);
1010 if (want_xday
&& !have_yday
)
1011 day_of_the_year (tm
);
1013 if ((have_uweek
|| have_wweek
) && have_wday
)
1015 int save_wday
= tm
->tm_wday
;
1016 int save_mday
= tm
->tm_mday
;
1017 int save_mon
= tm
->tm_mon
;
1018 int w_offset
= have_uweek
? 0 : 1;
1022 day_of_the_week (tm
);
1024 tm
->tm_mday
= save_mday
;
1026 tm
->tm_mon
= save_mon
;
1029 tm
->tm_yday
= ((7 - (tm
->tm_wday
- w_offset
)) % 7
1031 + save_wday
- w_offset
);
1033 if (!have_mday
|| !have_mon
)
1036 while (__mon_yday
[__isleap(1900 + tm
->tm_year
)][t_mon
]
1040 tm
->tm_mon
= t_mon
- 1;
1044 - __mon_yday
[__isleap(1900 + tm
->tm_year
)][t_mon
- 1] + 1);
1047 tm
->tm_wday
= save_wday
;
1055 strptime (buf
, format
, tm LOCALE_PARAM
)
1061 enum locale_status decided
;
1068 return strptime_internal (buf
, format
, tm
, &decided
, -1 LOCALE_ARG
);
1070 #if defined _LIBC && !defined USE_IN_EXTENDED_LOCALE_MODEL
1071 libc_hidden_def (strptime
)