(M68*:*:R3V[567]*:*): Use uppercase 'M'.
[glibc.git] / time / strptime.c
blobbd38f9681f32c885acc050d99bde2d7832292e69
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 not,
18 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':
216 /* The number of seconds may be very high so we cannot use
217 the `get_number' macro. Instead read the number
218 character for character and construct the result while
219 doing this. */
220 time_t secs;
221 if (*rp < '0' || *rp > '9')
222 /* We need at least one digit. */
223 return NULL;
227 secs *= 10;
228 secs += *rp++ - '0';
230 while (*rp >= '0' && *rp <= '9');
232 if (__localtime_r (&secs, tm) == NULL)
233 /* Error in function. */
234 return NULL;
236 break;
237 case 'S':
238 get_number (0, 61);
239 tm->tm_sec = val;
240 break;
241 case 'T':
242 recursive ("%H:%M:%S");
243 break;
244 case 'u':
245 get_number (1, 7);
246 tm->tm_wday = val % 7;
247 break;
248 case 'g':
249 get_number (0, 99);
250 /* XXX This cannot determine any field in TM. */
251 break;
252 case 'G':
253 if (*rp < '0' || *rp > '9')
254 return NULL;
255 /* XXX Ignore the number since we would need some more
256 information to compute a real date. */
258 ++rp;
259 while (*rp >= '0' && *rp <= '9');
260 break;
261 case 'U':
262 case 'V':
263 case 'W':
264 get_number (0, 53);
265 /* XXX This cannot determine any field in TM. */
266 break;
267 case 'w':
268 /* Match number of weekday. */
269 get_number (0, 6);
270 tm->tm_wday = val;
271 break;
272 case 'x':
273 recursive (_NL_CURRENT (LC_TIME, D_FMT));
274 break;
275 case 'X':
276 recursive (_NL_CURRENT (LC_TIME, T_FMT));
277 break;
278 case 'y':
279 /* Match year within century. */
280 get_number (0, 99);
281 tm->tm_year = val;
282 break;
283 case 'Y':
284 /* Match year including century number. */
285 get_number (0, INT_MAX);
286 tm->tm_year = val - (val >= 2000 ? 2000 : 1900);
287 break;
288 case 'Z':
289 /* XXX How to handle this? */
290 break;
291 case 'E':
292 switch (*fmt++)
294 case 'c':
295 /* Match locale's alternate date and time format. */
296 recursive (_NL_CURRENT (LC_TIME, ERA_D_T_FMT));
297 break;
298 case 'C':
299 case 'y':
300 case 'Y':
301 /* Match name of base year in locale's alternate
302 representation. */
303 /* XXX This is currently not implemented. It should
304 use the value _NL_CURRENT (LC_TIME, ERA) but POSIX
305 leaves this implementation defined and we haven't
306 figured out how to do it yet. */
307 break;
308 case 'x':
309 recursive (_NL_CURRENT (LC_TIME, ERA_D_FMT));
310 break;
311 case 'X':
312 recursive (_NL_CURRENT (LC_TIME, ERA_T_FMT));
313 break;
314 default:
315 return NULL;
317 break;
318 case 'O':
319 switch (*fmt++)
321 case 'd':
322 case 'e':
323 /* Match day of month using alternate numeric symbols. */
324 get_alt_number (1, 31);
325 tm->tm_mday = val;
326 break;
327 case 'H':
328 /* Match hour in 24-hour clock using alternate numeric
329 symbols. */
330 get_alt_number (0, 23);
331 tm->tm_hour = val;
332 have_I = 0;
333 break;
334 case 'I':
335 /* Match hour in 12-hour clock using alternate numeric
336 symbols. */
337 get_alt_number (1, 12);
338 tm->tm_hour = val - 1;
339 have_I = 1;
340 break;
341 case 'm':
342 /* Match month using alternate numeric symbols. */
343 get_alt_number (1, 12);
344 tm->tm_mon = val - 1;
345 break;
346 case 'M':
347 /* Match minutes using alternate numeric symbols. */
348 get_alt_number (0, 59);
349 tm->tm_min = val;
350 break;
351 case 'S':
352 /* Match seconds using alternate numeric symbols. */
353 get_alt_number (0, 61);
354 tm->tm_sec = val;
355 break;
356 case 'U':
357 case 'V':
358 case 'W':
359 get_alt_number (0, 53);
360 /* XXX This cannot determine any field in TM. */
361 break;
362 case 'w':
363 /* Match number of weekday using alternate numeric symbols. */
364 get_alt_number (0, 6);
365 tm->tm_wday = val;
366 break;
367 case 'y':
368 /* Match year within century using alternate numeric symbols. */
369 get_alt_number (0, 99);
370 break;
371 default:
372 return NULL;
374 break;
375 default:
376 return NULL;
380 if (have_I && is_pm)
381 tm->tm_hour += 12;
383 return (char *) rp;