1 /* $OpenBSD: strptime.c,v 1.11 2005/08/08 08:05:38 espie Exp $ */
2 /* $NetBSD: strptime.c,v 1.12 1998/01/20 21:39:40 mycroft Exp $ */
5 * Copyright (c) 1997, 1998 The NetBSD Foundation, Inc.
8 * This code was contributed to The NetBSD Foundation by Klaus Klein.
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.
18 * 3. All advertising materials mentioning features or use of this software
19 * must display the following acknowledgement:
20 * This product includes software developed by the NetBSD
21 * Foundation, Inc. and its contributors.
22 * 4. Neither the name of The NetBSD Foundation nor the names of its
23 * contributors may be used to endorse or promote products derived
24 * from this software without specific prior written permission.
26 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
27 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
28 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
29 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
30 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
31 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
32 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
33 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
34 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
35 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
36 * POSSIBILITY OF SUCH DAMAGE.
50 const char *abmon
[12];
56 const char *t_fmt_ampm
;
57 } _DefaultTimeLocale
= {
59 "Sun","Mon","Tue","Wed","Thu","Fri","Sat",
62 "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday",
66 "Jan", "Feb", "Mar", "Apr", "May", "Jun",
67 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
70 "January", "February", "March", "April", "May", "June", "July",
71 "August", "September", "October", "November", "December"
76 "%a %b %d %H:%M:%S %Y",
82 #define _ctloc(x) (_DefaultTimeLocale.x)
85 * We do not implement alternate representations. However, we always
86 * check whether a given modifier is allowed for a certain conversion.
90 #define _LEGAL_ALT(x) { if (alt_format & ~(x)) return (0); }
93 static int _conv_num(unsigned char **, int *, int, int);
94 static char *_strptime(const char *, const char *, struct tm
*, int);
98 strptime(const char *buf
, const char *fmt
, struct tm
*tm
)
100 return(_strptime(buf
, fmt
, tm
, 1));
104 _strptime(const char *buf
, const char *fmt
, struct tm
*tm
, int initialize
)
110 static int century
, relyear
;
113 century
= TM_YEAR_BASE
;
117 bp
= (unsigned char *)buf
;
118 while ((c
= *fmt
) != '\0') {
119 /* Clear `alternate' modifier prior to new conversion. */
122 /* Eat up white-space. */
131 if ((c
= *fmt
++) != '%')
135 again
: switch (c
= *fmt
++) {
136 case '%': /* "%%" is converted to "%". */
144 * "Alternative" modifiers. Just set the appropriate flag
145 * and start over again.
147 case 'E': /* "%E?" alternative conversion modifier. */
149 alt_format
|= _ALT_E
;
152 case 'O': /* "%O?" alternative conversion modifier. */
154 alt_format
|= _ALT_O
;
158 * "Complex" conversion rules, implemented through recursion.
160 case 'c': /* Date and time, using the locale's format. */
162 if ((bp
= _strptime(bp
, _ctloc(d_t_fmt
), tm
, 0)) == 0)
166 case 'D': /* The date as "%m/%d/%y". */
168 if ((bp
= _strptime(bp
, "%m/%d/%y", tm
, 0)) == 0)
172 case 'R': /* The time as "%H:%M". */
174 if ((bp
= _strptime(bp
, "%H:%M", tm
, 0)) == 0)
178 case 'r': /* The time as "%I:%M:%S %p". */
180 if ((bp
= _strptime(bp
, "%I:%M:%S %p", tm
, 0)) == 0)
184 case 'T': /* The time as "%H:%M:%S". */
186 if ((bp
= _strptime(bp
, "%H:%M:%S", tm
, 0)) == 0)
190 case 'X': /* The time, using the locale's format. */
192 if ((bp
= _strptime(bp
, _ctloc(t_fmt
), tm
, 0)) == 0)
196 case 'x': /* The date, using the locale's format. */
198 if ((bp
= _strptime(bp
, _ctloc(d_fmt
), tm
, 0)) == 0)
203 * "Elementary" conversion rules.
205 case 'A': /* The day of week, using the locale's form. */
208 for (i
= 0; i
< 7; i
++) {
210 len
= strlen(_ctloc(day
[i
]));
211 if (strncasecmp(_ctloc(day
[i
]), bp
, len
) == 0)
214 /* Abbreviated name. */
215 len
= strlen(_ctloc(abday
[i
]));
216 if (strncasecmp(_ctloc(abday
[i
]), bp
, len
) == 0)
220 /* Nothing matched. */
228 case 'B': /* The month, using the locale's form. */
232 for (i
= 0; i
< 12; i
++) {
234 len
= strlen(_ctloc(mon
[i
]));
235 if (strncasecmp(_ctloc(mon
[i
]), bp
, len
) == 0)
238 /* Abbreviated name. */
239 len
= strlen(_ctloc(abmon
[i
]));
240 if (strncasecmp(_ctloc(abmon
[i
]), bp
, len
) == 0)
244 /* Nothing matched. */
252 case 'C': /* The century number. */
254 if (!(_conv_num(&bp
, &i
, 0, 99)))
260 case 'd': /* The day of month. */
263 if (!(_conv_num(&bp
, &tm
->tm_mday
, 1, 31)))
267 case 'k': /* The hour (24-hour clock representation). */
272 if (!(_conv_num(&bp
, &tm
->tm_hour
, 0, 23)))
276 case 'l': /* The hour (12-hour clock representation). */
281 if (!(_conv_num(&bp
, &tm
->tm_hour
, 1, 12)))
285 case 'j': /* The day of year. */
287 if (!(_conv_num(&bp
, &tm
->tm_yday
, 1, 366)))
292 case 'M': /* The minute. */
294 if (!(_conv_num(&bp
, &tm
->tm_min
, 0, 59)))
298 case 'm': /* The month. */
300 if (!(_conv_num(&bp
, &tm
->tm_mon
, 1, 12)))
305 case 'p': /* The locale's equivalent of AM/PM. */
308 len
= strlen(_ctloc(am_pm
[0]));
309 if (strncasecmp(_ctloc(am_pm
[0]), bp
, len
) == 0) {
310 if (tm
->tm_hour
> 12) /* i.e., 13:00 AM ?! */
312 else if (tm
->tm_hour
== 12)
319 len
= strlen(_ctloc(am_pm
[1]));
320 if (strncasecmp(_ctloc(am_pm
[1]), bp
, len
) == 0) {
321 if (tm
->tm_hour
> 12) /* i.e., 13:00 PM ?! */
323 else if (tm
->tm_hour
< 12)
330 /* Nothing matched. */
333 case 'S': /* The seconds. */
335 if (!(_conv_num(&bp
, &tm
->tm_sec
, 0, 61)))
339 case 'U': /* The week of year, beginning on sunday. */
340 case 'W': /* The week of year, beginning on monday. */
343 * XXX This is bogus, as we can not assume any valid
344 * information present in the tm structure at this
345 * point to calculate a real value, so just check the
348 if (!(_conv_num(&bp
, &i
, 0, 53)))
352 case 'w': /* The day of week, beginning on sunday. */
354 if (!(_conv_num(&bp
, &tm
->tm_wday
, 0, 6)))
358 case 'Y': /* The year. */
360 if (!(_conv_num(&bp
, &i
, 0, 9999)))
364 tm
->tm_year
= i
- TM_YEAR_BASE
;
367 case 'y': /* The year within the century (2 digits). */
368 _LEGAL_ALT(_ALT_E
| _ALT_O
);
369 if (!(_conv_num(&bp
, &relyear
, 0, 99)))
374 * Miscellaneous conversions.
376 case 'n': /* Any kind of white-space. */
384 default: /* Unknown/unsupported conversion. */
392 * We need to evaluate the two digit year spec (%y)
393 * last as we can get a century spec (%C) at any time.
396 if (century
== TM_YEAR_BASE
) {
398 tm
->tm_year
= relyear
+ 2000 - TM_YEAR_BASE
;
400 tm
->tm_year
= relyear
+ 1900 - TM_YEAR_BASE
;
402 tm
->tm_year
= relyear
+ century
- TM_YEAR_BASE
;
411 _conv_num(unsigned char **buf
, int *dest
, int llim
, int ulim
)
416 if (**buf
< '0' || **buf
> '9')
419 /* we use rulim to break out of the loop when we run out of digits */
422 result
+= *(*buf
)++ - '0';
424 } while ((result
* 10 <= ulim
) && rulim
&& **buf
>= '0' && **buf
<= '9');
426 if (result
< llim
|| result
> ulim
)