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 *buf, const char *fmt, struct tm *tm)
62 const unsigned char *bp;
63 int alt_format, i, split_year = 0, neg, offs;
66 bp = (const u_char *)buf;
68 while (bp != NULL && (c = *fmt++) != '\0') {
69 /* Clear `alternate' modifier prior to new conversion. */
73 /* Eat up white-space. */
84 again: switch (c = *fmt++) {
85 case '%': /* "%%" is converted to "%". */
93 * "Alternative" modifiers. Just set the appropriate flag
94 * and start over again.
96 case 'E': /* "%E?" alternative conversion modifier. */
101 case 'O': /* "%O?" alternative conversion modifier. */
107 * "Complex" conversion rules, implemented through recursion.
109 case 'c': /* Date and time, using the locale's format. */
110 new_fmt = _ctloc(d_t_fmt);
113 case 'D': /* The date as "%m/%d/%y". */
114 new_fmt = "%m/%d/%y";
118 case 'F': /* The date as "%Y-%m-%d". */
119 new_fmt = "%Y-%m-%d";
123 case 'R': /* The time as "%H:%M". */
128 case 'r': /* The time in 12-hour clock representation. */
129 new_fmt =_ctloc(t_fmt_ampm);
133 case 'T': /* The time as "%H:%M:%S". */
134 new_fmt = "%H:%M:%S";
138 case 'X': /* The time, using the locale's format. */
139 new_fmt =_ctloc(t_fmt);
142 case 'x': /* The date, using the locale's format. */
143 new_fmt =_ctloc(d_fmt);
145 bp = (const u_char *)strptime((const char *)bp,
151 * "Elementary" conversion rules.
153 case 'A': /* The day of week, using the locale's form. */
155 bp = find_string(bp, &tm->tm_wday, _ctloc(day),
160 case 'B': /* The month, using the locale's form. */
163 bp = find_string(bp, &tm->tm_mon, _ctloc(mon),
168 case 'C': /* The century number. */
170 bp = conv_num(bp, &i, 0, 99);
172 i = i * 100 - TM_YEAR_BASE;
174 i += tm->tm_year % 100;
180 case 'd': /* The day of month. */
182 bp = conv_num(bp, &tm->tm_mday, 1, 31);
186 case 'k': /* The hour (24-hour clock representation). */
190 bp = conv_num(bp, &tm->tm_hour, 0, 23);
194 case 'l': /* The hour (12-hour clock representation). */
198 bp = conv_num(bp, &tm->tm_hour, 1, 12);
199 if (tm->tm_hour == 12)
204 case 'j': /* The day of year. */
206 bp = conv_num(bp, &i, 1, 366);
211 case 'M': /* The minute. */
212 bp = conv_num(bp, &tm->tm_min, 0, 59);
216 case 'm': /* The month. */
218 bp = conv_num(bp, &i, 1, 12);
223 case 'p': /* The locale's equivalent of AM/PM. */
224 bp = find_string(bp, &i, _ctloc(am_pm), NULL, 2);
225 if (tm->tm_hour > 11)
227 tm->tm_hour += i * 12;
231 case 'S': /* The seconds. */
232 bp = conv_num(bp, &tm->tm_sec, 0, 61);
236 case 'U': /* The week of year, beginning on sunday. */
237 case 'W': /* The week of year, beginning on monday. */
239 * XXX This is bogus, as we can not assume any valid
240 * information present in the tm structure at this
241 * point to calculate a real value, so just check the
244 bp = conv_num(bp, &i, 0, 53);
248 case 'w': /* The day of week, beginning on sunday. */
249 bp = conv_num(bp, &tm->tm_wday, 0, 6);
253 case 'u': /* The day of week, monday = 1. */
254 bp = conv_num(bp, &i, 1, 7);
259 case 'g': /* The year corresponding to the ISO week
260 * number but without the century.
262 bp = conv_num(bp, &i, 0, 99);
265 case 'G': /* The year corresponding to the ISO week
266 * number with century.
270 while (isdigit(*bp));
273 case 'V': /* The ISO 8601:1988 week number as decimal */
274 bp = conv_num(bp, &i, 0, 53);
277 case 'Y': /* The year. */
278 i = TM_YEAR_BASE; /* just for data sanity... */
279 bp = conv_num(bp, &i, 0, 9999);
280 tm->tm_year = i - TM_YEAR_BASE;
284 case 'y': /* The year within 100 years of the epoch. */
285 /* LEGAL_ALT(ALT_E | ALT_O); */
286 bp = conv_num(bp, &i, 0, 99);
289 /* preserve century */
290 i += (tm->tm_year / 100) * 100;
294 i = i + 2000 - TM_YEAR_BASE;
296 i = i + 1900 - TM_YEAR_BASE;
303 if (strncmp((const char *)bp, gmt, 3) == 0) {
313 const unsigned char *ep;
315 ep = find_string(bp, &i,
316 (const char * const *)tzname,
321 tm->TM_GMTOFF = -(timezone);
324 tm->TM_ZONE = tzname[i];
333 * We recognize all ISO 8601 formats:
362 for (i = 0; i < 4; ) {
364 offs = offs * 10 + (*bp++ - '0');
368 if (i == 2 && *bp == ':') {
382 /* Convert minutes into decimal */
383 offs = (offs / 100) * 100 + (i * 50) / 30;
390 tm->tm_isdst = 0; /* XXX */
392 tm->TM_GMTOFF = offs;
395 tm->TM_ZONE = NULL; /* XXX */
400 * Miscellaneous conversions.
402 case 'n': /* Any kind of white-space. */
410 default: /* Unknown/unsupported conversion. */
415 return __DECONST(char *, bp);
419 static const u_char *
420 conv_num(const unsigned char *buf, int *dest, uint llim, uint ulim)
425 /* The limit also determines the number of valid digits. */
429 if (ch < '0' || ch > '9')
437 } while ((result * 10 <= ulim) && rulim && ch >= '0' && ch <= '9');
439 if (result < llim || result > ulim)
446 static const u_char *
447 find_string(const u_char *bp, int *tgt, const char * const *n1,
448 const char * const *n2, int c)
453 /* check full name - then abbreviated ones */
454 for (; n1 != NULL; n1 = n2, n2 = NULL) {
455 for (i = 0; i < c; i++, n1++) {
457 if (strncasecmp(*n1, (const char *)bp, len) == 0) {
464 /* Nothing matched */