]>
Commit | Line | Data |
---|---|---|
5290baf0 | 1 | /* Convert a string representation of time to a time value. |
ab26a24a | 2 | Copyright (C) 1996-2000, 2001, 2002 Free Software Foundation, Inc. |
8a4b65b4 UD |
3 | This file is part of the GNU C Library. |
4 | Contributed by Ulrich Drepper <drepper@cygnus.com>, 1996. | |
f8adc70c | 5 | |
8a4b65b4 | 6 | The GNU C Library is free software; you can redistribute it and/or |
41bdb6e2 AJ |
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. | |
f8adc70c | 10 | |
8a4b65b4 UD |
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 | |
41bdb6e2 | 14 | Lesser General Public License for more details. |
f8adc70c | 15 | |
41bdb6e2 AJ |
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 | |
19 | 02111-1307 USA. */ | |
f8adc70c | 20 | |
5290baf0 UD |
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. */ | |
25 | ||
26 | #ifdef HAVE_CONFIG_H | |
27 | # include <config.h> | |
28 | #endif | |
29 | ||
f8adc70c RM |
30 | #include <ctype.h> |
31 | #include <langinfo.h> | |
32 | #include <limits.h> | |
33 | #include <string.h> | |
34 | #include <time.h> | |
35 | ||
5290baf0 UD |
36 | #ifdef _LIBC |
37 | # include "../locale/localeinfo.h" | |
38 | #endif | |
39 | ||
40 | ||
41 | #ifndef __P | |
2b15132f | 42 | # if defined __GNUC__ || (defined __STDC__ && __STDC__) |
5290baf0 UD |
43 | # define __P(args) args |
44 | # else | |
45 | # define __P(args) () | |
46 | # endif /* GCC. */ | |
47 | #endif /* Not __P. */ | |
48 | ||
c2cfb512 UD |
49 | |
50 | #if ! HAVE_LOCALTIME_R && ! defined localtime_r | |
51 | # ifdef _LIBC | |
52 | # define localtime_r __localtime_r | |
53 | # else | |
5290baf0 | 54 | /* Approximate localtime_r as best we can in its absence. */ |
c2cfb512 | 55 | # define localtime_r my_localtime_r |
5290baf0 UD |
56 | static struct tm *localtime_r __P ((const time_t *, struct tm *)); |
57 | static struct tm * | |
58 | localtime_r (t, tp) | |
59 | const time_t *t; | |
60 | struct tm *tp; | |
61 | { | |
62 | struct tm *l = localtime (t); | |
63 | if (! l) | |
64 | return 0; | |
65 | *tp = *l; | |
66 | return tp; | |
67 | } | |
c2cfb512 | 68 | # endif /* ! _LIBC */ |
5290baf0 | 69 | #endif /* ! HAVE_LOCALTIME_R && ! defined (localtime_r) */ |
f8adc70c RM |
70 | |
71 | ||
72 | #define match_char(ch1, ch2) if (ch1 != ch2) return NULL | |
5290baf0 UD |
73 | #if defined __GNUC__ && __GNUC__ >= 2 |
74 | # define match_string(cs1, s2) \ | |
f8adc70c | 75 | ({ size_t len = strlen (cs1); \ |
5290baf0 UD |
76 | int result = strncasecmp ((cs1), (s2), len) == 0; \ |
77 | if (result) (s2) += len; \ | |
f8adc70c | 78 | result; }) |
5290baf0 UD |
79 | #else |
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)) | |
83 | #endif | |
f8adc70c RM |
84 | /* We intentionally do not use isdigit() for testing because this will |
85 | lead to problems with the wide character version. */ | |
965e02a2 | 86 | #define get_number(from, to, n) \ |
f8adc70c | 87 | do { \ |
965e02a2 | 88 | int __n = n; \ |
f8adc70c | 89 | val = 0; \ |
e9dcb080 UD |
90 | while (*rp == ' ') \ |
91 | ++rp; \ | |
f8adc70c RM |
92 | if (*rp < '0' || *rp > '9') \ |
93 | return NULL; \ | |
94 | do { \ | |
95 | val *= 10; \ | |
96 | val += *rp++ - '0'; \ | |
965e02a2 | 97 | } while (--__n > 0 && val * 10 <= to && *rp >= '0' && *rp <= '9'); \ |
f8adc70c RM |
98 | if (val < from || val > to) \ |
99 | return NULL; \ | |
100 | } while (0) | |
5290baf0 | 101 | #ifdef _NL_CURRENT |
965e02a2 UD |
102 | # define get_alt_number(from, to, n) \ |
103 | ({ \ | |
2b15132f UD |
104 | __label__ do_normal; \ |
105 | \ | |
106 | if (*decided != raw) \ | |
107 | { \ | |
108 | val = _nl_parse_alt_digit (&rp); \ | |
109 | if (val == -1 && *decided != loc) \ | |
110 | { \ | |
111 | *decided = loc; \ | |
112 | goto do_normal; \ | |
113 | } \ | |
965e02a2 UD |
114 | if (val < from || val > to) \ |
115 | return NULL; \ | |
2b15132f UD |
116 | } \ |
117 | else \ | |
118 | { \ | |
965e02a2 | 119 | do_normal: \ |
2b15132f UD |
120 | get_number (from, to, n); \ |
121 | } \ | |
965e02a2 UD |
122 | 0; \ |
123 | }) | |
5290baf0 | 124 | #else |
965e02a2 | 125 | # define get_alt_number(from, to, n) \ |
5290baf0 | 126 | /* We don't have the alternate representation. */ \ |
965e02a2 | 127 | get_number(from, to, n) |
5290baf0 UD |
128 | #endif |
129 | #define recursive(new_fmt) \ | |
130 | (*(new_fmt) != '\0' \ | |
958d6807 | 131 | && (rp = strptime_internal (rp, (new_fmt), tm, decided, era_cnt)) != NULL) |
d41c6f61 | 132 | |
f8adc70c | 133 | |
5290baf0 UD |
134 | #ifdef _LIBC |
135 | /* This is defined in locale/C-time.c in the GNU libc. */ | |
a4f4b72b | 136 | extern const struct locale_data _nl_C_LC_TIME; |
5290baf0 UD |
137 | |
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) | |
655b26bb | 144 | # define HERE_D_FMT (_nl_C_LC_TIME.values[_NL_ITEM_INDEX (D_FMT)].string) |
5290baf0 UD |
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) | |
50304ef0 UD |
150 | |
151 | # define strncasecmp(s1, s2, n) __strncasecmp (s1, s2, n) | |
5290baf0 UD |
152 | #else |
153 | static char const weekday_name[][10] = | |
154 | { | |
155 | "Sunday", "Monday", "Tuesday", "Wednesday", | |
156 | "Thursday", "Friday", "Saturday" | |
157 | }; | |
158 | static char const ab_weekday_name[][4] = | |
159 | { | |
160 | "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" | |
161 | }; | |
162 | static char const month_name[][10] = | |
163 | { | |
164 | "January", "February", "March", "April", "May", "June", | |
165 | "July", "August", "September", "October", "November", "December" | |
166 | }; | |
167 | static char const ab_month_name[][4] = | |
168 | { | |
169 | "Jan", "Feb", "Mar", "Apr", "May", "Jun", | |
170 | "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" | |
171 | }; | |
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" | |
c2cfb512 | 178 | |
2aefabe1 | 179 | const unsigned short int __mon_yday[2][13] = |
c2cfb512 UD |
180 | { |
181 | /* Normal years. */ | |
182 | { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 }, | |
183 | /* Leap years. */ | |
184 | { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 } | |
185 | }; | |
5290baf0 UD |
186 | #endif |
187 | ||
188 | /* Status of lookup: do we use the locale data or the raw data? */ | |
189 | enum locale_status { not, loc, raw }; | |
190 | ||
c2cfb512 UD |
191 | |
192 | #ifndef __isleap | |
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)) | |
197 | #endif | |
198 | ||
199 | /* Compute the day of the week. */ | |
200 | static void | |
201 | day_of_the_week (struct tm *tm) | |
202 | { | |
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 | |
205 | the weekday. */ | |
6269e521 | 206 | int corr_year = 1900 + tm->tm_year - (tm->tm_mon < 2); |
c2cfb512 | 207 | int wday = (-473 |
6269e521 | 208 | + (365 * (tm->tm_year - 70)) |
c2cfb512 UD |
209 | + (corr_year / 4) |
210 | - ((corr_year / 4) / 25) + ((corr_year / 4) % 25 < 0) | |
211 | + (((corr_year / 4) / 25) / 4) | |
212 | + __mon_yday[0][tm->tm_mon] | |
6269e521 | 213 | + tm->tm_mday - 1); |
450f4601 | 214 | tm->tm_wday = ((wday % 7) + 7) % 7; |
c2cfb512 UD |
215 | } |
216 | ||
6269e521 | 217 | /* Compute the day of the year. */ |
c2cfb512 UD |
218 | static void |
219 | day_of_the_year (struct tm *tm) | |
220 | { | |
6269e521 UD |
221 | tm->tm_yday = (__mon_yday[__isleap (1900 + tm->tm_year)][tm->tm_mon] |
222 | + (tm->tm_mday - 1)); | |
c2cfb512 UD |
223 | } |
224 | ||
0c5ecdc4 | 225 | static char * |
dfd2257a UD |
226 | #ifdef _LIBC |
227 | internal_function | |
228 | #endif | |
958d6807 UD |
229 | strptime_internal __P ((const char *rp, const char *fmt, struct tm *tm, |
230 | enum locale_status *decided, int era_cnt)); | |
5290baf0 UD |
231 | |
232 | static char * | |
dfd2257a UD |
233 | #ifdef _LIBC |
234 | internal_function | |
235 | #endif | |
958d6807 UD |
236 | strptime_internal (rp, fmt, tm, decided, era_cnt) |
237 | const char *rp; | |
238 | const char *fmt; | |
5290baf0 UD |
239 | struct tm *tm; |
240 | enum locale_status *decided; | |
958d6807 | 241 | int era_cnt; |
f8adc70c | 242 | { |
958d6807 | 243 | const char *rp_backup; |
f8adc70c RM |
244 | int cnt; |
245 | size_t val; | |
246 | int have_I, is_pm; | |
a6ff34d7 | 247 | int century, want_century; |
958d6807 | 248 | int want_era; |
c2cfb512 UD |
249 | int have_wday, want_xday; |
250 | int have_yday; | |
8d93eb92 | 251 | int have_mon, have_mday; |
5d9f13dc UD |
252 | int have_uweek, have_wweek; |
253 | int week_no; | |
958d6807 UD |
254 | size_t num_eras; |
255 | struct era_entry *era; | |
965e02a2 | 256 | |
f8adc70c | 257 | have_I = is_pm = 0; |
a6ff34d7 UD |
258 | century = -1; |
259 | want_century = 0; | |
958d6807 UD |
260 | want_era = 0; |
261 | era = NULL; | |
5d9f13dc | 262 | week_no = 0; |
958d6807 | 263 | |
5d9f13dc UD |
264 | have_wday = want_xday = have_yday = have_mon = have_mday = have_uweek = 0; |
265 | have_wweek = 0; | |
f8adc70c RM |
266 | |
267 | while (*fmt != '\0') | |
268 | { | |
269 | /* A white space in the format string matches 0 more or white | |
270 | space in the input string. */ | |
271 | if (isspace (*fmt)) | |
272 | { | |
273 | while (isspace (*rp)) | |
274 | ++rp; | |
275 | ++fmt; | |
276 | continue; | |
277 | } | |
278 | ||
279 | /* Any character but `%' must be matched by the same character | |
280 | in the iput string. */ | |
281 | if (*fmt != '%') | |
282 | { | |
283 | match_char (*fmt++, *rp++); | |
284 | continue; | |
285 | } | |
286 | ||
287 | ++fmt; | |
5290baf0 UD |
288 | #ifndef _NL_CURRENT |
289 | /* We need this for handling the `E' modifier. */ | |
290 | start_over: | |
291 | #endif | |
958d6807 UD |
292 | |
293 | /* Make back up of current processing pointer. */ | |
294 | rp_backup = rp; | |
295 | ||
f8adc70c RM |
296 | switch (*fmt++) |
297 | { | |
298 | case '%': | |
299 | /* Match the `%' character itself. */ | |
300 | match_char ('%', *rp++); | |
301 | break; | |
302 | case 'a': | |
303 | case 'A': | |
304 | /* Match day of week. */ | |
305 | for (cnt = 0; cnt < 7; ++cnt) | |
306 | { | |
5290baf0 UD |
307 | #ifdef _NL_CURRENT |
308 | if (*decided !=raw) | |
309 | { | |
0d8733c4 | 310 | if (match_string (_NL_CURRENT (LC_TIME, DAY_1 + cnt), rp)) |
5290baf0 UD |
311 | { |
312 | if (*decided == not | |
0d8733c4 UD |
313 | && strcmp (_NL_CURRENT (LC_TIME, DAY_1 + cnt), |
314 | weekday_name[cnt])) | |
5290baf0 UD |
315 | *decided = loc; |
316 | break; | |
317 | } | |
0d8733c4 | 318 | if (match_string (_NL_CURRENT (LC_TIME, ABDAY_1 + cnt), rp)) |
5290baf0 UD |
319 | { |
320 | if (*decided == not | |
0d8733c4 UD |
321 | && strcmp (_NL_CURRENT (LC_TIME, ABDAY_1 + cnt), |
322 | ab_weekday_name[cnt])) | |
5290baf0 UD |
323 | *decided = loc; |
324 | break; | |
325 | } | |
326 | } | |
327 | #endif | |
328 | if (*decided != loc | |
0d8733c4 UD |
329 | && (match_string (weekday_name[cnt], rp) |
330 | || match_string (ab_weekday_name[cnt], rp))) | |
5290baf0 UD |
331 | { |
332 | *decided = raw; | |
333 | break; | |
334 | } | |
f8adc70c RM |
335 | } |
336 | if (cnt == 7) | |
337 | /* Does not match a weekday name. */ | |
338 | return NULL; | |
339 | tm->tm_wday = cnt; | |
c2cfb512 | 340 | have_wday = 1; |
f8adc70c RM |
341 | break; |
342 | case 'b': | |
343 | case 'B': | |
344 | case 'h': | |
345 | /* Match month name. */ | |
346 | for (cnt = 0; cnt < 12; ++cnt) | |
347 | { | |
5290baf0 UD |
348 | #ifdef _NL_CURRENT |
349 | if (*decided !=raw) | |
350 | { | |
0d8733c4 | 351 | if (match_string (_NL_CURRENT (LC_TIME, MON_1 + cnt), rp)) |
5290baf0 UD |
352 | { |
353 | if (*decided == not | |
0d8733c4 UD |
354 | && strcmp (_NL_CURRENT (LC_TIME, MON_1 + cnt), |
355 | month_name[cnt])) | |
5290baf0 UD |
356 | *decided = loc; |
357 | break; | |
358 | } | |
0d8733c4 | 359 | if (match_string (_NL_CURRENT (LC_TIME, ABMON_1 + cnt), rp)) |
5290baf0 UD |
360 | { |
361 | if (*decided == not | |
0d8733c4 UD |
362 | && strcmp (_NL_CURRENT (LC_TIME, ABMON_1 + cnt), |
363 | ab_month_name[cnt])) | |
5290baf0 UD |
364 | *decided = loc; |
365 | break; | |
366 | } | |
367 | } | |
368 | #endif | |
0d8733c4 UD |
369 | if (match_string (month_name[cnt], rp) |
370 | || match_string (ab_month_name[cnt], rp)) | |
5290baf0 UD |
371 | { |
372 | *decided = raw; | |
373 | break; | |
374 | } | |
f8adc70c RM |
375 | } |
376 | if (cnt == 12) | |
377 | /* Does not match a month name. */ | |
378 | return NULL; | |
379 | tm->tm_mon = cnt; | |
c2cfb512 | 380 | want_xday = 1; |
f8adc70c RM |
381 | break; |
382 | case 'c': | |
383 | /* Match locale's date and time format. */ | |
5290baf0 UD |
384 | #ifdef _NL_CURRENT |
385 | if (*decided != raw) | |
386 | { | |
387 | if (!recursive (_NL_CURRENT (LC_TIME, D_T_FMT))) | |
388 | { | |
389 | if (*decided == loc) | |
390 | return NULL; | |
958d6807 UD |
391 | else |
392 | rp = rp_backup; | |
5290baf0 UD |
393 | } |
394 | else | |
395 | { | |
396 | if (*decided == not && | |
397 | strcmp (_NL_CURRENT (LC_TIME, D_T_FMT), HERE_D_T_FMT)) | |
398 | *decided = loc; | |
c2cfb512 | 399 | want_xday = 1; |
5290baf0 UD |
400 | break; |
401 | } | |
402 | *decided = raw; | |
403 | } | |
404 | #endif | |
405 | if (!recursive (HERE_D_T_FMT)) | |
406 | return NULL; | |
c2cfb512 | 407 | want_xday = 1; |
f8adc70c RM |
408 | break; |
409 | case 'C': | |
410 | /* Match century number. */ | |
958d6807 | 411 | match_century: |
965e02a2 | 412 | get_number (0, 99, 2); |
a6ff34d7 | 413 | century = val; |
c2cfb512 | 414 | want_xday = 1; |
f8adc70c RM |
415 | break; |
416 | case 'd': | |
417 | case 'e': | |
418 | /* Match day of month. */ | |
965e02a2 | 419 | get_number (1, 31, 2); |
f8adc70c | 420 | tm->tm_mday = val; |
8d93eb92 | 421 | have_mday = 1; |
e9dcb080 UD |
422 | want_xday = 1; |
423 | break; | |
424 | case 'F': | |
425 | if (!recursive ("%Y-%m-%d")) | |
426 | return NULL; | |
427 | want_xday = 1; | |
f8adc70c | 428 | break; |
5290baf0 UD |
429 | case 'x': |
430 | #ifdef _NL_CURRENT | |
431 | if (*decided != raw) | |
432 | { | |
433 | if (!recursive (_NL_CURRENT (LC_TIME, D_FMT))) | |
434 | { | |
435 | if (*decided == loc) | |
436 | return NULL; | |
958d6807 UD |
437 | else |
438 | rp = rp_backup; | |
5290baf0 UD |
439 | } |
440 | else | |
441 | { | |
f9a7302f | 442 | if (*decided == not |
5290baf0 UD |
443 | && strcmp (_NL_CURRENT (LC_TIME, D_FMT), HERE_D_FMT)) |
444 | *decided = loc; | |
c2cfb512 | 445 | want_xday = 1; |
5290baf0 UD |
446 | break; |
447 | } | |
448 | *decided = raw; | |
449 | } | |
450 | #endif | |
451 | /* Fall through. */ | |
f8adc70c RM |
452 | case 'D': |
453 | /* Match standard day format. */ | |
5290baf0 UD |
454 | if (!recursive (HERE_D_FMT)) |
455 | return NULL; | |
c2cfb512 | 456 | want_xday = 1; |
f8adc70c | 457 | break; |
e9dcb080 | 458 | case 'k': |
f8adc70c RM |
459 | case 'H': |
460 | /* Match hour in 24-hour clock. */ | |
965e02a2 | 461 | get_number (0, 23, 2); |
f8adc70c RM |
462 | tm->tm_hour = val; |
463 | have_I = 0; | |
464 | break; | |
45e0579f UD |
465 | case 'l': |
466 | /* Match hour in 12-hour clock. GNU extension. */ | |
f8adc70c RM |
467 | case 'I': |
468 | /* Match hour in 12-hour clock. */ | |
965e02a2 | 469 | get_number (1, 12, 2); |
779ae82e | 470 | tm->tm_hour = val % 12; |
f8adc70c RM |
471 | have_I = 1; |
472 | break; | |
473 | case 'j': | |
474 | /* Match day number of year. */ | |
965e02a2 | 475 | get_number (1, 366, 3); |
f8adc70c | 476 | tm->tm_yday = val - 1; |
c2cfb512 | 477 | have_yday = 1; |
f8adc70c RM |
478 | break; |
479 | case 'm': | |
480 | /* Match number of month. */ | |
965e02a2 | 481 | get_number (1, 12, 2); |
f8adc70c | 482 | tm->tm_mon = val - 1; |
8d93eb92 | 483 | have_mon = 1; |
c2cfb512 | 484 | want_xday = 1; |
f8adc70c RM |
485 | break; |
486 | case 'M': | |
487 | /* Match minute. */ | |
965e02a2 | 488 | get_number (0, 59, 2); |
f8adc70c RM |
489 | tm->tm_min = val; |
490 | break; | |
491 | case 'n': | |
492 | case 't': | |
493 | /* Match any white space. */ | |
494 | while (isspace (*rp)) | |
495 | ++rp; | |
496 | break; | |
497 | case 'p': | |
498 | /* Match locale's equivalent of AM/PM. */ | |
5290baf0 UD |
499 | #ifdef _NL_CURRENT |
500 | if (*decided != raw) | |
f8adc70c | 501 | { |
5290baf0 UD |
502 | if (match_string (_NL_CURRENT (LC_TIME, AM_STR), rp)) |
503 | { | |
504 | if (strcmp (_NL_CURRENT (LC_TIME, AM_STR), HERE_AM_STR)) | |
505 | *decided = loc; | |
506 | break; | |
507 | } | |
508 | if (match_string (_NL_CURRENT (LC_TIME, PM_STR), rp)) | |
509 | { | |
510 | if (strcmp (_NL_CURRENT (LC_TIME, PM_STR), HERE_PM_STR)) | |
511 | *decided = loc; | |
512 | is_pm = 1; | |
513 | break; | |
514 | } | |
515 | *decided = raw; | |
f8adc70c | 516 | } |
5290baf0 UD |
517 | #endif |
518 | if (!match_string (HERE_AM_STR, rp)) | |
519 | if (match_string (HERE_PM_STR, rp)) | |
520 | is_pm = 1; | |
521 | else | |
522 | return NULL; | |
523 | break; | |
f8adc70c | 524 | case 'r': |
5290baf0 UD |
525 | #ifdef _NL_CURRENT |
526 | if (*decided != raw) | |
527 | { | |
528 | if (!recursive (_NL_CURRENT (LC_TIME, T_FMT_AMPM))) | |
529 | { | |
530 | if (*decided == loc) | |
531 | return NULL; | |
958d6807 UD |
532 | else |
533 | rp = rp_backup; | |
5290baf0 UD |
534 | } |
535 | else | |
536 | { | |
537 | if (*decided == not && | |
538 | strcmp (_NL_CURRENT (LC_TIME, T_FMT_AMPM), | |
539 | HERE_T_FMT_AMPM)) | |
540 | *decided = loc; | |
541 | break; | |
542 | } | |
543 | *decided = raw; | |
544 | } | |
545 | #endif | |
546 | if (!recursive (HERE_T_FMT_AMPM)) | |
547 | return NULL; | |
f8adc70c RM |
548 | break; |
549 | case 'R': | |
5290baf0 UD |
550 | if (!recursive ("%H:%M")) |
551 | return NULL; | |
f8adc70c | 552 | break; |
d41c6f61 UD |
553 | case 's': |
554 | { | |
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 | |
558 | doing this. */ | |
a25f2023 | 559 | time_t secs = 0; |
d41c6f61 UD |
560 | if (*rp < '0' || *rp > '9') |
561 | /* We need at least one digit. */ | |
562 | return NULL; | |
563 | ||
564 | do | |
565 | { | |
566 | secs *= 10; | |
567 | secs += *rp++ - '0'; | |
568 | } | |
569 | while (*rp >= '0' && *rp <= '9'); | |
570 | ||
5290baf0 | 571 | if (localtime_r (&secs, tm) == NULL) |
d41c6f61 UD |
572 | /* Error in function. */ |
573 | return NULL; | |
574 | } | |
575 | break; | |
f8adc70c | 576 | case 'S': |
965e02a2 | 577 | get_number (0, 61, 2); |
f8adc70c RM |
578 | tm->tm_sec = val; |
579 | break; | |
5290baf0 UD |
580 | case 'X': |
581 | #ifdef _NL_CURRENT | |
582 | if (*decided != raw) | |
583 | { | |
584 | if (!recursive (_NL_CURRENT (LC_TIME, T_FMT))) | |
585 | { | |
586 | if (*decided == loc) | |
587 | return NULL; | |
958d6807 UD |
588 | else |
589 | rp = rp_backup; | |
5290baf0 UD |
590 | } |
591 | else | |
592 | { | |
593 | if (strcmp (_NL_CURRENT (LC_TIME, T_FMT), HERE_T_FMT)) | |
594 | *decided = loc; | |
595 | break; | |
596 | } | |
597 | *decided = raw; | |
598 | } | |
599 | #endif | |
600 | /* Fall through. */ | |
f8adc70c | 601 | case 'T': |
5290baf0 UD |
602 | if (!recursive (HERE_T_FMT)) |
603 | return NULL; | |
f8adc70c | 604 | break; |
d41c6f61 | 605 | case 'u': |
965e02a2 | 606 | get_number (1, 7, 1); |
d41c6f61 | 607 | tm->tm_wday = val % 7; |
c2cfb512 | 608 | have_wday = 1; |
d41c6f61 UD |
609 | break; |
610 | case 'g': | |
965e02a2 | 611 | get_number (0, 99, 2); |
d41c6f61 UD |
612 | /* XXX This cannot determine any field in TM. */ |
613 | break; | |
614 | case 'G': | |
615 | if (*rp < '0' || *rp > '9') | |
616 | return NULL; | |
617 | /* XXX Ignore the number since we would need some more | |
618 | information to compute a real date. */ | |
619 | do | |
620 | ++rp; | |
621 | while (*rp >= '0' && *rp <= '9'); | |
622 | break; | |
f8adc70c | 623 | case 'U': |
5d9f13dc UD |
624 | get_number (0, 53, 2); |
625 | week_no = val; | |
626 | have_uweek = 1; | |
627 | break; | |
f8adc70c | 628 | case 'W': |
5d9f13dc UD |
629 | get_number (0, 53, 2); |
630 | week_no = val; | |
631 | have_wweek = 1; | |
632 | break; | |
633 | case 'V': | |
965e02a2 | 634 | get_number (0, 53, 2); |
5290baf0 UD |
635 | /* XXX This cannot determine any field in TM without some |
636 | information. */ | |
f8adc70c RM |
637 | break; |
638 | case 'w': | |
639 | /* Match number of weekday. */ | |
965e02a2 | 640 | get_number (0, 6, 1); |
f8adc70c | 641 | tm->tm_wday = val; |
c2cfb512 | 642 | have_wday = 1; |
f8adc70c | 643 | break; |
f8adc70c | 644 | case 'y': |
958d6807 | 645 | match_year_in_century: |
f8adc70c | 646 | /* Match year within century. */ |
965e02a2 | 647 | get_number (0, 99, 2); |
685c3086 | 648 | /* The "Year 2000: The Millennium Rollover" paper suggests that |
1cab5444 UD |
649 | values in the range 69-99 refer to the twentieth century. */ |
650 | tm->tm_year = val >= 69 ? val : val + 100; | |
685c3086 | 651 | /* Indicate that we want to use the century, if specified. */ |
a6ff34d7 | 652 | want_century = 1; |
c2cfb512 | 653 | want_xday = 1; |
f8adc70c RM |
654 | break; |
655 | case 'Y': | |
656 | /* Match year including century number. */ | |
965e02a2 | 657 | get_number (0, 9999, 4); |
0d8733c4 | 658 | tm->tm_year = val - 1900; |
a6ff34d7 | 659 | want_century = 0; |
c2cfb512 | 660 | want_xday = 1; |
f8adc70c RM |
661 | break; |
662 | case 'Z': | |
663 | /* XXX How to handle this? */ | |
664 | break; | |
665 | case 'E': | |
5290baf0 | 666 | #ifdef _NL_CURRENT |
f8adc70c RM |
667 | switch (*fmt++) |
668 | { | |
669 | case 'c': | |
670 | /* Match locale's alternate date and time format. */ | |
5290baf0 UD |
671 | if (*decided != raw) |
672 | { | |
673 | const char *fmt = _NL_CURRENT (LC_TIME, ERA_D_T_FMT); | |
674 | ||
675 | if (*fmt == '\0') | |
676 | fmt = _NL_CURRENT (LC_TIME, D_T_FMT); | |
677 | ||
678 | if (!recursive (fmt)) | |
679 | { | |
680 | if (*decided == loc) | |
681 | return NULL; | |
958d6807 UD |
682 | else |
683 | rp = rp_backup; | |
5290baf0 UD |
684 | } |
685 | else | |
686 | { | |
687 | if (strcmp (fmt, HERE_D_T_FMT)) | |
688 | *decided = loc; | |
c2cfb512 | 689 | want_xday = 1; |
5290baf0 UD |
690 | break; |
691 | } | |
692 | *decided = raw; | |
693 | } | |
694 | /* The C locale has no era information, so use the | |
695 | normal representation. */ | |
696 | if (!recursive (HERE_D_T_FMT)) | |
697 | return NULL; | |
c2cfb512 | 698 | want_xday = 1; |
f8adc70c RM |
699 | break; |
700 | case 'C': | |
958d6807 UD |
701 | if (*decided != raw) |
702 | { | |
703 | if (era_cnt >= 0) | |
704 | { | |
705 | era = _nl_select_era_entry (era_cnt); | |
706 | if (match_string (era->era_name, rp)) | |
707 | { | |
708 | *decided = loc; | |
709 | break; | |
710 | } | |
711 | else | |
712 | return NULL; | |
713 | } | |
714 | else | |
715 | { | |
716 | num_eras = _NL_CURRENT_WORD (LC_TIME, | |
717 | _NL_TIME_ERA_NUM_ENTRIES); | |
5eefad82 | 718 | for (era_cnt = 0; era_cnt < (int) num_eras; |
958d6807 UD |
719 | ++era_cnt, rp = rp_backup) |
720 | { | |
721 | era = _nl_select_era_entry (era_cnt); | |
722 | if (match_string (era->era_name, rp)) | |
723 | { | |
724 | *decided = loc; | |
725 | break; | |
726 | } | |
727 | } | |
5eefad82 | 728 | if (era_cnt == (int) num_eras) |
958d6807 UD |
729 | { |
730 | era_cnt = -1; | |
731 | if (*decided == loc) | |
732 | return NULL; | |
733 | } | |
734 | else | |
735 | break; | |
736 | } | |
737 | ||
738 | *decided = raw; | |
739 | } | |
740 | /* The C locale has no era information, so use the | |
741 | normal representation. */ | |
742 | goto match_century; | |
743 | case 'y': | |
744 | if (*decided == raw) | |
745 | goto match_year_in_century; | |
746 | ||
747 | get_number(0, 9999, 4); | |
748 | tm->tm_year = val; | |
749 | want_era = 1; | |
750 | want_xday = 1; | |
2b15132f | 751 | want_century = 1; |
958d6807 | 752 | break; |
f8adc70c | 753 | case 'Y': |
958d6807 UD |
754 | if (*decided != raw) |
755 | { | |
756 | num_eras = _NL_CURRENT_WORD (LC_TIME, | |
757 | _NL_TIME_ERA_NUM_ENTRIES); | |
5eefad82 | 758 | for (era_cnt = 0; era_cnt < (int) num_eras; |
958d6807 UD |
759 | ++era_cnt, rp = rp_backup) |
760 | { | |
761 | era = _nl_select_era_entry (era_cnt); | |
762 | if (recursive (era->era_format)) | |
763 | break; | |
764 | } | |
5eefad82 | 765 | if (era_cnt == (int) num_eras) |
958d6807 UD |
766 | { |
767 | era_cnt = -1; | |
768 | if (*decided == loc) | |
769 | return NULL; | |
770 | else | |
771 | rp = rp_backup; | |
772 | } | |
773 | else | |
774 | { | |
775 | *decided = loc; | |
776 | era_cnt = -1; | |
777 | break; | |
778 | } | |
779 | ||
780 | *decided = raw; | |
781 | } | |
782 | get_number (0, 9999, 4); | |
783 | tm->tm_year = val - 1900; | |
784 | want_century = 0; | |
785 | want_xday = 1; | |
f8adc70c RM |
786 | break; |
787 | case 'x': | |
5290baf0 UD |
788 | if (*decided != raw) |
789 | { | |
790 | const char *fmt = _NL_CURRENT (LC_TIME, ERA_D_FMT); | |
791 | ||
792 | if (*fmt == '\0') | |
793 | fmt = _NL_CURRENT (LC_TIME, D_FMT); | |
794 | ||
795 | if (!recursive (fmt)) | |
796 | { | |
797 | if (*decided == loc) | |
798 | return NULL; | |
958d6807 UD |
799 | else |
800 | rp = rp_backup; | |
5290baf0 UD |
801 | } |
802 | else | |
803 | { | |
804 | if (strcmp (fmt, HERE_D_FMT)) | |
805 | *decided = loc; | |
806 | break; | |
807 | } | |
808 | *decided = raw; | |
809 | } | |
810 | if (!recursive (HERE_D_FMT)) | |
811 | return NULL; | |
f8adc70c RM |
812 | break; |
813 | case 'X': | |
5290baf0 UD |
814 | if (*decided != raw) |
815 | { | |
816 | const char *fmt = _NL_CURRENT (LC_TIME, ERA_T_FMT); | |
817 | ||
818 | if (*fmt == '\0') | |
819 | fmt = _NL_CURRENT (LC_TIME, T_FMT); | |
820 | ||
821 | if (!recursive (fmt)) | |
822 | { | |
823 | if (*decided == loc) | |
824 | return NULL; | |
958d6807 UD |
825 | else |
826 | rp = rp_backup; | |
5290baf0 UD |
827 | } |
828 | else | |
829 | { | |
830 | if (strcmp (fmt, HERE_T_FMT)) | |
831 | *decided = loc; | |
832 | break; | |
833 | } | |
834 | *decided = raw; | |
835 | } | |
836 | if (!recursive (HERE_T_FMT)) | |
837 | return NULL; | |
f8adc70c RM |
838 | break; |
839 | default: | |
840 | return NULL; | |
841 | } | |
842 | break; | |
5290baf0 UD |
843 | #else |
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. */ | |
849 | return NULL; | |
850 | ||
851 | goto start_over; | |
852 | #endif | |
f8adc70c RM |
853 | case 'O': |
854 | switch (*fmt++) | |
855 | { | |
856 | case 'd': | |
857 | case 'e': | |
858 | /* Match day of month using alternate numeric symbols. */ | |
965e02a2 | 859 | get_alt_number (1, 31, 2); |
f8adc70c | 860 | tm->tm_mday = val; |
8d93eb92 | 861 | have_mday = 1; |
c2cfb512 | 862 | want_xday = 1; |
f8adc70c RM |
863 | break; |
864 | case 'H': | |
865 | /* Match hour in 24-hour clock using alternate numeric | |
866 | symbols. */ | |
965e02a2 | 867 | get_alt_number (0, 23, 2); |
f8adc70c RM |
868 | tm->tm_hour = val; |
869 | have_I = 0; | |
870 | break; | |
871 | case 'I': | |
872 | /* Match hour in 12-hour clock using alternate numeric | |
873 | symbols. */ | |
965e02a2 | 874 | get_alt_number (1, 12, 2); |
2b15132f | 875 | tm->tm_hour = val % 12; |
f8adc70c RM |
876 | have_I = 1; |
877 | break; | |
878 | case 'm': | |
879 | /* Match month using alternate numeric symbols. */ | |
965e02a2 | 880 | get_alt_number (1, 12, 2); |
f8adc70c | 881 | tm->tm_mon = val - 1; |
8d93eb92 | 882 | have_mon = 1; |
c2cfb512 | 883 | want_xday = 1; |
f8adc70c RM |
884 | break; |
885 | case 'M': | |
886 | /* Match minutes using alternate numeric symbols. */ | |
965e02a2 | 887 | get_alt_number (0, 59, 2); |
f8adc70c RM |
888 | tm->tm_min = val; |
889 | break; | |
890 | case 'S': | |
891 | /* Match seconds using alternate numeric symbols. */ | |
965e02a2 | 892 | get_alt_number (0, 61, 2); |
f8adc70c RM |
893 | tm->tm_sec = val; |
894 | break; | |
895 | case 'U': | |
5d9f13dc UD |
896 | get_alt_number (0, 53, 2); |
897 | week_no = val; | |
898 | have_uweek = 1; | |
899 | break; | |
f8adc70c | 900 | case 'W': |
5d9f13dc UD |
901 | get_alt_number (0, 53, 2); |
902 | week_no = val; | |
903 | have_wweek = 1; | |
904 | break; | |
905 | case 'V': | |
965e02a2 | 906 | get_alt_number (0, 53, 2); |
5290baf0 UD |
907 | /* XXX This cannot determine any field in TM without |
908 | further information. */ | |
f8adc70c RM |
909 | break; |
910 | case 'w': | |
911 | /* Match number of weekday using alternate numeric symbols. */ | |
965e02a2 | 912 | get_alt_number (0, 6, 1); |
f8adc70c | 913 | tm->tm_wday = val; |
c2cfb512 | 914 | have_wday = 1; |
f8adc70c RM |
915 | break; |
916 | case 'y': | |
917 | /* Match year within century using alternate numeric symbols. */ | |
965e02a2 | 918 | get_alt_number (0, 99, 2); |
1618c590 | 919 | tm->tm_year = val >= 69 ? val : val + 100; |
c2cfb512 | 920 | want_xday = 1; |
f8adc70c RM |
921 | break; |
922 | default: | |
923 | return NULL; | |
924 | } | |
925 | break; | |
926 | default: | |
927 | return NULL; | |
928 | } | |
929 | } | |
930 | ||
931 | if (have_I && is_pm) | |
932 | tm->tm_hour += 12; | |
d41c6f61 | 933 | |
450f4601 UD |
934 | if (century != -1) |
935 | { | |
936 | if (want_century) | |
937 | tm->tm_year = tm->tm_year % 100 + (century - 19) * 100; | |
938 | else | |
939 | /* Only the century, but not the year. Strange, but so be it. */ | |
940 | tm->tm_year = (century - 19) * 100; | |
941 | } | |
a6ff34d7 | 942 | |
958d6807 UD |
943 | if (era_cnt != -1) |
944 | { | |
2b15132f | 945 | era = _nl_select_era_entry (era_cnt); |
958d6807 UD |
946 | if (want_era) |
947 | tm->tm_year = (era->start_date[0] | |
948 | + ((tm->tm_year - era->offset) | |
949 | * era->absolute_direction)); | |
950 | else | |
951 | /* Era start year assumed. */ | |
952 | tm->tm_year = era->start_date[0]; | |
953 | } | |
954 | else | |
955 | if (want_era) | |
2b15132f UD |
956 | { |
957 | /* No era found but we have seen an E modifier. Rectify some | |
958 | values. */ | |
959 | if (want_century && century == -1 && tm->tm_year < 69) | |
960 | tm->tm_year += 100; | |
961 | } | |
958d6807 UD |
962 | |
963 | if (want_xday && !have_wday) | |
964 | { | |
965 | if ( !(have_mon && have_mday) && have_yday) | |
966 | { | |
967 | /* We don't have tm_mon and/or tm_mday, compute them. */ | |
8d93eb92 UD |
968 | int t_mon = 0; |
969 | while (__mon_yday[__isleap(1900 + tm->tm_year)][t_mon] <= tm->tm_yday) | |
970 | t_mon++; | |
971 | if (!have_mon) | |
972 | tm->tm_mon = t_mon - 1; | |
973 | if (!have_mday) | |
958d6807 UD |
974 | tm->tm_mday = |
975 | (tm->tm_yday | |
976 | - __mon_yday[__isleap(1900 + tm->tm_year)][t_mon - 1] + 1); | |
977 | } | |
8d93eb92 | 978 | day_of_the_week (tm); |
958d6807 | 979 | } |
2b15132f | 980 | |
c2cfb512 UD |
981 | if (want_xday && !have_yday) |
982 | day_of_the_year (tm); | |
2b15132f | 983 | |
5d9f13dc UD |
984 | if ((have_uweek || have_wweek) && have_wday) |
985 | { | |
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; | |
990 | ||
991 | tm->tm_mday = 1; | |
992 | tm->tm_mon = 0; | |
993 | day_of_the_week (tm); | |
994 | if (have_mday) | |
995 | tm->tm_mday = save_mday; | |
996 | if (have_mon) | |
997 | tm->tm_mon = save_mon; | |
998 | ||
999 | if (!have_yday) | |
1000 | tm->tm_yday = ((7 - (tm->tm_wday - w_offset)) % 7 | |
1001 | + (week_no - 1) *7 | |
1002 | + save_wday - w_offset); | |
1003 | ||
1004 | if (!have_mday || !have_mon) | |
1005 | { | |
1006 | int t_mon = 0; | |
1007 | while (__mon_yday[__isleap(1900 + tm->tm_year)][t_mon] | |
1008 | <= tm->tm_yday) | |
1009 | t_mon++; | |
1010 | if (!have_mon) | |
1011 | tm->tm_mon = t_mon - 1; | |
1012 | if (!have_mday) | |
1013 | tm->tm_mday = | |
1014 | (tm->tm_yday | |
1015 | - __mon_yday[__isleap(1900 + tm->tm_year)][t_mon - 1] + 1); | |
1016 | } | |
1017 | ||
1018 | tm->tm_wday = save_wday; | |
1019 | } | |
c2cfb512 | 1020 | |
f8adc70c RM |
1021 | return (char *) rp; |
1022 | } | |
5290baf0 UD |
1023 | |
1024 | ||
1025 | char * | |
1026 | strptime (buf, format, tm) | |
1027 | const char *buf; | |
1028 | const char *format; | |
1029 | struct tm *tm; | |
1030 | { | |
1031 | enum locale_status decided; | |
958d6807 | 1032 | |
5290baf0 UD |
1033 | #ifdef _NL_CURRENT |
1034 | decided = not; | |
1035 | #else | |
1036 | decided = raw; | |
1037 | #endif | |
958d6807 | 1038 | return strptime_internal (buf, format, tm, &decided, -1); |
5290baf0 | 1039 | } |