2 * Copyright (C) 2014 Internet Systems Consortium, Inc. ("ISC")
4 * Permission to use, copy, modify, and/or distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
8 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
9 * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
10 * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
11 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
12 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
13 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
14 * PERFORMANCE OF THIS SOFTWARE.
18 * Copyright (c) 1997, 1998 The NetBSD Foundation, Inc.
19 * All rights reserved.
21 * This code was contributed to The NetBSD Foundation by Klaus Klein.
23 * Redistribution and use in source and binary forms, with or without
24 * modification, are permitted provided that the following conditions
26 * 1. Redistributions of source code must retain the above copyright
27 * notice, this list of conditions and the following disclaimer.
28 * 2. Redistributions in binary form must reproduce the above copyright
29 * notice, this list of conditions and the following disclaimer in the
30 * documentation and/or other materials provided with the distribution.
32 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
33 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
34 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
35 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
36 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
37 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
38 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
39 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
40 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
41 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
42 * POSSIBILITY OF SUCH DAMAGE.
57 * Portable conversion routines for struct tm, replacing
58 * timegm() and strptime(), which are not available on all
59 * platforms and don't always behave the same way when they
64 * We do not implement alternate representations. However, we always
65 * check whether a given modifier is allowed for a certain conversion.
69 #define LEGAL_ALT(x) { if (alt_format & ~(x)) return (0); }
72 #define TM_YEAR_BASE 1900
75 static const char *day[7] = {
76 "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday",
79 static const char *abday[7] = {
80 "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
82 static const char *mon[12] = {
83 "January", "February", "March", "April", "May", "June", "July",
84 "August", "September", "October", "November", "December"
86 static const char *abmon[12] = {
87 "Jan", "Feb", "Mar", "Apr", "May", "Jun",
88 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
90 static const char *am_pm[2] = {
95 conv_num(const char **buf, int *dest, int llim, int ulim) {
98 /* The limit also determines the number of valid digits. */
101 if (**buf < '0' || **buf > '9')
106 result += *(*buf)++ - '0';
108 } while ((result * 10 <= ulim) &&
109 rulim && **buf >= '0' && **buf <= '9');
111 if (result < llim || result > ulim)
119 isc_tm_timegm(struct tm *tm) {
121 int i, yday = 0, leapday;
122 int mdays[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30 };
124 leapday = ((((tm->tm_year + 1900 ) % 4) == 0 &&
125 ((tm->tm_year + 1900 ) % 100) != 0) ||
126 ((tm->tm_year + 1900 ) % 400) == 0) ? 1 : 0;
129 yday = tm->tm_mday - 1;
130 for (i = 1; i <= tm->tm_mon; i++)
131 yday += mdays[i - 1];
134 (3600 * tm->tm_hour) +
136 ((tm->tm_year - 70) * 365) +
137 ((tm->tm_year - 69) / 4) -
138 ((tm->tm_year - 1) / 100) +
139 ((tm->tm_year + 299) / 400)));
144 isc_tm_strptime(const char *buf, const char *fmt, struct tm *tm) {
148 int alt_format, i, split_year = 0;
150 REQUIRE(buf != NULL);
151 REQUIRE(fmt != NULL);
154 memset(tm, 0, sizeof(struct tm));
158 while ((c = *fmt) != '\0') {
159 /* Clear `alternate' modifier prior to new conversion. */
162 /* Eat up white-space. */
163 if (isspace((unsigned char) c)) {
164 while (isspace((unsigned char) *bp))
171 if ((c = *fmt++) != '%')
175 again: switch (c = *fmt++) {
176 case '%': /* "%%" is converted to "%". */
183 * "Alternative" modifiers. Just set the appropriate flag
184 * and start over again.
186 case 'E': /* "%E?" alternative conversion modifier. */
191 case 'O': /* "%O?" alternative conversion modifier. */
197 * "Complex" conversion rules, implemented through recursion.
199 case 'c': /* Date and time, using the locale's format. */
201 if (!(bp = isc_tm_strptime(bp, "%x %X", tm)))
205 case 'D': /* The date as "%m/%d/%y". */
207 if (!(bp = isc_tm_strptime(bp, "%m/%d/%y", tm)))
211 case 'R': /* The time as "%H:%M". */
213 if (!(bp = isc_tm_strptime(bp, "%H:%M", tm)))
217 case 'r': /* The time in 12-hour clock representation. */
219 if (!(bp = isc_tm_strptime(bp, "%I:%M:%S %p", tm)))
223 case 'T': /* The time as "%H:%M:%S". */
225 if (!(bp = isc_tm_strptime(bp, "%H:%M:%S", tm)))
229 case 'X': /* The time, using the locale's format. */
231 if (!(bp = isc_tm_strptime(bp, "%H:%M:%S", tm)))
235 case 'x': /* The date, using the locale's format. */
237 if (!(bp = isc_tm_strptime(bp, "%m/%d/%y", tm)))
242 * "Elementary" conversion rules.
244 case 'A': /* The day of week, using the locale's form. */
247 for (i = 0; i < 7; i++) {
249 len = strlen(day[i]);
250 if (strncasecmp(day[i], bp, len) == 0)
253 /* Abbreviated name. */
254 len = strlen(abday[i]);
255 if (strncasecmp(abday[i], bp, len) == 0)
259 /* Nothing matched. */
267 case 'B': /* The month, using the locale's form. */
271 for (i = 0; i < 12; i++) {
273 len = strlen(mon[i]);
274 if (strncasecmp(mon[i], bp, len) == 0)
277 /* Abbreviated name. */
278 len = strlen(abmon[i]);
279 if (strncasecmp(abmon[i], bp, len) == 0)
283 /* Nothing matched. */
291 case 'C': /* The century number. */
293 if (!(conv_num(&bp, &i, 0, 99)))
297 tm->tm_year = (tm->tm_year % 100) + (i * 100);
299 tm->tm_year = i * 100;
304 case 'd': /* The day of month. */
307 if (!(conv_num(&bp, &tm->tm_mday, 1, 31)))
311 case 'k': /* The hour (24-hour clock representation). */
316 if (!(conv_num(&bp, &tm->tm_hour, 0, 23)))
320 case 'l': /* The hour (12-hour clock representation). */
325 if (!(conv_num(&bp, &tm->tm_hour, 1, 12)))
327 if (tm->tm_hour == 12)
331 case 'j': /* The day of year. */
333 if (!(conv_num(&bp, &i, 1, 366)))
338 case 'M': /* The minute. */
340 if (!(conv_num(&bp, &tm->tm_min, 0, 59)))
344 case 'm': /* The month. */
346 if (!(conv_num(&bp, &i, 1, 12)))
351 case 'p': /* The locale's equivalent of AM/PM. */
354 if (strcasecmp(am_pm[0], bp) == 0) {
355 if (tm->tm_hour > 11)
358 bp += strlen(am_pm[0]);
362 else if (strcasecmp(am_pm[1], bp) == 0) {
363 if (tm->tm_hour > 11)
367 bp += strlen(am_pm[1]);
371 /* Nothing matched. */
374 case 'S': /* The seconds. */
376 if (!(conv_num(&bp, &tm->tm_sec, 0, 61)))
380 case 'U': /* The week of year, beginning on sunday. */
381 case 'W': /* The week of year, beginning on monday. */
384 * XXX This is bogus, as we can not assume any valid
385 * information present in the tm structure at this
386 * point to calculate a real value, so just check the
389 if (!(conv_num(&bp, &i, 0, 53)))
393 case 'w': /* The day of week, beginning on sunday. */
395 if (!(conv_num(&bp, &tm->tm_wday, 0, 6)))
399 case 'Y': /* The year. */
401 if (!(conv_num(&bp, &i, 0, 9999)))
404 tm->tm_year = i - TM_YEAR_BASE;
407 case 'y': /* The year within 100 years of the epoch. */
408 LEGAL_ALT(ALT_E | ALT_O);
409 if (!(conv_num(&bp, &i, 0, 99)))
413 tm->tm_year = ((tm->tm_year / 100) * 100) + i;
418 tm->tm_year = i + 2000 - TM_YEAR_BASE;
420 tm->tm_year = i + 1900 - TM_YEAR_BASE;
424 * Miscellaneous conversions.
426 case 'n': /* Any kind of white-space. */
429 while (isspace((unsigned char) *bp))
434 default: /* Unknown/unsupported conversion. */
441 /* LINTED functional specification */