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 */