1 /* $NetBSD: strptime.c,v 1.31 2008/11/04 21:08:33 christos Exp $ */
2 /* $DragonFly: src/lib/libc/stdtime/strptime.c,v 1.5 2005/12/04 23:25:40 swildner Exp $ */
5 * Copyright (c) 1997, 1998, 2005, 2008 The NetBSD Foundation, Inc.
8 * This code was contributed to The NetBSD Foundation by Klaus Klein.
9 * Heavily optimised by David Laight
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
20 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
21 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
22 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
24 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30 * POSSIBILITY OF SUCH DAMAGE.
33 #include <sys/localedef.h>
41 #define _ctloc(x) (_CurrentTimeLocale->x)
44 * We do not implement alternate representations. However, we always
45 * check whether a given modifier is allowed for a certain conversion.
49 #define LEGAL_ALT(x) { if (alt_format & ~(x)) return NULL; }
51 static char gmt[] = { "GMT" };
52 static char utc[] = { "UTC" };
54 static const u_char *conv_num(const unsigned char *, int *, uint, uint);
55 static const u_char *find_string(const u_char *, int *, const char * const *,
56 const char * const *, int);
59 strptime(const char * __restrict buf, const char * __restrict fmt,
60 struct tm * __restrict tm)
63 const unsigned char *bp;
64 int alt_format, i, split_year = 0, neg, offs;
67 bp = (const u_char *)buf;
69 while (bp != NULL && (c = *fmt++) != '\0') {
70 /* Clear `alternate' modifier prior to new conversion. */
74 /* Eat up white-space. */
85 again: switch (c = *fmt++) {
86 case '%': /* "%%" is converted to "%". */
94 * "Alternative" modifiers. Just set the appropriate flag
95 * and start over again.
97 case 'E': /* "%E?" alternative conversion modifier. */
102 case 'O': /* "%O?" alternative conversion modifier. */
108 * "Complex" conversion rules, implemented through recursion.
110 case 'c': /* Date and time, using the locale's format. */
111 new_fmt = _ctloc(d_t_fmt);
114 case 'D': /* The date as "%m/%d/%y". */
115 new_fmt = "%m/%d/%y";
119 case 'F': /* The date as "%Y-%m-%d". */
120 new_fmt = "%Y-%m-%d";
124 case 'R': /* The time as "%H:%M". */
129 case 'r': /* The time in 12-hour clock representation. */
130 new_fmt =_ctloc(t_fmt_ampm);
134 case 'T': /* The time as "%H:%M:%S". */
135 new_fmt = "%H:%M:%S";
139 case 'X': /* The time, using the locale's format. */
140 new_fmt =_ctloc(t_fmt);
143 case 'x': /* The date, using the locale's format. */
144 new_fmt =_ctloc(d_fmt);
146 bp = (const u_char *)strptime((const char *)bp,
152 * "Elementary" conversion rules.
154 case 'A': /* The day of week, using the locale's form. */
156 bp = find_string(bp, &tm->tm_wday, _ctloc(day),
161 case 'B': /* The month, using the locale's form. */
164 bp = find_string(bp, &tm->tm_mon, _ctloc(mon),
169 case 'C': /* The century number. */
171 bp = conv_num(bp, &i, 0, 99);
173 i = i * 100 - TM_YEAR_BASE;
175 i += tm->tm_year % 100;
181 case 'd': /* The day of month. */
183 bp = conv_num(bp, &tm->tm_mday, 1, 31);
187 case 'k': /* The hour (24-hour clock representation). */
191 bp = conv_num(bp, &tm->tm_hour, 0, 23);
195 case 'l': /* The hour (12-hour clock representation). */
199 bp = conv_num(bp, &tm->tm_hour, 1, 12);
200 if (tm->tm_hour == 12)
205 case 'j': /* The day of year. */
207 bp = conv_num(bp, &i, 1, 366);
212 case 'M': /* The minute. */
213 bp = conv_num(bp, &tm->tm_min, 0, 59);
217 case 'm': /* The month. */
219 bp = conv_num(bp, &i, 1, 12);
224 case 'p': /* The locale's equivalent of AM/PM. */
225 bp = find_string(bp, &i, _ctloc(am_pm), NULL, 2);
226 if (tm->tm_hour > 11)
228 tm->tm_hour += i * 12;
232 case 'S': /* The seconds. */
233 bp = conv_num(bp, &tm->tm_sec, 0, 61);
237 case 'U': /* The week of year, beginning on sunday. */
238 case 'W': /* The week of year, beginning on monday. */
240 * XXX This is bogus, as we can not assume any valid
241 * information present in the tm structure at this
242 * point to calculate a real value, so just check the
245 bp = conv_num(bp, &i, 0, 53);
249 case 'w': /* The day of week, beginning on sunday. */
250 bp = conv_num(bp, &tm->tm_wday, 0, 6);
254 case 'u': /* The day of week, monday = 1. */
255 bp = conv_num(bp, &i, 1, 7);
260 case 'g': /* The year corresponding to the ISO week
261 * number but without the century.
263 bp = conv_num(bp, &i, 0, 99);
266 case 'G': /* The year corresponding to the ISO week
267 * number with century.
271 while (isdigit(*bp));
274 case 'V': /* The ISO 8601:1988 week number as decimal */
275 bp = conv_num(bp, &i, 0, 53);
278 case 'Y': /* The year. */
279 i = TM_YEAR_BASE; /* just for data sanity... */
280 bp = conv_num(bp, &i, 0, 9999);
281 tm->tm_year = i - TM_YEAR_BASE;
285 case 'y': /* The year within 100 years of the epoch. */
286 /* LEGAL_ALT(ALT_E | ALT_O); */
287 bp = conv_num(bp, &i, 0, 99);
290 /* preserve century */
291 i += (tm->tm_year / 100) * 100;
295 i = i + 2000 - TM_YEAR_BASE;
297 i = i + 1900 - TM_YEAR_BASE;
304 if (strncmp((const char *)bp, gmt, 3) == 0) {
314 const unsigned char *ep;
316 ep = find_string(bp, &i,
317 (const char * const *)tzname,
322 tm->TM_GMTOFF = -(timezone);
325 tm->TM_ZONE = tzname[i];
334 * We recognize all ISO 8601 formats:
363 for (i = 0; i < 4; ) {
365 offs = offs * 10 + (*bp++ - '0');
369 if (i == 2 && *bp == ':') {
383 /* Convert minutes into decimal */
384 offs = (offs / 100) * 100 + (i * 50) / 30;
391 tm->tm_isdst = 0; /* XXX */
393 tm->TM_GMTOFF = offs;
396 tm->TM_ZONE = NULL; /* XXX */
401 * Miscellaneous conversions.
403 case 'n': /* Any kind of white-space. */
411 default: /* Unknown/unsupported conversion. */
416 return __DECONST(char *, bp);
420 static const u_char *
421 conv_num(const unsigned char *buf, int *dest, uint llim, uint ulim)
426 /* The limit also determines the number of valid digits. */
430 if (ch < '0' || ch > '9')
438 } while ((result * 10 <= ulim) && rulim && ch >= '0' && ch <= '9');
440 if (result < llim || result > ulim)
447 static const u_char *
448 find_string(const u_char *bp, int *tgt, const char * const *n1,
449 const char * const *n2, int c)
454 /* check full name - then abbreviated ones */
455 for (; n1 != NULL; n1 = n2, n2 = NULL) {
456 for (i = 0; i < c; i++, n1++) {
458 if (strncasecmp(*n1, (const char *)bp, len) == 0) {
465 /* Nothing matched */