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.
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.
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.
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., 59 Temple Place - Suite 330,
19
Boston, MA 02111-1307, USA. */
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. */
31
#ifdef HAVE_LANGINFO_H
39
# include "../locale/localeinfo.h"
44
# if defined (__GNUC__) || (defined (__STDC__) && __STDC__)
45
# define __P(args) args
52
#if ! HAVE_LOCALTIME_R && ! defined localtime_r
54
# define localtime_r __localtime_r
56
/* Approximate localtime_r as best we can in its absence. */
57
# define localtime_r my_localtime_r
58
static struct tm *localtime_r __P ((const time_t *, struct tm *));
64
struct tm *l = localtime (t);
71
#endif /* ! HAVE_LOCALTIME_R && ! defined (localtime_r) */
74
#define match_char(ch1, ch2) if (ch1 != ch2) return NULL
75
#if defined __GNUC__ && __GNUC__ >= 2
76
# define match_string(cs1, s2) \
77
({ size_t len = strlen (cs1); \
78
int result = strncasecmp ((cs1), (s2), len) == 0; \
79
if (result) (s2) += len; \
82
/* Oh come on. Get a reasonable compiler. */
83
# define match_string(cs1, s2) \
84
(strncasecmp ((cs1), (s2), strlen (cs1)) ? 0 : ((s2) += strlen (cs1), 1))
86
/* We intentionally do not use isdigit() for testing because this will
87
lead to problems with the wide character version. */
88
#define get_number(from, to, n) \
94
if (*rp < '0' || *rp > '9') \
99
} while (--__n > 0 && val * 10 <= to && *rp >= '0' && *rp <= '9'); \
100
if (val < from || val > to) \
104
# define get_alt_number(from, to, n) \
106
__label__ do_normal; \
107
if (*decided != raw) \
109
const char *alts = _NL_CURRENT (LC_TIME, ALT_DIGITS); \
117
while (*alts != '\0') \
119
size_t len = strlen (alts); \
120
if (strncasecmp (alts, rp, len) == 0) \
127
if (*decided == not && ! any) \
129
/* If we haven't read anything it's an error. */ \
132
/* Correct the premature multiplication. */ \
138
} while (--__n > 0 && val * 10 <= to); \
139
if (val < from || val > to) \
145
get_number (from, to, n); \
150
# define get_alt_number(from, to, n) \
151
/* We don't have the alternate representation. */ \
152
get_number(from, to, n)
154
#define recursive(new_fmt) \
155
(*(new_fmt) != '\0' \
156
&& (rp = strptime_internal (rp, (new_fmt), tm, decided)) != NULL)
160
/* This is defined in locale/C-time.c in the GNU libc. */
161
extern const struct locale_data _nl_C_LC_TIME;
162
extern const unsigned short int __mon_yday[2][13];
164
# define weekday_name (&_nl_C_LC_TIME.values[_NL_ITEM_INDEX (DAY_1)].string)
165
# define ab_weekday_name \
166
(&_nl_C_LC_TIME.values[_NL_ITEM_INDEX (ABDAY_1)].string)
167
# define month_name (&_nl_C_LC_TIME.values[_NL_ITEM_INDEX (MON_1)].string)
168
# define ab_month_name (&_nl_C_LC_TIME.values[_NL_ITEM_INDEX (ABMON_1)].string)
169
# define HERE_D_T_FMT (_nl_C_LC_TIME.values[_NL_ITEM_INDEX (D_T_FMT)].string)
170
# define HERE_D_FMT (_nl_C_LC_TIME.values[_NL_ITEM_INDEX (D_FMT)].string)
171
# define HERE_AM_STR (_nl_C_LC_TIME.values[_NL_ITEM_INDEX (AM_STR)].string)
172
# define HERE_PM_STR (_nl_C_LC_TIME.values[_NL_ITEM_INDEX (PM_STR)].string)
173
# define HERE_T_FMT_AMPM \
174
(_nl_C_LC_TIME.values[_NL_ITEM_INDEX (T_FMT_AMPM)].string)
175
# define HERE_T_FMT (_nl_C_LC_TIME.values[_NL_ITEM_INDEX (T_FMT)].string)
177
# define strncasecmp(s1, s2, n) __strncasecmp (s1, s2, n)
179
static char const weekday_name[][10] =
181
"Sunday", "Monday", "Tuesday", "Wednesday",
182
"Thursday", "Friday", "Saturday"
184
static char const ab_weekday_name[][4] =
186
"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
188
static char const month_name[][10] =
190
"January", "February", "March", "April", "May", "June",
191
"July", "August", "September", "October", "November", "December"
193
static char const ab_month_name[][4] =
195
"Jan", "Feb", "Mar", "Apr", "May", "Jun",
196
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
198
# define HERE_D_T_FMT "%a %b %e %H:%M:%S %Y"
199
# define HERE_D_FMT "%m/%d/%y"
200
# define HERE_AM_STR "AM"
201
# define HERE_PM_STR "PM"
202
# define HERE_T_FMT_AMPM "%I:%M:%S %p"
203
# define HERE_T_FMT "%H:%M:%S"
205
const unsigned short int __mon_yday[2][13] =
208
{ 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 },
210
{ 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 }
214
/* Status of lookup: do we use the locale data or the raw data? */
215
enum locale_status { not, loc, raw };
219
/* Nonzero if YEAR is a leap year (every 4 years,
220
except every 100th isn't, and every 400th is). */
221
# define __isleap(year) \
222
((year) % 4 == 0 && ((year) % 100 != 0 || (year) % 400 == 0))
225
/* Compute the day of the week. */
227
day_of_the_week (struct tm *tm)
229
/* We know that January 1st 1970 was a Thursday (= 4). Compute the
230
the difference between this data in the one on TM and so determine
232
int corr_year = 1900 + tm->tm_year - (tm->tm_mon < 2);
234
+ (365 * (tm->tm_year - 70))
236
- ((corr_year / 4) / 25) + ((corr_year / 4) % 25 < 0)
237
+ (((corr_year / 4) / 25) / 4)
238
+ __mon_yday[0][tm->tm_mon]
240
tm->tm_wday = wday % 7;
243
/* Compute the day of the year. */
245
day_of_the_year (struct tm *tm)
247
tm->tm_yday = (__mon_yday[__isleap (1900 + tm->tm_year)][tm->tm_mon]
248
+ (tm->tm_mday - 1));
255
strptime_internal __P ((const char *buf, const char *format, struct tm *tm,
256
enum locale_status *decided));
262
strptime_internal (buf, format, tm, decided)
266
enum locale_status *decided;
273
int century, want_century;
274
int have_wday, want_xday;
276
int have_mon, have_mday;
283
have_wday = want_xday = have_yday = have_mon = have_mday = 0;
287
/* A white space in the format string matches 0 more or white
288
space in the input string. */
291
while (isspace (*rp))
297
/* Any character but `%' must be matched by the same character
298
in the iput string. */
301
match_char (*fmt++, *rp++);
307
/* We need this for handling the `E' modifier. */
313
/* Match the `%' character itself. */
314
match_char ('%', *rp++);
318
/* Match day of week. */
319
for (cnt = 0; cnt < 7; ++cnt)
324
if (match_string (_NL_CURRENT (LC_TIME, DAY_1 + cnt), rp))
327
&& strcmp (_NL_CURRENT (LC_TIME, DAY_1 + cnt),
332
if (match_string (_NL_CURRENT (LC_TIME, ABDAY_1 + cnt), rp))
335
&& strcmp (_NL_CURRENT (LC_TIME, ABDAY_1 + cnt),
336
ab_weekday_name[cnt]))
343
&& (match_string (weekday_name[cnt], rp)
344
|| match_string (ab_weekday_name[cnt], rp)))
351
/* Does not match a weekday name. */
359
/* Match month name. */
360
for (cnt = 0; cnt < 12; ++cnt)
365
if (match_string (_NL_CURRENT (LC_TIME, MON_1 + cnt), rp))
368
&& strcmp (_NL_CURRENT (LC_TIME, MON_1 + cnt),
373
if (match_string (_NL_CURRENT (LC_TIME, ABMON_1 + cnt), rp))
376
&& strcmp (_NL_CURRENT (LC_TIME, ABMON_1 + cnt),
383
if (match_string (month_name[cnt], rp)
384
|| match_string (ab_month_name[cnt], rp))
391
/* Does not match a month name. */
397
/* Match locale's date and time format. */
401
if (!recursive (_NL_CURRENT (LC_TIME, D_T_FMT)))
408
if (*decided == not &&
409
strcmp (_NL_CURRENT (LC_TIME, D_T_FMT), HERE_D_T_FMT))
417
if (!recursive (HERE_D_T_FMT))
422
/* Match century number. */
423
get_number (0, 99, 2);
429
/* Match day of month. */
430
get_number (1, 31, 2);
436
if (!recursive ("%Y-%m-%d"))
444
if (!recursive (_NL_CURRENT (LC_TIME, D_FMT)))
452
&& strcmp (_NL_CURRENT (LC_TIME, D_FMT), HERE_D_FMT))
462
/* Match standard day format. */
463
if (!recursive (HERE_D_FMT))
469
/* Match hour in 24-hour clock. */
470
get_number (0, 23, 2);
475
/* Match hour in 12-hour clock. */
476
get_number (1, 12, 2);
477
tm->tm_hour = val % 12;
481
/* Match day number of year. */
482
get_number (1, 366, 3);
483
tm->tm_yday = val - 1;
487
/* Match number of month. */
488
get_number (1, 12, 2);
489
tm->tm_mon = val - 1;
495
get_number (0, 59, 2);
500
/* Match any white space. */
501
while (isspace (*rp))
505
/* Match locale's equivalent of AM/PM. */
509
if (match_string (_NL_CURRENT (LC_TIME, AM_STR), rp))
511
if (strcmp (_NL_CURRENT (LC_TIME, AM_STR), HERE_AM_STR))
515
if (match_string (_NL_CURRENT (LC_TIME, PM_STR), rp))
517
if (strcmp (_NL_CURRENT (LC_TIME, PM_STR), HERE_PM_STR))
525
if (!match_string (HERE_AM_STR, rp))
526
if (match_string (HERE_PM_STR, rp))
535
if (!recursive (_NL_CURRENT (LC_TIME, T_FMT_AMPM)))
542
if (*decided == not &&
543
strcmp (_NL_CURRENT (LC_TIME, T_FMT_AMPM),
551
if (!recursive (HERE_T_FMT_AMPM))
555
if (!recursive ("%H:%M"))
560
/* The number of seconds may be very high so we cannot use
561
the `get_number' macro. Instead read the number
562
character for character and construct the result while
565
if (*rp < '0' || *rp > '9')
566
/* We need at least one digit. */
574
while (*rp >= '0' && *rp <= '9');
576
if (localtime_r (&secs, tm) == NULL)
577
/* Error in function. */
582
get_number (0, 61, 2);
589
if (!recursive (_NL_CURRENT (LC_TIME, T_FMT)))
596
if (strcmp (_NL_CURRENT (LC_TIME, T_FMT), HERE_T_FMT))
605
if (!recursive (HERE_T_FMT))
609
get_number (1, 7, 1);
610
tm->tm_wday = val % 7;
614
get_number (0, 99, 2);
615
/* XXX This cannot determine any field in TM. */
618
if (*rp < '0' || *rp > '9')
620
/* XXX Ignore the number since we would need some more
621
information to compute a real date. */
624
while (*rp >= '0' && *rp <= '9');
629
get_number (0, 53, 2);
630
/* XXX This cannot determine any field in TM without some
634
/* Match number of weekday. */
635
get_number (0, 6, 1);
640
/* Match year within century. */
641
get_number (0, 99, 2);
642
/* The "Year 2000: The Millennium Rollover" paper suggests that
643
values in the range 69-99 refer to the twentieth century. */
644
tm->tm_year = val >= 69 ? val : val + 100;
645
/* Indicate that we want to use the century, if specified. */
650
/* Match year including century number. */
651
get_number (0, 9999, 4);
652
tm->tm_year = val - 1900;
657
/* XXX How to handle this? */
664
/* Match locale's alternate date and time format. */
667
const char *fmt = _NL_CURRENT (LC_TIME, ERA_D_T_FMT);
670
fmt = _NL_CURRENT (LC_TIME, D_T_FMT);
672
if (!recursive (fmt))
679
if (strcmp (fmt, HERE_D_T_FMT))
686
/* The C locale has no era information, so use the
687
normal representation. */
688
if (!recursive (HERE_D_T_FMT))
695
/* Match name of base year in locale's alternate
697
/* XXX This is currently not implemented. It should
698
use the value _NL_CURRENT (LC_TIME, ERA). */
703
const char *fmt = _NL_CURRENT (LC_TIME, ERA_D_FMT);
706
fmt = _NL_CURRENT (LC_TIME, D_FMT);
708
if (!recursive (fmt))
715
if (strcmp (fmt, HERE_D_FMT))
721
if (!recursive (HERE_D_FMT))
727
const char *fmt = _NL_CURRENT (LC_TIME, ERA_T_FMT);
730
fmt = _NL_CURRENT (LC_TIME, T_FMT);
732
if (!recursive (fmt))
739
if (strcmp (fmt, HERE_T_FMT))
745
if (!recursive (HERE_T_FMT))
753
/* We have no information about the era format. Just use
754
the normal format. */
755
if (*fmt != 'c' && *fmt != 'C' && *fmt != 'y' && *fmt != 'Y'
756
&& *fmt != 'x' && *fmt != 'X')
757
/* This is an illegal format. */
767
/* Match day of month using alternate numeric symbols. */
768
get_alt_number (1, 31, 2);
774
/* Match hour in 24-hour clock using alternate numeric
776
get_alt_number (0, 23, 2);
781
/* Match hour in 12-hour clock using alternate numeric
783
get_alt_number (1, 12, 2);
784
tm->tm_hour = val - 1;
788
/* Match month using alternate numeric symbols. */
789
get_alt_number (1, 12, 2);
790
tm->tm_mon = val - 1;
795
/* Match minutes using alternate numeric symbols. */
796
get_alt_number (0, 59, 2);
800
/* Match seconds using alternate numeric symbols. */
801
get_alt_number (0, 61, 2);
807
get_alt_number (0, 53, 2);
808
/* XXX This cannot determine any field in TM without
809
further information. */
812
/* Match number of weekday using alternate numeric symbols. */
813
get_alt_number (0, 6, 1);
818
/* Match year within century using alternate numeric symbols. */
819
get_alt_number (0, 99, 2);
820
tm->tm_year = val >= 69 ? val : val + 100;
835
if (want_century && century != -1)
836
tm->tm_year = tm->tm_year % 100 + (century - 19) * 100;
838
if (want_xday && !have_wday) {
839
if ( !(have_mon && have_mday) && have_yday) {
840
/* we don't have tm_mon and/or tm_mday, compute them */
842
while (__mon_yday[__isleap(1900 + tm->tm_year)][t_mon] <= tm->tm_yday)
845
tm->tm_mon = t_mon - 1;
847
tm->tm_mday = tm->tm_yday - __mon_yday[__isleap(1900 + tm->tm_year)][t_mon - 1] + 1;
849
day_of_the_week (tm);
851
if (want_xday && !have_yday)
852
day_of_the_year (tm);
859
strptime (buf, format, tm)
864
enum locale_status decided;
870
return strptime_internal (buf, format, tm, &decided);