1 /* Convert a string representation of time to a time value.
2 Copyright (C) 1996, 1997, 1998, 1999 Free Software Foundation, Inc.
3 This file is part of the GNU C Library.
4 Contributed by Ulrich Drepper <drepper@cygnus.com>, 1996.
5
6 The GNU C Library is free software; you can redistribute it and/or
7 modify it under the terms of the GNU Library General Public License as
8 published by the Free Software Foundation; either version 2 of the
9 License, or (at your option) any later version.
10
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 Library General Public License for more details.
15
16 You should have received a copy of the GNU Library General Public
17 License along with the GNU C Library; see the file COPYING.LIB. If not,
18 write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19 Boston, MA 02110-1301 USA. */
20
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
30 #include <ctype.h>
31 //#include <langinfo.h>
32 #include <limits.h>
33 #include <string.h>
34 #include <time.h>
35
36 // ABI EXPORT
37 #include "ut_export.h"
38
UT_strncasecmp(const char * s1,const char * s2,size_t n)39 static int UT_strncasecmp (const char *s1, const char *s2, size_t n)
40 {
41 int ret;
42
43 if (n == 0)
44 return 0;
45
46 while ((ret = ((unsigned char) tolower (*s1) - (unsigned char) tolower (*s2))) == 0 && *s1++)
47 {
48 if (--n == 0)
49 return 0;
50 ++s2;
51 }
52 return ret;
53 }
54
55 #ifdef _LIBC
56 # include "../locale/localeinfo.h"
57 #endif
58
59
60 #ifndef __P
61 # if defined (__GNUC__) || (defined (__STDC__) && __STDC__)
62 # define __P(args) args
63 # else
64 # define __P(args) ()
65 # endif /* GCC. */
66 #endif /* Not __P. */
67
68
69 #if ! HAVE_LOCALTIME_R && ! defined localtime_r
70 # ifdef _LIBC
71 # define localtime_r __localtime_r
72 # else
73 /* Approximate localtime_r as best we can in its absence. */
74 # define localtime_r my_localtime_r
75 static struct tm *localtime_r (const time_t *, struct tm *);
localtime_r(const time_t * t,struct tm * tp)76 static struct tm * localtime_r (const time_t *t, struct tm *tp)
77 {
78 struct tm *l = localtime (t);
79 if (! l)
80 return 0;
81 *tp = *l;
82 return tp;
83 }
84 # endif /* ! _LIBC */
85 #endif /* ! HAVE_LOCALTIME_R && ! defined (localtime_r) */
86
87 #define match_char(ch1, ch2) if (ch1 != ch2) return NULL
88 #if defined __GNUC__ && __GNUC__ >= 2
89 # define match_string(cs1, s2) \
90 ({ size_t len = strlen (cs1); \
91 int result = UT_strncasecmp ((cs1), (s2), len) == 0; \
92 if (result) (s2) += len; \
93 result; })
94 #else
95 /* Oh come on. Get a reasonable compiler. */
96 # define match_string(cs1, s2) \
97 (UT_strncasecmp ((cs1), (s2), strlen (cs1)) ? 0 : ((s2) += strlen (cs1), 1))
98 #endif
99 /* We intentionally do not use isdigit() for testing because this will
100 lead to problems with the wide character version. */
101 #define get_number(from, to, n) \
102 do { \
103 int __n = n; \
104 val = 0; \
105 while (*rp == ' ') \
106 ++rp; \
107 if (*rp < '0' || *rp > '9') \
108 return NULL; \
109 do { \
110 val *= 10; \
111 val += *rp++ - '0'; \
112 } while (--__n > 0 && val * 10 <= to && *rp >= '0' && *rp <= '9'); \
113 if (val < from || val > to) \
114 return NULL; \
115 } while (0)
116 #ifdef _NL_CURRENT
117 # define get_alt_number(from, to, n) \
118 ({ \
119 __label__ do_normal; \
120 if (*decided != raw) \
121 { \
122 const char *alts = _NL_CURRENT (LC_TIME, ALT_DIGITS); \
123 int __n = n; \
124 int any = 0; \
125 while (*rp == ' ') \
126 ++rp; \
127 val = 0; \
128 do { \
129 val *= 10; \
130 while (*alts != '\0') \
131 { \
132 size_t len = strlen (alts); \
133 if (UT_strncasecmp (alts, rp, len) == 0) \
134 break; \
135 alts += len + 1; \
136 ++val; \
137 } \
138 if (*alts == '\0') \
139 { \
140 if (*decided == not && ! any) \
141 goto do_normal; \
142 /* If we haven't read anything it's an error. */ \
143 if (! any) \
144 return NULL; \
145 /* Correct the premature multiplication. */ \
146 val /= 10; \
147 break; \
148 } \
149 else \
150 *decided = loc; \
151 } while (--__n > 0 && val * 10 <= to); \
152 if (val < from || val > to) \
153 return NULL; \
154 } \
155 else \
156 { \
157 do_normal: \
158 get_number (from, to, n); \
159 } \
160 0; \
161 })
162 #else
163 # define get_alt_number(from, to, n) \
164 /* We don't have the alternate representation. */ \
165 get_number(from, to, n)
166 #endif
167 #define recursive(new_fmt) \
168 (*(new_fmt) != '\0' \
169 && (rp = UT_strptime_internal (rp, (new_fmt), tm, decided)) != NULL)
170
171
172 #ifdef _LIBC
173 /* This is defined in locale/C-time.c in the GNU libc. */
174 extern const struct locale_data _nl_C_LC_TIME;
175 extern const unsigned short int __mon_yday[2][13];
176
177 # define weekday_name (&_nl_C_LC_TIME.values[_NL_ITEM_INDEX (DAY_1)].string)
178 # define ab_weekday_name \
179 (&_nl_C_LC_TIME.values[_NL_ITEM_INDEX (ABDAY_1)].string)
180 # define month_name (&_nl_C_LC_TIME.values[_NL_ITEM_INDEX (MON_1)].string)
181 # define ab_month_name (&_nl_C_LC_TIME.values[_NL_ITEM_INDEX (ABMON_1)].string)
182 # define HERE_D_T_FMT (_nl_C_LC_TIME.values[_NL_ITEM_INDEX (D_T_FMT)].string)
183 # define HERE_D_FMT (_nl_C_LC_TIME.values[_NL_ITEM_INDEX (D_FMT)].string)
184 # define HERE_AM_STR (_nl_C_LC_TIME.values[_NL_ITEM_INDEX (AM_STR)].string)
185 # define HERE_PM_STR (_nl_C_LC_TIME.values[_NL_ITEM_INDEX (PM_STR)].string)
186 # define HERE_T_FMT_AMPM \
187 (_nl_C_LC_TIME.values[_NL_ITEM_INDEX (T_FMT_AMPM)].string)
188 # define HERE_T_FMT (_nl_C_LC_TIME.values[_NL_ITEM_INDEX (T_FMT)].string)
189
190 # define UT_strncasecmp(s1, s2, n) __UT_strncasecmp (s1, s2, n)
191 #else
192 static char const weekday_name[][10] =
193 {
194 "Sunday", "Monday", "Tuesday", "Wednesday",
195 "Thursday", "Friday", "Saturday"
196 };
197 static char const ab_weekday_name[][4] =
198 {
199 "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
200 };
201 static char const month_name[][10] =
202 {
203 "January", "February", "March", "April", "May", "June",
204 "July", "August", "September", "October", "November", "December"
205 };
206 static char const ab_month_name[][4] =
207 {
208 "Jan", "Feb", "Mar", "Apr", "May", "Jun",
209 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
210 };
211 # define HERE_D_T_FMT "%a %b %e %H:%M:%S %Y"
212 # define HERE_D_FMT "%m/%d/%y"
213 # define HERE_AM_STR "AM"
214 # define HERE_PM_STR "PM"
215 # define HERE_T_FMT_AMPM "%I:%M:%S %p"
216 # define HERE_T_FMT "%H:%M:%S"
217
218 const unsigned short int __mon_yday[13][13] =
219 {
220 /* Normal years. */
221 { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 },
222 /* Leap years. */
223 { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 }
224 };
225 #endif
226
227 /* Status of lookup: do we use the locale data or the raw data? */
228 enum locale_status { not, loc, raw };
229
230
231 #ifndef __isleap
232 /* Nonzero if YEAR is a leap year (every 4 years,
233 except every 100th isn't, and every 400th is). */
234 # define __isleap(year) \
235 ((year) % 4 == 0 && ((year) % 100 != 0 || (year) % 400 == 0))
236 #endif
237
238 /* Compute the day of the week. */
239 static void
day_of_the_week(struct tm * tm)240 day_of_the_week (struct tm *tm)
241 {
242 /* We know that January 1st 1970 was a Thursday (= 4). Compute the
243 the difference between this data in the one on TM and so determine
244 the weekday. */
245 int corr_year = 1900 + tm->tm_year - (tm->tm_mon < 2);
246 int wday = (-473
247 + (365 * (tm->tm_year - 70))
248 + (corr_year / 4)
249 - ((corr_year / 4) / 25) + ((corr_year / 4) % 25 < 0)
250 + (((corr_year / 4) / 25) / 4)
251 + __mon_yday[0][tm->tm_mon]
252 + tm->tm_mday - 1);
253 tm->tm_wday = wday % 7;
254 }
255
256 /* Compute the day of the year. */
257 static void
day_of_the_year(struct tm * tm)258 day_of_the_year (struct tm *tm)
259 {
260 tm->tm_yday = (__mon_yday[__isleap (1900 + tm->tm_year)][tm->tm_mon]
261 + (tm->tm_mday - 1));
262 }
263
264 static char *
265 #ifdef _LIBC
266 internal_function
267 #endif
268 UT_strptime_internal (const char *buf, const char *format, struct tm *tm, enum locale_status *decided);
269
270 static char *
271 #ifdef _LIBC
272 internal_function
273 #endif
UT_strptime_internal(const char * buf,const char * format,struct tm * tm,enum locale_status * decided)274 UT_strptime_internal (const char *buf, const char *format, struct tm *tm, enum locale_status *decided)
275 {
276 const char *rp;
277 const char *fmt;
278 int cnt;
279 size_t val;
280 int have_I, is_pm;
281 int century, want_century;
282 int have_wday, want_xday;
283 int have_yday;
284 int have_mon, have_mday;
285
286 rp = buf;
287 fmt = format;
288 have_I = is_pm = 0;
289 century = -1;
290 want_century = 0;
291 have_wday = want_xday = have_yday = have_mon = have_mday = 0;
292
293 while (*fmt != '\0')
294 {
295 /* A white space in the format string matches 0 more or white
296 space in the input string. */
297 if (isspace (*fmt))
298 {
299 while (isspace (*rp))
300 ++rp;
301 ++fmt;
302 continue;
303 }
304
305 /* Any character but `%' must be matched by the same character
306 in the iput string. */
307 if (*fmt != '%')
308 {
309 match_char (*fmt++, *rp++);
310 continue;
311 }
312
313 ++fmt;
314 #ifndef _NL_CURRENT
315 /* We need this for handling the `E' modifier. */
316 start_over:
317 #endif
318 switch (*fmt++)
319 {
320 case '%':
321 /* Match the `%' character itself. */
322 match_char ('%', *rp++);
323 break;
324 case 'a':
325 case 'A':
326 /* Match day of week. */
327 for (cnt = 0; cnt < 7; ++cnt)
328 {
329 #ifdef _NL_CURRENT
330 if (*decided !=raw)
331 {
332 if (match_string (_NL_CURRENT (LC_TIME, DAY_1 + cnt), rp))
333 {
334 if (*decided == not
335 && strcmp (_NL_CURRENT (LC_TIME, DAY_1 + cnt),
336 weekday_name[cnt]))
337 *decided = loc;
338 break;
339 }
340 if (match_string (_NL_CURRENT (LC_TIME, ABDAY_1 + cnt), rp))
341 {
342 if (*decided == not
343 && strcmp (_NL_CURRENT (LC_TIME, ABDAY_1 + cnt),
344 ab_weekday_name[cnt]))
345 *decided = loc;
346 break;
347 }
348 }
349 #endif
350 if (*decided != loc
351 && (match_string (weekday_name[cnt], rp)
352 || match_string (ab_weekday_name[cnt], rp)))
353 {
354 *decided = raw;
355 break;
356 }
357 }
358 if (cnt == 7)
359 /* Does not match a weekday name. */
360 return NULL;
361 tm->tm_wday = cnt;
362 have_wday = 1;
363 break;
364 case 'b':
365 case 'B':
366 case 'h':
367 /* Match month name. */
368 for (cnt = 0; cnt < 12; ++cnt)
369 {
370 #ifdef _NL_CURRENT
371 if (*decided !=raw)
372 {
373 if (match_string (_NL_CURRENT (LC_TIME, MON_1 + cnt), rp))
374 {
375 if (*decided == not
376 && strcmp (_NL_CURRENT (LC_TIME, MON_1 + cnt),
377 month_name[cnt]))
378 *decided = loc;
379 break;
380 }
381 if (match_string (_NL_CURRENT (LC_TIME, ABMON_1 + cnt), rp))
382 {
383 if (*decided == not
384 && strcmp (_NL_CURRENT (LC_TIME, ABMON_1 + cnt),
385 ab_month_name[cnt]))
386 *decided = loc;
387 break;
388 }
389 }
390 #endif
391 if (match_string (month_name[cnt], rp)
392 || match_string (ab_month_name[cnt], rp))
393 {
394 *decided = raw;
395 break;
396 }
397 }
398 if (cnt == 12)
399 /* Does not match a month name. */
400 return NULL;
401 tm->tm_mon = cnt;
402 want_xday = 1;
403 break;
404 case 'c':
405 /* Match locale's date and time format. */
406 #ifdef _NL_CURRENT
407 if (*decided != raw)
408 {
409 if (!recursive (_NL_CURRENT (LC_TIME, D_T_FMT)))
410 {
411 if (*decided == loc)
412 return NULL;
413 }
414 else
415 {
416 if (*decided == not &&
417 strcmp (_NL_CURRENT (LC_TIME, D_T_FMT), HERE_D_T_FMT))
418 *decided = loc;
419 want_xday = 1;
420 break;
421 }
422 *decided = raw;
423 }
424 #endif
425 if (!recursive (HERE_D_T_FMT))
426 return NULL;
427 want_xday = 1;
428 break;
429 case 'C':
430 /* Match century number. */
431 get_number (0, 99, 2);
432 century = val;
433 want_xday = 1;
434 break;
435 case 'd':
436 case 'e':
437 /* Match day of month. */
438 get_number (1, 31, 2);
439 tm->tm_mday = val;
440 have_mday = 1;
441 want_xday = 1;
442 break;
443 case 'F':
444 if (!recursive ("%Y-%m-%d"))
445 return NULL;
446 want_xday = 1;
447 break;
448 case 'x':
449 #ifdef _NL_CURRENT
450 if (*decided != raw)
451 {
452 if (!recursive (_NL_CURRENT (LC_TIME, D_FMT)))
453 {
454 if (*decided == loc)
455 return NULL;
456 }
457 else
458 {
459 if (decided == not
460 && strcmp (_NL_CURRENT (LC_TIME, D_FMT), HERE_D_FMT))
461 *decided = loc;
462 want_xday = 1;
463 break;
464 }
465 *decided = raw;
466 }
467 #endif
468 /* Fall through. */
469 case 'D':
470 /* Match standard day format. */
471 if (!recursive (HERE_D_FMT))
472 return NULL;
473 want_xday = 1;
474 break;
475 case 'k':
476 case 'H':
477 /* Match hour in 24-hour clock. */
478 get_number (0, 23, 2);
479 tm->tm_hour = val;
480 have_I = 0;
481 break;
482 case 'I':
483 /* Match hour in 12-hour clock. */
484 get_number (1, 12, 2);
485 tm->tm_hour = val % 12;
486 have_I = 1;
487 break;
488 case 'j':
489 /* Match day number of year. */
490 get_number (1, 366, 3);
491 tm->tm_yday = val - 1;
492 have_yday = 1;
493 break;
494 case 'm':
495 /* Match number of month. */
496 get_number (1, 12, 2);
497 tm->tm_mon = val - 1;
498 have_mon = 1;
499 want_xday = 1;
500 break;
501 case 'M':
502 /* Match minute. */
503 get_number (0, 59, 2);
504 tm->tm_min = val;
505 break;
506 case 'n':
507 case 't':
508 /* Match any white space. */
509 while (isspace (*rp))
510 ++rp;
511 break;
512 case 'p':
513 /* Match locale's equivalent of AM/PM. */
514 #ifdef _NL_CURRENT
515 if (*decided != raw)
516 {
517 if (match_string (_NL_CURRENT (LC_TIME, AM_STR), rp))
518 {
519 if (strcmp (_NL_CURRENT (LC_TIME, AM_STR), HERE_AM_STR))
520 *decided = loc;
521 break;
522 }
523 if (match_string (_NL_CURRENT (LC_TIME, PM_STR), rp))
524 {
525 if (strcmp (_NL_CURRENT (LC_TIME, PM_STR), HERE_PM_STR))
526 *decided = loc;
527 is_pm = 1;
528 break;
529 }
530 *decided = raw;
531 }
532 #endif
533 if (!match_string (HERE_AM_STR, rp))
534 {
535 if (match_string (HERE_PM_STR, rp))
536 is_pm = 1;
537 else
538 return NULL;
539 }
540 break;
541 case 'r':
542 #ifdef _NL_CURRENT
543 if (*decided != raw)
544 {
545 if (!recursive (_NL_CURRENT (LC_TIME, T_FMT_AMPM)))
546 {
547 if (*decided == loc)
548 return NULL;
549 }
550 else
551 {
552 if (*decided == not &&
553 strcmp (_NL_CURRENT (LC_TIME, T_FMT_AMPM),
554 HERE_T_FMT_AMPM))
555 *decided = loc;
556 break;
557 }
558 *decided = raw;
559 }
560 #endif
561 if (!recursive (HERE_T_FMT_AMPM))
562 return NULL;
563 break;
564 case 'R':
565 if (!recursive ("%H:%M"))
566 return NULL;
567 break;
568 case 's':
569 {
570 /* The number of seconds may be very high so we cannot use
571 the `get_number' macro. Instead read the number
572 character for character and construct the result while
573 doing this. */
574 time_t secs = 0;
575 if (*rp < '0' || *rp > '9')
576 /* We need at least one digit. */
577 return NULL;
578
579 do
580 {
581 secs *= 10;
582 secs += *rp++ - '0';
583 }
584 while (*rp >= '0' && *rp <= '9');
585
586 if (localtime_r (&secs, tm) == NULL)
587 /* Error in function. */
588 return NULL;
589 }
590 break;
591 case 'S':
592 get_number (0, 61, 2);
593 tm->tm_sec = val;
594 break;
595 case 'X':
596 #ifdef _NL_CURRENT
597 if (*decided != raw)
598 {
599 if (!recursive (_NL_CURRENT (LC_TIME, T_FMT)))
600 {
601 if (*decided == loc)
602 return NULL;
603 }
604 else
605 {
606 if (strcmp (_NL_CURRENT (LC_TIME, T_FMT), HERE_T_FMT))
607 *decided = loc;
608 break;
609 }
610 *decided = raw;
611 }
612 #endif
613 /* Fall through. */
614 case 'T':
615 if (!recursive (HERE_T_FMT))
616 return NULL;
617 break;
618 case 'u':
619 get_number (1, 7, 1);
620 tm->tm_wday = val % 7;
621 have_wday = 1;
622 break;
623 case 'g':
624 get_number (0, 99, 2);
625 /* XXX This cannot determine any field in TM. */
626 break;
627 case 'G':
628 if (*rp < '0' || *rp > '9')
629 return NULL;
630 /* XXX Ignore the number since we would need some more
631 information to compute a real date. */
632 do
633 ++rp;
634 while (*rp >= '0' && *rp <= '9');
635 break;
636 case 'U':
637 case 'V':
638 case 'W':
639 get_number (0, 53, 2);
640 /* XXX This cannot determine any field in TM without some
641 information. */
642 break;
643 case 'w':
644 /* Match number of weekday. */
645 get_number (0, 6, 1);
646 tm->tm_wday = val;
647 have_wday = 1;
648 break;
649 case 'y':
650 /* Match year within century. */
651 get_number (0, 99, 2);
652 /* The "Year 2000: The Millennium Rollover" paper suggests that
653 values in the range 69-99 refer to the twentieth century. */
654 tm->tm_year = val >= 69 ? val : val + 100;
655 /* Indicate that we want to use the century, if specified. */
656 want_century = 1;
657 want_xday = 1;
658 break;
659 case 'Y':
660 /* Match year including century number. */
661 get_number (0, 9999, 4);
662 tm->tm_year = val - 1900;
663 want_century = 0;
664 want_xday = 1;
665 break;
666 case 'Z':
667 /* XXX How to handle this? */
668 break;
669 case 'E':
670 #ifdef _NL_CURRENT
671 switch (*fmt++)
672 {
673 case 'c':
674 /* Match locale's alternate date and time format. */
675 if (*decided != raw)
676 {
677 const char *fmt = _NL_CURRENT (LC_TIME, ERA_D_T_FMT);
678
679 if (*fmt == '\0')
680 fmt = _NL_CURRENT (LC_TIME, D_T_FMT);
681
682 if (!recursive (fmt))
683 {
684 if (*decided == loc)
685 return NULL;
686 }
687 else
688 {
689 if (strcmp (fmt, HERE_D_T_FMT))
690 *decided = loc;
691 want_xday = 1;
692 break;
693 }
694 *decided = raw;
695 }
696 /* The C locale has no era information, so use the
697 normal representation. */
698 if (!recursive (HERE_D_T_FMT))
699 return NULL;
700 want_xday = 1;
701 break;
702 case 'C':
703 case 'y':
704 case 'Y':
705 /* Match name of base year in locale's alternate
706 representation. */
707 /* XXX This is currently not implemented. It should
708 use the value _NL_CURRENT (LC_TIME, ERA). */
709 break;
710 case 'x':
711 if (*decided != raw)
712 {
713 const char *fmt = _NL_CURRENT (LC_TIME, ERA_D_FMT);
714
715 if (*fmt == '\0')
716 fmt = _NL_CURRENT (LC_TIME, D_FMT);
717
718 if (!recursive (fmt))
719 {
720 if (*decided == loc)
721 return NULL;
722 }
723 else
724 {
725 if (strcmp (fmt, HERE_D_FMT))
726 *decided = loc;
727 break;
728 }
729 *decided = raw;
730 }
731 if (!recursive (HERE_D_FMT))
732 return NULL;
733 break;
734 case 'X':
735 if (*decided != raw)
736 {
737 const char *fmt = _NL_CURRENT (LC_TIME, ERA_T_FMT);
738
739 if (*fmt == '\0')
740 fmt = _NL_CURRENT (LC_TIME, T_FMT);
741
742 if (!recursive (fmt))
743 {
744 if (*decided == loc)
745 return NULL;
746 }
747 else
748 {
749 if (strcmp (fmt, HERE_T_FMT))
750 *decided = loc;
751 break;
752 }
753 *decided = raw;
754 }
755 if (!recursive (HERE_T_FMT))
756 return NULL;
757 break;
758 default:
759 return NULL;
760 }
761 break;
762 #else
763 /* We have no information about the era format. Just use
764 the normal format. */
765 if (*fmt != 'c' && *fmt != 'C' && *fmt != 'y' && *fmt != 'Y'
766 && *fmt != 'x' && *fmt != 'X')
767 /* This is an illegal format. */
768 return NULL;
769
770 goto start_over;
771 #endif
772 case 'O':
773 switch (*fmt++)
774 {
775 case 'd':
776 case 'e':
777 /* Match day of month using alternate numeric symbols. */
778 get_alt_number (1, 31, 2);
779 tm->tm_mday = val;
780 have_mday = 1;
781 want_xday = 1;
782 break;
783 case 'H':
784 /* Match hour in 24-hour clock using alternate numeric
785 symbols. */
786 get_alt_number (0, 23, 2);
787 tm->tm_hour = val;
788 have_I = 0;
789 break;
790 case 'I':
791 /* Match hour in 12-hour clock using alternate numeric
792 symbols. */
793 get_alt_number (1, 12, 2);
794 tm->tm_hour = val - 1;
795 have_I = 1;
796 break;
797 case 'm':
798 /* Match month using alternate numeric symbols. */
799 get_alt_number (1, 12, 2);
800 tm->tm_mon = val - 1;
801 have_mon = 1;
802 want_xday = 1;
803 break;
804 case 'M':
805 /* Match minutes using alternate numeric symbols. */
806 get_alt_number (0, 59, 2);
807 tm->tm_min = val;
808 break;
809 case 'S':
810 /* Match seconds using alternate numeric symbols. */
811 get_alt_number (0, 61, 2);
812 tm->tm_sec = val;
813 break;
814 case 'U':
815 case 'V':
816 case 'W':
817 get_alt_number (0, 53, 2);
818 /* XXX This cannot determine any field in TM without
819 further information. */
820 break;
821 case 'w':
822 /* Match number of weekday using alternate numeric symbols. */
823 get_alt_number (0, 6, 1);
824 tm->tm_wday = val;
825 have_wday = 1;
826 break;
827 case 'y':
828 /* Match year within century using alternate numeric symbols. */
829 get_alt_number (0, 99, 2);
830 tm->tm_year = val >= 69 ? val : val + 100;
831 want_xday = 1;
832 break;
833 default:
834 return NULL;
835 }
836 break;
837 default:
838 return NULL;
839 }
840 }
841
842 if (have_I && is_pm)
843 tm->tm_hour += 12;
844
845 if (want_century && century != -1)
846 tm->tm_year = tm->tm_year % 100 + (century - 19) * 100;
847
848 if (want_xday && !have_wday) {
849 if ( !(have_mon && have_mday) && have_yday) {
850 /* we don't have tm_mon and/or tm_mday, compute them */
851 int t_mon = 0;
852 while (__mon_yday[__isleap(1900 + tm->tm_year)][t_mon] <= tm->tm_yday)
853 t_mon++;
854 if (!have_mon)
855 tm->tm_mon = t_mon - 1;
856 if (!have_mday)
857 tm->tm_mday = tm->tm_yday - __mon_yday[__isleap(1900 + tm->tm_year)][t_mon - 1] + 1;
858 }
859 day_of_the_week (tm);
860 }
861 if (want_xday && !have_yday)
862 day_of_the_year (tm);
863
864 return (char *) rp;
865 }
866
867 ABI_EXPORT
868 char *UT_strptime (const char *buf, const char *format, struct tm *tmtm);
869
UT_strptime(const char * buf,const char * format,struct tm * tmtm)870 char *UT_strptime (const char *buf, const char *format, struct tm *tmtm)
871 {
872 enum locale_status decided;
873 #ifdef _NL_CURRENT
874 decided = not;
875 #else
876 decided = raw;
877 #endif
878 return UT_strptime_internal (buf, format, tmtm, &decided);
879 }
880