1 /* $NetBSD: strptime.c,v 1.34 2009/12/14 05:51:56 matt Exp $ */
4 * Copyright (c) 1997, 1998, 2005, 2008 The NetBSD Foundation, Inc.
7 * This code was contributed to The NetBSD Foundation by Klaus Klein.
8 * Heavily optimised by David Laight
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
32 #include <sys/cdefs.h>
33 #if defined(LIBC_SCCS) && !defined(lint)
34 __RCSID("$NetBSD: strptime.c,v 1.34 2009/12/14 05:51:56 matt Exp $");
37 #include "namespace.h"
38 #include <sys/localedef.h>
47 __weak_alias(strptime
,_strptime
)
50 #define _ctloc(x) (_CurrentTimeLocale->x)
53 * We do not implement alternate representations. However, we always
54 * check whether a given modifier is allowed for a certain conversion.
58 #define LEGAL_ALT(x) { if (alt_format & ~(x)) return NULL; }
60 static char gmt
[] = { "GMT" };
61 static char utc
[] = { "UTC" };
62 /* RFC-822/RFC-2822 */
63 static const char * const nast
[5] = {
64 "EST", "CST", "MST", "PST", "\0\0\0"
66 static const char * const nadt
[5] = {
67 "EDT", "CDT", "MDT", "PDT", "\0\0\0"
70 static const u_char
*conv_num(const unsigned char *, int *, uint
, uint
);
71 static const u_char
*find_string(const u_char
*, int *, const char * const *,
72 const char * const *, int);
76 strptime(const char *buf
, const char *fmt
, struct tm
*tm
)
79 const unsigned char *bp
, *ep
;
80 int alt_format
, i
, split_year
= 0, neg
= 0, offs
;
83 bp
= (const u_char
*)buf
;
85 while (bp
!= NULL
&& (c
= *fmt
++) != '\0') {
86 /* Clear `alternate' modifier prior to new conversion. */
90 /* Eat up white-space. */
101 again
: switch (c
= *fmt
++) {
102 case '%': /* "%%" is converted to "%". */
110 * "Alternative" modifiers. Just set the appropriate flag
111 * and start over again.
113 case 'E': /* "%E?" alternative conversion modifier. */
118 case 'O': /* "%O?" alternative conversion modifier. */
124 * "Complex" conversion rules, implemented through recursion.
126 case 'c': /* Date and time, using the locale's format. */
127 new_fmt
= _ctloc(d_t_fmt
);
130 case 'D': /* The date as "%m/%d/%y". */
131 new_fmt
= "%m/%d/%y";
135 case 'F': /* The date as "%Y-%m-%d". */
136 new_fmt
= "%Y-%m-%d";
140 case 'R': /* The time as "%H:%M". */
145 case 'r': /* The time in 12-hour clock representation. */
146 new_fmt
=_ctloc(t_fmt_ampm
);
150 case 'T': /* The time as "%H:%M:%S". */
151 new_fmt
= "%H:%M:%S";
155 case 'X': /* The time, using the locale's format. */
156 new_fmt
=_ctloc(t_fmt
);
159 case 'x': /* The date, using the locale's format. */
160 new_fmt
=_ctloc(d_fmt
);
162 bp
= (const u_char
*)strptime((const char *)bp
,
168 * "Elementary" conversion rules.
170 case 'A': /* The day of week, using the locale's form. */
172 bp
= find_string(bp
, &tm
->tm_wday
, _ctloc(day
),
177 case 'B': /* The month, using the locale's form. */
180 bp
= find_string(bp
, &tm
->tm_mon
, _ctloc(mon
),
185 case 'C': /* The century number. */
187 bp
= conv_num(bp
, &i
, 0, 99);
189 i
= i
* 100 - TM_YEAR_BASE
;
191 i
+= tm
->tm_year
% 100;
197 case 'd': /* The day of month. */
199 bp
= conv_num(bp
, &tm
->tm_mday
, 1, 31);
203 case 'k': /* The hour (24-hour clock representation). */
207 bp
= conv_num(bp
, &tm
->tm_hour
, 0, 23);
211 case 'l': /* The hour (12-hour clock representation). */
215 bp
= conv_num(bp
, &tm
->tm_hour
, 1, 12);
216 if (tm
->tm_hour
== 12)
221 case 'j': /* The day of year. */
223 bp
= conv_num(bp
, &i
, 1, 366);
228 case 'M': /* The minute. */
229 bp
= conv_num(bp
, &tm
->tm_min
, 0, 59);
233 case 'm': /* The month. */
235 bp
= conv_num(bp
, &i
, 1, 12);
240 case 'p': /* The locale's equivalent of AM/PM. */
241 bp
= find_string(bp
, &i
, _ctloc(am_pm
), NULL
, 2);
242 if (tm
->tm_hour
> 11)
244 tm
->tm_hour
+= i
* 12;
248 case 'S': /* The seconds. */
249 bp
= conv_num(bp
, &tm
->tm_sec
, 0, 61);
254 #define TIME_MAX INT64_MAX
256 case 's': /* seconds since the epoch */
259 uint64_t rulim
= TIME_MAX
;
261 if (*bp
< '0' || *bp
> '9') {
270 } while ((sse
* 10 <= TIME_MAX
) &&
271 rulim
&& *bp
>= '0' && *bp
<= '9');
273 if (sse
< 0 || (uint64_t)sse
> TIME_MAX
) {
278 if (localtime_r(&sse
, tm
) == NULL
)
283 case 'U': /* The week of year, beginning on sunday. */
284 case 'W': /* The week of year, beginning on monday. */
286 * XXX This is bogus, as we can not assume any valid
287 * information present in the tm structure at this
288 * point to calculate a real value, so just check the
291 bp
= conv_num(bp
, &i
, 0, 53);
295 case 'w': /* The day of week, beginning on sunday. */
296 bp
= conv_num(bp
, &tm
->tm_wday
, 0, 6);
300 case 'u': /* The day of week, monday = 1. */
301 bp
= conv_num(bp
, &i
, 1, 7);
306 case 'g': /* The year corresponding to the ISO week
307 * number but without the century.
309 bp
= conv_num(bp
, &i
, 0, 99);
312 case 'G': /* The year corresponding to the ISO week
313 * number with century.
317 while (isdigit(*bp
));
320 case 'V': /* The ISO 8601:1988 week number as decimal */
321 bp
= conv_num(bp
, &i
, 0, 53);
324 case 'Y': /* The year. */
325 i
= TM_YEAR_BASE
; /* just for data sanity... */
326 bp
= conv_num(bp
, &i
, 0, 9999);
327 tm
->tm_year
= i
- TM_YEAR_BASE
;
331 case 'y': /* The year within 100 years of the epoch. */
332 /* LEGAL_ALT(ALT_E | ALT_O); */
333 bp
= conv_num(bp
, &i
, 0, 99);
336 /* preserve century */
337 i
+= (tm
->tm_year
/ 100) * 100;
341 i
= i
+ 2000 - TM_YEAR_BASE
;
343 i
= i
+ 1900 - TM_YEAR_BASE
;
350 if (strncmp((const char *)bp
, gmt
, 3) == 0) {
360 ep
= find_string(bp
, &i
,
361 (const char * const *)tzname
,
366 tm
->TM_GMTOFF
= -(timezone
);
369 tm
->TM_ZONE
= tzname
[i
];
378 * We recognize all ISO 8601 formats:
383 * We recognize all RFC-822/RFC-2822 formats:
385 * North American : UTC offsets
386 * E[DS]T = Eastern : -4 | -5
387 * C[DS]T = Central : -5 | -6
388 * M[DS]T = Mountain: -6 | -7
389 * P[DS]T = Pacific : -7 | -8
391 * [A-IL-M] = -1 ... -9 (J not used)
423 ep
= find_string(bp
, &i
, nast
, NULL
, 4);
426 tm
->TM_GMTOFF
= -5 - i
;
429 tm
->TM_ZONE
= __UNCONST(nast
[i
]);
434 ep
= find_string(bp
, &i
, nadt
, NULL
, 4);
438 tm
->TM_GMTOFF
= -4 - i
;
441 tm
->TM_ZONE
= __UNCONST(nadt
[i
]);
447 if ((*bp
>= 'A' && *bp
<= 'I') ||
448 (*bp
>= 'L' && *bp
<= 'Y')) {
451 if (*bp
>= 'A' && *bp
<= 'I')
453 ('A' - 1) - (int)*bp
;
454 else if (*bp
>= 'L' && *bp
<= 'M')
455 tm
->TM_GMTOFF
= 'A' - (int)*bp
;
456 else if (*bp
>= 'N' && *bp
<= 'Y')
457 tm
->TM_GMTOFF
= (int)*bp
- 'M';
460 tm
->TM_ZONE
= NULL
; /* XXX */
468 for (i
= 0; i
< 4; ) {
470 offs
= offs
* 10 + (*bp
++ - '0');
474 if (i
== 2 && *bp
== ':') {
488 /* Convert minutes into decimal */
489 offs
= (offs
/ 100) * 100 + (i
* 50) / 30;
496 tm
->tm_isdst
= 0; /* XXX */
498 tm
->TM_GMTOFF
= offs
;
501 tm
->TM_ZONE
= NULL
; /* XXX */
506 * Miscellaneous conversions.
508 case 'n': /* Any kind of white-space. */
516 default: /* Unknown/unsupported conversion. */
521 return __UNCONST(bp
);
525 static const u_char
*
526 conv_num(const unsigned char *buf
, int *dest
, uint llim
, uint ulim
)
531 /* The limit also determines the number of valid digits. */
535 if (ch
< '0' || ch
> '9')
543 } while ((result
* 10 <= ulim
) && rulim
&& ch
>= '0' && ch
<= '9');
545 if (result
< llim
|| result
> ulim
)
552 static const u_char
*
553 find_string(const u_char
*bp
, int *tgt
, const char * const *n1
,
554 const char * const *n2
, int c
)
559 /* check full name - then abbreviated ones */
560 for (; n1
!= NULL
; n1
= n2
, n2
= NULL
) {
561 for (i
= 0; i
< c
; i
++, n1
++) {
563 if (strncasecmp(*n1
, (const char *)bp
, len
) == 0) {
570 /* Nothing matched */