2 * Ported from NetBSD to Windows by Ron Koenderink, 2007
3 * Port rebased by Markus Armbruster, 2009
11 typedef unsigned char u_char;
12 typedef unsigned int uint;
17 const char *abmon[12];
23 const char *t_fmt_ampm;
26 static const TimeLocale DefaultTimeLocale =
29 "Sun","Mon","Tue","Wed","Thu","Fri","Sat",
32 "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday",
36 "Jan", "Feb", "Mar", "Apr", "May", "Jun",
37 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
40 "January", "February", "March", "April", "May", "June", "July",
41 "August", "September", "October", "November", "December"
46 "%a %b %e %H:%M:%S %Y",
52 #define _CurrentTimeLocale (&DefaultTimeLocale)
53 #define TM_YEAR_BASE 1900
54 #define __UNCONST(a) ((void *)(a))
56 /* $NetBSD: strptime.c,v 1.28 2008/04/28 20:23:01 martin Exp $ */
59 * Copyright (c) 1997, 1998, 2005, 2008 The NetBSD Foundation, Inc.
60 * All rights reserved.
62 * This code was contributed to The NetBSD Foundation by Klaus Klein.
63 * Heavily optimised by David Laight
65 * Redistribution and use in source and binary forms, with or without
66 * modification, are permitted provided that the following conditions
68 * 1. Redistributions of source code must retain the above copyright
69 * notice, this list of conditions and the following disclaimer.
70 * 2. Redistributions in binary form must reproduce the above copyright
71 * notice, this list of conditions and the following disclaimer in the
72 * documentation and/or other materials provided with the distribution.
74 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
75 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
76 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
77 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
78 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
79 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
80 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
81 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
82 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
83 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
84 * POSSIBILITY OF SUCH DAMAGE.
88 #include <sys/cdefs.h>
89 #if defined(LIBC_SCCS) && !defined(lint)
90 __RCSID("$NetBSD: strptime.c,v 1.28 2008/04/28 20:23:01 martin Exp $");
93 #include "namespace.h"
94 #include <sys/localedef.h>
104 __weak_alias(strptime,_strptime)
108 #define _ctloc(x) (_CurrentTimeLocale->x)
111 * We do not implement alternate representations. However, we always
112 * check whether a given modifier is allowed for a certain conversion.
116 #define LEGAL_ALT(x) { if (alt_format & ~(x)) return NULL; }
118 static const char gmt[4] = { "GMT" };
120 static const u_char *conv_num(const unsigned char *, int *, uint, uint);
121 static const u_char *find_string(const u_char *, int *, const char * const *,
122 const char * const *, int);
126 strptime(const char *buf, const char *fmt, struct tm *tm)
129 const unsigned char *bp;
130 int alt_format, i, split_year = 0;
133 bp = (const u_char *)buf;
135 while (bp != NULL && (c = *fmt++) != '\0') {
136 /* Clear `alternate' modifier prior to new conversion. */
140 /* Eat up white-space. */
151 again: switch (c = *fmt++) {
152 case '%': /* "%%" is converted to "%". */
160 * "Alternative" modifiers. Just set the appropriate flag
161 * and start over again.
163 case 'E': /* "%E?" alternative conversion modifier. */
168 case 'O': /* "%O?" alternative conversion modifier. */
174 * "Complex" conversion rules, implemented through recursion.
176 case 'c': /* Date and time, using the locale's format. */
177 new_fmt = _ctloc(d_t_fmt);
180 case 'D': /* The date as "%m/%d/%y". */
181 new_fmt = "%m/%d/%y";
185 case 'F': /* The date as "%Y-%m-%d". */
186 new_fmt = "%Y-%m-%d";
190 case 'R': /* The time as "%H:%M". */
195 case 'r': /* The time in 12-hour clock representation. */
196 new_fmt =_ctloc(t_fmt_ampm);
200 case 'T': /* The time as "%H:%M:%S". */
201 new_fmt = "%H:%M:%S";
205 case 'X': /* The time, using the locale's format. */
206 new_fmt =_ctloc(t_fmt);
209 case 'x': /* The date, using the locale's format. */
210 new_fmt =_ctloc(d_fmt);
212 bp = (const u_char *)strptime((const char *)bp,
218 * "Elementary" conversion rules.
220 case 'A': /* The day of week, using the locale's form. */
222 bp = find_string(bp, &tm->tm_wday, _ctloc(day),
227 case 'B': /* The month, using the locale's form. */
230 bp = find_string(bp, &tm->tm_mon, _ctloc(mon),
235 case 'C': /* The century number. */
237 bp = conv_num(bp, &i, 0, 99);
239 i = i * 100 - TM_YEAR_BASE;
241 i += tm->tm_year % 100;
247 case 'd': /* The day of month. */
249 bp = conv_num(bp, &tm->tm_mday, 1, 31);
253 case 'k': /* The hour (24-hour clock representation). */
257 bp = conv_num(bp, &tm->tm_hour, 0, 23);
261 case 'l': /* The hour (12-hour clock representation). */
265 bp = conv_num(bp, &tm->tm_hour, 1, 12);
266 if (tm->tm_hour == 12)
271 case 'j': /* The day of year. */
273 bp = conv_num(bp, &i, 1, 366);
278 case 'M': /* The minute. */
279 bp = conv_num(bp, &tm->tm_min, 0, 59);
283 case 'm': /* The month. */
285 bp = conv_num(bp, &i, 1, 12);
290 case 'p': /* The locale's equivalent of AM/PM. */
291 bp = find_string(bp, &i, _ctloc(am_pm), NULL, 2);
292 if (tm->tm_hour > 11)
294 tm->tm_hour += i * 12;
298 case 'S': /* The seconds. */
299 bp = conv_num(bp, &tm->tm_sec, 0, 61);
303 case 'U': /* The week of year, beginning on sunday. */
304 case 'W': /* The week of year, beginning on monday. */
306 * XXX This is bogus, as we can not assume any valid
307 * information present in the tm structure at this
308 * point to calculate a real value, so just check the
311 bp = conv_num(bp, &i, 0, 53);
315 case 'w': /* The day of week, beginning on sunday. */
316 bp = conv_num(bp, &tm->tm_wday, 0, 6);
320 case 'Y': /* The year. */
321 i = TM_YEAR_BASE; /* just for data sanity... */
322 bp = conv_num(bp, &i, 0, 9999);
323 tm->tm_year = i - TM_YEAR_BASE;
327 case 'y': /* The year within 100 years of the epoch. */
328 /* LEGAL_ALT(ALT_E | ALT_O); */
329 bp = conv_num(bp, &i, 0, 99);
332 /* preserve century */
333 i += (tm->tm_year / 100) * 100;
337 i = i + 2000 - TM_YEAR_BASE;
339 i = i + 1900 - TM_YEAR_BASE;
346 if (strncmp((const char *)bp, gmt, 3) == 0) {
356 const unsigned char *ep;
358 ep = find_string(bp, &i,
359 (const char * const *)tzname,
364 tm->TM_GMTOFF = -(timezone);
367 tm->TM_ZONE = tzname[i];
375 * Miscellaneous conversions.
377 case 'n': /* Any kind of white-space. */
385 default: /* Unknown/unsupported conversion. */
390 return __UNCONST(bp);
394 static const u_char *
395 conv_num(const unsigned char *buf, int *dest, uint llim, uint ulim)
400 /* The limit also determines the number of valid digits. */
404 if (ch < '0' || ch > '9')
412 } while ((result * 10 <= ulim) && rulim && ch >= '0' && ch <= '9');
414 if (result < llim || result > ulim)
421 static const u_char *
422 find_string(const u_char *bp, int *tgt, const char * const *n1,
423 const char * const *n2, int c)
428 /* check full name - then abbreviated ones */
429 for (; n1 != NULL; n1 = n2, n2 = NULL) {
430 for (i = 0; i < c; i++, n1++) {
432 if (strncasecmp(*n1, (const char *)bp, len) == 0) {
439 /* Nothing matched */