Update for 960809.
[glibc.git] / time / strptime.c
blobcb3d126b9ce3175e480491723705a7652202dc6d
1 /* strptime - Convert a string representation of time to a time value.
2 Copyright (C) 1996 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
18 not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19 Boston, MA 02111-1307, USA. */
21 #include <ctype.h>
22 #include <langinfo.h>
23 #include <limits.h>
24 #include <string.h>
25 #include <time.h>
27 #include "../locale/localeinfo.h"
30 #define match_char(ch1, ch2) if (ch1 != ch2) return NULL
31 #define match_string(cs1, s2) \
32 ({ size_t len = strlen (cs1); \
33 int result = strncasecmp (cs1, s2, len) == 0; \
34 if (result) s2 += len; \
35 result; })
36 /* We intentionally do not use isdigit() for testing because this will
37 lead to problems with the wide character version. */
38 #define get_number(from, to) \
39 do { \
40 val = 0; \
41 if (*rp < '0' || *rp > '9') \
42 return NULL; \
43 do { \
44 val *= 10; \
45 val += *rp++ - '0'; \
46 } while (val * 10 <= to && *rp >= '0' && *rp <= '9'); \
47 if (val < from || val > to) \
48 return NULL; \
49 } while (0)
50 #define get_alt_number(from, to) \
51 do { \
52 const char *alts = _NL_CURRENT (LC_TIME, ALT_DIGITS); \
53 val = 0; \
54 while (*alts != '\0') \
55 { \
56 size_t len = strlen (alts); \
57 if (strncasecmp (alts, rp, len) == 0) \
58 break; \
59 alts = strchr (alts, '\0') + 1; \
60 ++val; \
61 } \
62 if (*alts == '\0') \
63 return NULL; \
64 } while (0)
65 #define recursive(new_fmt) \
66 do { \
67 if (*new_fmt == '\0') \
68 return NULL; \
69 rp = strptime (rp, new_fmt, tm); \
70 if (rp == NULL) \
71 return NULL; \
72 } while (0)
75 char *
76 strptime (const char *buf, const char *format, struct tm *tm)
78 const char *rp;
79 const char *fmt;
80 int cnt;
81 size_t val;
82 int have_I, is_pm;
84 rp = buf;
85 fmt = format;
86 have_I = is_pm = 0;
88 while (*fmt != '\0')
90 /* A white space in the format string matches 0 more or white
91 space in the input string. */
92 if (isspace (*fmt))
94 while (isspace (*rp))
95 ++rp;
96 ++fmt;
97 continue;
100 /* Any character but `%' must be matched by the same character
101 in the iput string. */
102 if (*fmt != '%')
104 match_char (*fmt++, *rp++);
105 continue;
108 ++fmt;
109 switch (*fmt++)
111 case '%':
112 /* Match the `%' character itself. */
113 match_char ('%', *rp++);
114 break;
115 case 'a':
116 case 'A':
117 /* Match day of week. */
118 for (cnt = 0; cnt < 7; ++cnt)
120 if (match_string (_NL_CURRENT (LC_TIME, ABDAY_1 + cnt), rp))
121 break;
122 if (match_string (_NL_CURRENT (LC_TIME, DAY_1 + cnt), rp))
123 break;
125 if (cnt == 7)
126 /* Does not match a weekday name. */
127 return NULL;
128 tm->tm_wday = cnt;
129 break;
130 case 'b':
131 case 'B':
132 case 'h':
133 /* Match month name. */
134 for (cnt = 0; cnt < 12; ++cnt)
136 if (match_string (_NL_CURRENT (LC_TIME, ABMON_1 + cnt), rp))
137 break;
138 if (match_string (_NL_CURRENT (LC_TIME, MON_1 + cnt), rp))
139 break;
141 if (cnt == 12)
142 /* Does not match a month name. */
143 return NULL;
144 tm->tm_mon = cnt;
145 break;
146 case 'c':
147 /* Match locale's date and time format. */
148 recursive (_NL_CURRENT (LC_TIME, D_T_FMT));
149 break;
150 case 'C':
151 /* Match century number. */
152 get_number (0, 99);
153 /* We don't need the number. */
154 break;
155 case 'd':
156 case 'e':
157 /* Match day of month. */
158 get_number (1, 31);
159 tm->tm_mday = val;
160 break;
161 case 'D':
162 /* Match standard day format. */
163 recursive ("%m/%d/%y");
164 break;
165 case 'H':
166 /* Match hour in 24-hour clock. */
167 get_number (0, 23);
168 tm->tm_hour = val;
169 have_I = 0;
170 break;
171 case 'I':
172 /* Match hour in 12-hour clock. */
173 get_number (1, 12);
174 tm->tm_hour = val - 1;
175 have_I = 1;
176 break;
177 case 'j':
178 /* Match day number of year. */
179 get_number (1, 366);
180 tm->tm_yday = val - 1;
181 break;
182 case 'm':
183 /* Match number of month. */
184 get_number (1, 12);
185 tm->tm_mon = val - 1;
186 break;
187 case 'M':
188 /* Match minute. */
189 get_number (0, 59);
190 tm->tm_min = val;
191 break;
192 case 'n':
193 case 't':
194 /* Match any white space. */
195 while (isspace (*rp))
196 ++rp;
197 break;
198 case 'p':
199 /* Match locale's equivalent of AM/PM. */
200 if (match_string (_NL_CURRENT (LC_TIME, AM_STR), rp))
201 break;
202 if (match_string (_NL_CURRENT (LC_TIME, PM_STR), rp))
204 is_pm = 1;
205 break;
207 return NULL;
208 case 'r':
209 recursive (_NL_CURRENT (LC_TIME, T_FMT_AMPM));
210 break;
211 case 'R':
212 recursive ("%H:%M");
213 break;
214 case 'S':
215 get_number (0, 61);
216 tm->tm_sec = val;
217 break;
218 case 'T':
219 recursive ("%H:%M:%S");
220 break;
221 case 'U':
222 case 'V':
223 case 'W':
224 get_number (0, 53);
225 /* XXX This cannot determine any field in TM. */
226 break;
227 case 'w':
228 /* Match number of weekday. */
229 get_number (0, 6);
230 tm->tm_wday = val;
231 break;
232 case 'x':
233 recursive (_NL_CURRENT (LC_TIME, D_FMT));
234 break;
235 case 'X':
236 recursive (_NL_CURRENT (LC_TIME, T_FMT));
237 break;
238 case 'y':
239 /* Match year within century. */
240 get_number (0, 99);
241 tm->tm_year = val;
242 break;
243 case 'Y':
244 /* Match year including century number. */
245 get_number (0, INT_MAX);
246 tm->tm_year = val - (val >= 2000 ? 2000 : 1900);
247 break;
248 case 'Z':
249 /* XXX How to handle this? */
250 break;
251 case 'E':
252 switch (*fmt++)
254 case 'c':
255 /* Match locale's alternate date and time format. */
256 recursive (_NL_CURRENT (LC_TIME, ERA_D_T_FMT));
257 break;
258 case 'C':
259 case 'y':
260 case 'Y':
261 /* Match name of base year in locale's alternate
262 representation. */
263 /* XXX This is currently not implemented. It should
264 use the value _NL_CURRENT (LC_TIME, ERA) but POSIX
265 leaves this implementation defined and we haven't
266 figured out how to do it yet. */
267 break;
268 case 'x':
269 recursive (_NL_CURRENT (LC_TIME, ERA_D_FMT));
270 break;
271 case 'X':
272 recursive (_NL_CURRENT (LC_TIME, ERA_T_FMT));
273 break;
274 default:
275 return NULL;
277 break;
278 case 'O':
279 switch (*fmt++)
281 case 'd':
282 case 'e':
283 /* Match day of month using alternate numeric symbols. */
284 get_alt_number (1, 31);
285 tm->tm_mday = val;
286 break;
287 case 'H':
288 /* Match hour in 24-hour clock using alternate numeric
289 symbols. */
290 get_alt_number (0, 23);
291 tm->tm_hour = val;
292 have_I = 0;
293 break;
294 case 'I':
295 /* Match hour in 12-hour clock using alternate numeric
296 symbols. */
297 get_alt_number (1, 12);
298 tm->tm_hour = val - 1;
299 have_I = 1;
300 break;
301 case 'm':
302 /* Match month using alternate numeric symbols. */
303 get_alt_number (1, 12);
304 tm->tm_mon = val - 1;
305 break;
306 case 'M':
307 /* Match minutes using alternate numeric symbols. */
308 get_alt_number (0, 59);
309 tm->tm_min = val;
310 break;
311 case 'S':
312 /* Match seconds using alternate numeric symbols. */
313 get_alt_number (0, 61);
314 tm->tm_sec = val;
315 break;
316 case 'U':
317 case 'V':
318 case 'W':
319 get_alt_number (0, 53);
320 /* XXX This cannot determine any field in TM. */
321 break;
322 case 'w':
323 /* Match number of weekday using alternate numeric symbols. */
324 get_alt_number (0, 6);
325 tm->tm_wday = val;
326 break;
327 case 'y':
328 /* Match year within century using alternate numeric symbols. */
329 get_alt_number (0, 99);
330 break;
331 default:
332 return NULL;
334 break;
335 default:
336 return NULL;
340 if (have_I && is_pm)
341 tm->tm_hour += 12;
343 return (char *) rp;