1 /* $NetBSD: tm.c,v 1.1.1.3 2014/12/10 03:34:44 christos Exp $ */
4 * Copyright (C) 2014 Internet Systems Consortium, Inc. ("ISC")
6 * Permission to use, copy, modify, and/or distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
10 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
11 * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
12 * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
13 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
14 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
15 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
16 * PERFORMANCE OF THIS SOFTWARE.
20 * Copyright (c) 1997, 1998 The NetBSD Foundation, Inc.
21 * All rights reserved.
23 * This code was contributed to The NetBSD Foundation by Klaus Klein.
25 * Redistribution and use in source and binary forms, with or without
26 * modification, are permitted provided that the following conditions
28 * 1. Redistributions of source code must retain the above copyright
29 * notice, this list of conditions and the following disclaimer.
30 * 2. Redistributions in binary form must reproduce the above copyright
31 * notice, this list of conditions and the following disclaimer in the
32 * documentation and/or other materials provided with the distribution.
34 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
35 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
36 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
37 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
38 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
39 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
40 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
41 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
42 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
43 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
44 * POSSIBILITY OF SUCH DAMAGE.
59 * Portable conversion routines for struct tm, replacing
60 * timegm() and strptime(), which are not available on all
61 * platforms and don't always behave the same way when they
66 * We do not implement alternate representations. However, we always
67 * check whether a given modifier is allowed for a certain conversion.
71 #define LEGAL_ALT(x) { if (alt_format & ~(x)) return (0); }
74 #define TM_YEAR_BASE 1900
77 static const char *day
[7] = {
78 "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday",
81 static const char *abday
[7] = {
82 "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
84 static const char *mon
[12] = {
85 "January", "February", "March", "April", "May", "June", "July",
86 "August", "September", "October", "November", "December"
88 static const char *abmon
[12] = {
89 "Jan", "Feb", "Mar", "Apr", "May", "Jun",
90 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
92 static const char *am_pm
[2] = {
97 conv_num(const char **buf
, int *dest
, int llim
, int ulim
) {
100 /* The limit also determines the number of valid digits. */
103 if (**buf
< '0' || **buf
> '9')
108 result
+= *(*buf
)++ - '0';
110 } while ((result
* 10 <= ulim
) &&
111 rulim
&& **buf
>= '0' && **buf
<= '9');
113 if (result
< llim
|| result
> ulim
)
121 isc_tm_timegm(struct tm
*tm
) {
123 int i
, yday
= 0, leapday
;
124 int mdays
[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30 };
126 leapday
= ((((tm
->tm_year
+ 1900 ) % 4) == 0 &&
127 ((tm
->tm_year
+ 1900 ) % 100) != 0) ||
128 ((tm
->tm_year
+ 1900 ) % 400) == 0) ? 1 : 0;
131 yday
= tm
->tm_mday
- 1;
132 for (i
= 1; i
<= tm
->tm_mon
; i
++)
133 yday
+= mdays
[i
- 1];
136 (3600 * tm
->tm_hour
) +
138 ((tm
->tm_year
- 70) * 365) +
139 ((tm
->tm_year
- 69) / 4) -
140 ((tm
->tm_year
- 1) / 100) +
141 ((tm
->tm_year
+ 299) / 400)));
146 isc_tm_strptime(const char *buf
, const char *fmt
, struct tm
*tm
) {
150 int alt_format
, i
, split_year
= 0;
152 REQUIRE(buf
!= NULL
);
153 REQUIRE(fmt
!= NULL
);
156 memset(tm
, 0, sizeof(struct tm
));
160 while ((c
= *fmt
) != '\0') {
161 /* Clear `alternate' modifier prior to new conversion. */
164 /* Eat up white-space. */
165 if (isspace((unsigned char) c
)) {
166 while (isspace((unsigned char) *bp
))
173 if ((c
= *fmt
++) != '%')
177 again
: switch (c
= *fmt
++) {
178 case '%': /* "%%" is converted to "%". */
185 * "Alternative" modifiers. Just set the appropriate flag
186 * and start over again.
188 case 'E': /* "%E?" alternative conversion modifier. */
193 case 'O': /* "%O?" alternative conversion modifier. */
199 * "Complex" conversion rules, implemented through recursion.
201 case 'c': /* Date and time, using the locale's format. */
203 if (!(bp
= isc_tm_strptime(bp
, "%x %X", tm
)))
207 case 'D': /* The date as "%m/%d/%y". */
209 if (!(bp
= isc_tm_strptime(bp
, "%m/%d/%y", tm
)))
213 case 'R': /* The time as "%H:%M". */
215 if (!(bp
= isc_tm_strptime(bp
, "%H:%M", tm
)))
219 case 'r': /* The time in 12-hour clock representation. */
221 if (!(bp
= isc_tm_strptime(bp
, "%I:%M:%S %p", tm
)))
225 case 'T': /* The time as "%H:%M:%S". */
227 if (!(bp
= isc_tm_strptime(bp
, "%H:%M:%S", tm
)))
231 case 'X': /* The time, using the locale's format. */
233 if (!(bp
= isc_tm_strptime(bp
, "%H:%M:%S", tm
)))
237 case 'x': /* The date, using the locale's format. */
239 if (!(bp
= isc_tm_strptime(bp
, "%m/%d/%y", tm
)))
244 * "Elementary" conversion rules.
246 case 'A': /* The day of week, using the locale's form. */
249 for (i
= 0; i
< 7; i
++) {
251 len
= strlen(day
[i
]);
252 if (strncasecmp(day
[i
], bp
, len
) == 0)
255 /* Abbreviated name. */
256 len
= strlen(abday
[i
]);
257 if (strncasecmp(abday
[i
], bp
, len
) == 0)
261 /* Nothing matched. */
269 case 'B': /* The month, using the locale's form. */
273 for (i
= 0; i
< 12; i
++) {
275 len
= strlen(mon
[i
]);
276 if (strncasecmp(mon
[i
], bp
, len
) == 0)
279 /* Abbreviated name. */
280 len
= strlen(abmon
[i
]);
281 if (strncasecmp(abmon
[i
], bp
, len
) == 0)
285 /* Nothing matched. */
293 case 'C': /* The century number. */
295 if (!(conv_num(&bp
, &i
, 0, 99)))
299 tm
->tm_year
= (tm
->tm_year
% 100) + (i
* 100);
301 tm
->tm_year
= i
* 100;
306 case 'd': /* The day of month. */
309 if (!(conv_num(&bp
, &tm
->tm_mday
, 1, 31)))
313 case 'k': /* The hour (24-hour clock representation). */
318 if (!(conv_num(&bp
, &tm
->tm_hour
, 0, 23)))
322 case 'l': /* The hour (12-hour clock representation). */
327 if (!(conv_num(&bp
, &tm
->tm_hour
, 1, 12)))
329 if (tm
->tm_hour
== 12)
333 case 'j': /* The day of year. */
335 if (!(conv_num(&bp
, &i
, 1, 366)))
340 case 'M': /* The minute. */
342 if (!(conv_num(&bp
, &tm
->tm_min
, 0, 59)))
346 case 'm': /* The month. */
348 if (!(conv_num(&bp
, &i
, 1, 12)))
353 case 'p': /* The locale's equivalent of AM/PM. */
356 if (strcasecmp(am_pm
[0], bp
) == 0) {
357 if (tm
->tm_hour
> 11)
360 bp
+= strlen(am_pm
[0]);
364 else if (strcasecmp(am_pm
[1], bp
) == 0) {
365 if (tm
->tm_hour
> 11)
369 bp
+= strlen(am_pm
[1]);
373 /* Nothing matched. */
376 case 'S': /* The seconds. */
378 if (!(conv_num(&bp
, &tm
->tm_sec
, 0, 61)))
382 case 'U': /* The week of year, beginning on sunday. */
383 case 'W': /* The week of year, beginning on monday. */
386 * XXX This is bogus, as we can not assume any valid
387 * information present in the tm structure at this
388 * point to calculate a real value, so just check the
391 if (!(conv_num(&bp
, &i
, 0, 53)))
395 case 'w': /* The day of week, beginning on sunday. */
397 if (!(conv_num(&bp
, &tm
->tm_wday
, 0, 6)))
401 case 'Y': /* The year. */
403 if (!(conv_num(&bp
, &i
, 0, 9999)))
406 tm
->tm_year
= i
- TM_YEAR_BASE
;
409 case 'y': /* The year within 100 years of the epoch. */
410 LEGAL_ALT(ALT_E
| ALT_O
);
411 if (!(conv_num(&bp
, &i
, 0, 99)))
415 tm
->tm_year
= ((tm
->tm_year
/ 100) * 100) + i
;
420 tm
->tm_year
= i
+ 2000 - TM_YEAR_BASE
;
422 tm
->tm_year
= i
+ 1900 - TM_YEAR_BASE
;
426 * Miscellaneous conversions.
428 case 'n': /* Any kind of white-space. */
431 while (isspace((unsigned char) *bp
))
436 default: /* Unknown/unsupported conversion. */
443 /* LINTED functional specification */