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, decided, era_cnt)) != NULL)
135 /* This is defined in locale/C-time.c in the GNU libc. */
136 extern const struct locale_data _nl_C_LC_TIME attribute_hidden
;
138 # define weekday_name (&_nl_C_LC_TIME.values[_NL_ITEM_INDEX (DAY_1)].string)
139 # define ab_weekday_name \
140 (&_nl_C_LC_TIME.values[_NL_ITEM_INDEX (ABDAY_1)].string)
141 # define month_name (&_nl_C_LC_TIME.values[_NL_ITEM_INDEX (MON_1)].string)
142 # define ab_month_name (&_nl_C_LC_TIME.values[_NL_ITEM_INDEX (ABMON_1)].string)
143 # define HERE_D_T_FMT (_nl_C_LC_TIME.values[_NL_ITEM_INDEX (D_T_FMT)].string)
144 # define HERE_D_FMT (_nl_C_LC_TIME.values[_NL_ITEM_INDEX (D_FMT)].string)
145 # define HERE_AM_STR (_nl_C_LC_TIME.values[_NL_ITEM_INDEX (AM_STR)].string)
146 # define HERE_PM_STR (_nl_C_LC_TIME.values[_NL_ITEM_INDEX (PM_STR)].string)
147 # define HERE_T_FMT_AMPM \
148 (_nl_C_LC_TIME.values[_NL_ITEM_INDEX (T_FMT_AMPM)].string)
149 # define HERE_T_FMT (_nl_C_LC_TIME.values[_NL_ITEM_INDEX (T_FMT)].string)
151 # define strncasecmp(s1, s2, n) __strncasecmp (s1, s2, n)
153 static char const weekday_name
[][10] =
155 "Sunday", "Monday", "Tuesday", "Wednesday",
156 "Thursday", "Friday", "Saturday"
158 static char const ab_weekday_name
[][4] =
160 "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
162 static char const month_name
[][10] =
164 "January", "February", "March", "April", "May", "June",
165 "July", "August", "September", "October", "November", "December"
167 static char const ab_month_name
[][4] =
169 "Jan", "Feb", "Mar", "Apr", "May", "Jun",
170 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
172 # define HERE_D_T_FMT "%a %b %e %H:%M:%S %Y"
173 # define HERE_D_FMT "%m/%d/%y"
174 # define HERE_AM_STR "AM"
175 # define HERE_PM_STR "PM"
176 # define HERE_T_FMT_AMPM "%I:%M:%S %p"
177 # define HERE_T_FMT "%H:%M:%S"
179 const unsigned short int __mon_yday
[2][13] =
182 { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 },
184 { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 }
188 /* Status of lookup: do we use the locale data or the raw data? */
189 enum locale_status
{ not, loc
, raw
};
193 /* Nonzero if YEAR is a leap year (every 4 years,
194 except every 100th isn't, and every 400th is). */
195 # define __isleap(year) \
196 ((year) % 4 == 0 && ((year) % 100 != 0 || (year) % 400 == 0))
199 /* Compute the day of the week. */
201 day_of_the_week (struct tm
*tm
)
203 /* We know that January 1st 1970 was a Thursday (= 4). Compute the
204 the difference between this data in the one on TM and so determine
206 int corr_year
= 1900 + tm
->tm_year
- (tm
->tm_mon
< 2);
208 + (365 * (tm
->tm_year
- 70))
210 - ((corr_year
/ 4) / 25) + ((corr_year
/ 4) % 25 < 0)
211 + (((corr_year
/ 4) / 25) / 4)
212 + __mon_yday
[0][tm
->tm_mon
]
214 tm
->tm_wday
= ((wday
% 7) + 7) % 7;
217 /* Compute the day of the year. */
219 day_of_the_year (struct tm
*tm
)
221 tm
->tm_yday
= (__mon_yday
[__isleap (1900 + tm
->tm_year
)][tm
->tm_mon
]
222 + (tm
->tm_mday
- 1));
229 strptime_internal
__P ((const char *rp
, const char *fmt
, struct tm
*tm
,
230 enum locale_status
*decided
, int era_cnt
));
236 strptime_internal (rp
, fmt
, tm
, decided
, era_cnt
)
240 enum locale_status
*decided
;
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
;
255 struct era_entry
*era
;
264 have_wday
= want_xday
= have_yday
= have_mon
= have_mday
= have_uweek
= 0;
269 /* A white space in the format string matches 0 more or white
270 space in the input string. */
273 while (isspace (*rp
))
279 /* Any character but `%' must be matched by the same character
280 in the iput string. */
283 match_char (*fmt
++, *rp
++);
289 /* We need this for handling the `E' modifier. */
293 /* Make back up of current processing pointer. */
299 /* Match the `%' character itself. */
300 match_char ('%', *rp
++);
304 /* Match day of week. */
305 for (cnt
= 0; cnt
< 7; ++cnt
)
310 if (match_string (_NL_CURRENT (LC_TIME
, DAY_1
+ cnt
), rp
))
313 && strcmp (_NL_CURRENT (LC_TIME
, DAY_1
+ cnt
),
318 if (match_string (_NL_CURRENT (LC_TIME
, ABDAY_1
+ cnt
), rp
))
321 && strcmp (_NL_CURRENT (LC_TIME
, ABDAY_1
+ cnt
),
322 ab_weekday_name
[cnt
]))
329 && (match_string (weekday_name
[cnt
], rp
)
330 || match_string (ab_weekday_name
[cnt
], rp
)))
337 /* Does not match a weekday name. */
345 /* Match month name. */
346 for (cnt
= 0; cnt
< 12; ++cnt
)
351 if (match_string (_NL_CURRENT (LC_TIME
, MON_1
+ cnt
), rp
))
354 && strcmp (_NL_CURRENT (LC_TIME
, MON_1
+ cnt
),
359 if (match_string (_NL_CURRENT (LC_TIME
, ABMON_1
+ cnt
), rp
))
362 && strcmp (_NL_CURRENT (LC_TIME
, ABMON_1
+ cnt
),
369 if (match_string (month_name
[cnt
], rp
)
370 || match_string (ab_month_name
[cnt
], rp
))
377 /* Does not match a month name. */
383 /* Match locale's date and time format. */
387 if (!recursive (_NL_CURRENT (LC_TIME
, D_T_FMT
)))
396 if (*decided
== not &&
397 strcmp (_NL_CURRENT (LC_TIME
, D_T_FMT
), HERE_D_T_FMT
))
405 if (!recursive (HERE_D_T_FMT
))
410 /* Match century number. */
412 get_number (0, 99, 2);
418 /* Match day of month. */
419 get_number (1, 31, 2);
425 if (!recursive ("%Y-%m-%d"))
433 if (!recursive (_NL_CURRENT (LC_TIME
, D_FMT
)))
443 && strcmp (_NL_CURRENT (LC_TIME
, D_FMT
), HERE_D_FMT
))
453 /* Match standard day format. */
454 if (!recursive (HERE_D_FMT
))
460 /* Match hour in 24-hour clock. */
461 get_number (0, 23, 2);
466 /* Match hour in 12-hour clock. GNU extension. */
468 /* Match hour in 12-hour clock. */
469 get_number (1, 12, 2);
470 tm
->tm_hour
= val
% 12;
474 /* Match day number of year. */
475 get_number (1, 366, 3);
476 tm
->tm_yday
= val
- 1;
480 /* Match number of month. */
481 get_number (1, 12, 2);
482 tm
->tm_mon
= val
- 1;
488 get_number (0, 59, 2);
493 /* Match any white space. */
494 while (isspace (*rp
))
498 /* Match locale's equivalent of AM/PM. */
502 if (match_string (_NL_CURRENT (LC_TIME
, AM_STR
), rp
))
504 if (strcmp (_NL_CURRENT (LC_TIME
, AM_STR
), HERE_AM_STR
))
508 if (match_string (_NL_CURRENT (LC_TIME
, PM_STR
), rp
))
510 if (strcmp (_NL_CURRENT (LC_TIME
, PM_STR
), HERE_PM_STR
))
518 if (!match_string (HERE_AM_STR
, rp
))
519 if (match_string (HERE_PM_STR
, rp
))
528 if (!recursive (_NL_CURRENT (LC_TIME
, T_FMT_AMPM
)))
537 if (*decided
== not &&
538 strcmp (_NL_CURRENT (LC_TIME
, T_FMT_AMPM
),
546 if (!recursive (HERE_T_FMT_AMPM
))
550 if (!recursive ("%H:%M"))
555 /* The number of seconds may be very high so we cannot use
556 the `get_number' macro. Instead read the number
557 character for character and construct the result while
560 if (*rp
< '0' || *rp
> '9')
561 /* We need at least one digit. */
569 while (*rp
>= '0' && *rp
<= '9');
571 if (localtime_r (&secs
, tm
) == NULL
)
572 /* Error in function. */
577 get_number (0, 61, 2);
584 if (!recursive (_NL_CURRENT (LC_TIME
, T_FMT
)))
593 if (strcmp (_NL_CURRENT (LC_TIME
, T_FMT
), HERE_T_FMT
))
602 if (!recursive (HERE_T_FMT
))
606 get_number (1, 7, 1);
607 tm
->tm_wday
= val
% 7;
611 get_number (0, 99, 2);
612 /* XXX This cannot determine any field in TM. */
615 if (*rp
< '0' || *rp
> '9')
617 /* XXX Ignore the number since we would need some more
618 information to compute a real date. */
621 while (*rp
>= '0' && *rp
<= '9');
624 get_number (0, 53, 2);
629 get_number (0, 53, 2);
634 get_number (0, 53, 2);
635 /* XXX This cannot determine any field in TM without some
639 /* Match number of weekday. */
640 get_number (0, 6, 1);
645 match_year_in_century
:
646 /* Match year within century. */
647 get_number (0, 99, 2);
648 /* The "Year 2000: The Millennium Rollover" paper suggests that
649 values in the range 69-99 refer to the twentieth century. */
650 tm
->tm_year
= val
>= 69 ? val
: val
+ 100;
651 /* Indicate that we want to use the century, if specified. */
656 /* Match year including century number. */
657 get_number (0, 9999, 4);
658 tm
->tm_year
= val
- 1900;
663 /* XXX How to handle this? */
670 /* Match locale's alternate date and time format. */
673 const char *fmt
= _NL_CURRENT (LC_TIME
, ERA_D_T_FMT
);
676 fmt
= _NL_CURRENT (LC_TIME
, D_T_FMT
);
678 if (!recursive (fmt
))
687 if (strcmp (fmt
, HERE_D_T_FMT
))
694 /* The C locale has no era information, so use the
695 normal representation. */
696 if (!recursive (HERE_D_T_FMT
))
705 era
= _nl_select_era_entry (era_cnt
);
706 if (match_string (era
->era_name
, rp
))
716 num_eras
= _NL_CURRENT_WORD (LC_TIME
,
717 _NL_TIME_ERA_NUM_ENTRIES
);
718 for (era_cnt
= 0; era_cnt
< (int) num_eras
;
719 ++era_cnt
, rp
= rp_backup
)
721 era
= _nl_select_era_entry (era_cnt
);
722 if (match_string (era
->era_name
, rp
))
728 if (era_cnt
== (int) num_eras
)
740 /* The C locale has no era information, so use the
741 normal representation. */
745 goto match_year_in_century
;
747 get_number(0, 9999, 4);
756 num_eras
= _NL_CURRENT_WORD (LC_TIME
,
757 _NL_TIME_ERA_NUM_ENTRIES
);
758 for (era_cnt
= 0; era_cnt
< (int) num_eras
;
759 ++era_cnt
, rp
= rp_backup
)
761 era
= _nl_select_era_entry (era_cnt
);
762 if (recursive (era
->era_format
))
765 if (era_cnt
== (int) num_eras
)
782 get_number (0, 9999, 4);
783 tm
->tm_year
= val
- 1900;
790 const char *fmt
= _NL_CURRENT (LC_TIME
, ERA_D_FMT
);
793 fmt
= _NL_CURRENT (LC_TIME
, D_FMT
);
795 if (!recursive (fmt
))
804 if (strcmp (fmt
, HERE_D_FMT
))
810 if (!recursive (HERE_D_FMT
))
816 const char *fmt
= _NL_CURRENT (LC_TIME
, ERA_T_FMT
);
819 fmt
= _NL_CURRENT (LC_TIME
, T_FMT
);
821 if (!recursive (fmt
))
830 if (strcmp (fmt
, HERE_T_FMT
))
836 if (!recursive (HERE_T_FMT
))
844 /* We have no information about the era format. Just use
845 the normal format. */
846 if (*fmt
!= 'c' && *fmt
!= 'C' && *fmt
!= 'y' && *fmt
!= 'Y'
847 && *fmt
!= 'x' && *fmt
!= 'X')
848 /* This is an illegal format. */
858 /* Match day of month using alternate numeric symbols. */
859 get_alt_number (1, 31, 2);
865 /* Match hour in 24-hour clock using alternate numeric
867 get_alt_number (0, 23, 2);
872 /* Match hour in 12-hour clock using alternate numeric
874 get_alt_number (1, 12, 2);
875 tm
->tm_hour
= val
% 12;
879 /* Match month using alternate numeric symbols. */
880 get_alt_number (1, 12, 2);
881 tm
->tm_mon
= val
- 1;
886 /* Match minutes using alternate numeric symbols. */
887 get_alt_number (0, 59, 2);
891 /* Match seconds using alternate numeric symbols. */
892 get_alt_number (0, 61, 2);
896 get_alt_number (0, 53, 2);
901 get_alt_number (0, 53, 2);
906 get_alt_number (0, 53, 2);
907 /* XXX This cannot determine any field in TM without
908 further information. */
911 /* Match number of weekday using alternate numeric symbols. */
912 get_alt_number (0, 6, 1);
917 /* Match year within century using alternate numeric symbols. */
918 get_alt_number (0, 99, 2);
919 tm
->tm_year
= val
>= 69 ? val
: val
+ 100;
937 tm
->tm_year
= tm
->tm_year
% 100 + (century
- 19) * 100;
939 /* Only the century, but not the year. Strange, but so be it. */
940 tm
->tm_year
= (century
- 19) * 100;
945 era
= _nl_select_era_entry (era_cnt
);
947 tm
->tm_year
= (era
->start_date
[0]
948 + ((tm
->tm_year
- era
->offset
)
949 * era
->absolute_direction
));
951 /* Era start year assumed. */
952 tm
->tm_year
= era
->start_date
[0];
957 /* No era found but we have seen an E modifier. Rectify some
959 if (want_century
&& century
== -1 && tm
->tm_year
< 69)
963 if (want_xday
&& !have_wday
)
965 if ( !(have_mon
&& have_mday
) && have_yday
)
967 /* We don't have tm_mon and/or tm_mday, compute them. */
969 while (__mon_yday
[__isleap(1900 + tm
->tm_year
)][t_mon
] <= tm
->tm_yday
)
972 tm
->tm_mon
= t_mon
- 1;
976 - __mon_yday
[__isleap(1900 + tm
->tm_year
)][t_mon
- 1] + 1);
978 day_of_the_week (tm
);
981 if (want_xday
&& !have_yday
)
982 day_of_the_year (tm
);
984 if ((have_uweek
|| have_wweek
) && have_wday
)
986 int save_wday
= tm
->tm_wday
;
987 int save_mday
= tm
->tm_mday
;
988 int save_mon
= tm
->tm_mon
;
989 int w_offset
= have_uweek
? 0 : 1;
993 day_of_the_week (tm
);
995 tm
->tm_mday
= save_mday
;
997 tm
->tm_mon
= save_mon
;
1000 tm
->tm_yday
= ((7 - (tm
->tm_wday
- w_offset
)) % 7
1002 + save_wday
- w_offset
);
1004 if (!have_mday
|| !have_mon
)
1007 while (__mon_yday
[__isleap(1900 + tm
->tm_year
)][t_mon
]
1011 tm
->tm_mon
= t_mon
- 1;
1015 - __mon_yday
[__isleap(1900 + tm
->tm_year
)][t_mon
- 1] + 1);
1018 tm
->tm_wday
= save_wday
;
1026 strptime (buf
, format
, tm
)
1031 enum locale_status decided
;
1038 return strptime_internal (buf
, format
, tm
, &decided
, -1);