]>
git.saurik.com Git - apple/libc.git/blob - stdtime/FreeBSD/strptime.c
2 * Copyright (c) 2014 Gary Mills
3 * Copyright 2011, Nexenta Systems, Inc. All rights reserved.
4 * Copyright (c) 1994 Powerdog Industries. All rights reserved.
6 * Copyright (c) 2011 The FreeBSD Foundation
8 * Portions of this software were developed by David Chisnall
9 * under sponsorship from the FreeBSD Foundation.
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
18 * in the documentation and/or other materials provided with the
21 * THIS SOFTWARE IS PROVIDED BY POWERDOG INDUSTRIES ``AS IS'' AND ANY
22 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
24 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE POWERDOG INDUSTRIES BE
25 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
26 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
27 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
28 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
29 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
30 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
31 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33 * The views and conclusions contained in the software and documentation
34 * are those of the authors and should not be interpreted as representing
35 * official policies, either expressed or implied, of Powerdog Industries.
38 #include <sys/cdefs.h>
41 static char copyright
[] __unused
=
42 "@(#) Copyright (c) 1994 Powerdog Industries. All rights reserved.";
43 static char sccsid
[] __unused
= "@(#)strptime.c 0.1 (Powerdog) 94/03/27";
44 #endif /* !defined NOID */
46 __FBSDID("$FreeBSD$");
48 #include "xlocale_private.h"
50 #include "namespace.h"
58 #include "un-namespace.h"
59 #include "libc_private.h"
60 #include "timelocal.h"
63 time_t _mktime(struct tm
*, const char *);
65 #define asizeof(a) (sizeof(a) / sizeof((a)[0]))
67 enum {CONVERT_NONE
, CONVERT_GMT
, CONVERT_ZONE
};
68 enum week_kind
{ WEEK_U
= 'U', WEEK_V
= 'V', WEEK_W
= 'W'};
70 #define _strptime(b,f,t,c,l) _strptime0(b,f,t,c,l,FLAG_NONE,0,WEEK_U)
72 #define FLAG_NONE 0x01
73 #define FLAG_YEAR 0x02
74 #define FLAG_MONTH 0x04
75 #define FLAG_YDAY 0x08
76 #define FLAG_MDAY 0x10
77 #define FLAG_WDAY 0x20
78 #define FLAG_WEEK 0x40
79 #define FLAG_CENTURY 0x100
80 #define FLAG_YEAR_IN_CENTURY 0x200
83 * Calculate the week day of the first day of a year. Valid for
84 * the Gregorian calendar, which began Sept 14, 1752 in the UK
85 * and its colonies. Ref:
86 * http://en.wikipedia.org/wiki/Determination_of_the_day_of_the_week
90 first_wday_of(int year
)
92 return (((2 * (3 - (year
/ 100) % 4)) + (year
% 100) +
93 ((year
% 100) / 4) + (isleap(year
) ? 6 : 0) + 1) % 7);
97 _strptime0(const char *buf
, const char *fmt
, struct tm
*tm
, int *convp
, locale_t locale
, int flags
, int week_number
, enum week_kind week_kind
)
103 int Ealternative
, Oalternative
;
104 const struct lc_time_T
*tptr
= __get_current_time_locale(locale
);
105 static int start_of_month
[2][13] = {
106 {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365},
107 {0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366}
115 if (isspace_l((unsigned char)c
, locale
))
117 isspace_l((unsigned char)*buf
, locale
))
119 else if (c
!= *buf
++)
135 buf
= _strptime(buf
, tptr
->date_fmt
, tm
, convp
, locale
);
138 flags
|= FLAG_WDAY
| FLAG_MONTH
| FLAG_MDAY
| FLAG_YEAR
;
142 if (!isdigit_l((unsigned char)*buf
, locale
))
145 /* XXX This will break for 3-digit centuries. */
147 for (i
= 0; len
&& *buf
!= 0 &&
148 isdigit_l((unsigned char)*buf
, locale
); buf
++) {
156 if (flags
& FLAG_YEAR_IN_CENTURY
) {
157 tm
->tm_year
= i
* 100 + (tm
->tm_year
% 100) - TM_YEAR_BASE
;
158 flags
&= ~FLAG_YEAR_IN_CENTURY
;
160 tm
->tm_year
= i
* 100 - TM_YEAR_BASE
;
162 flags
|= FLAG_CENTURY
;
168 buf
= _strptime(buf
, tptr
->c_fmt
, tm
, convp
, locale
);
171 flags
|= FLAG_WDAY
| FLAG_MONTH
| FLAG_MDAY
| FLAG_YEAR
;
172 flags
&= ~(FLAG_CENTURY
| FLAG_YEAR_IN_CENTURY
);
176 buf
= _strptime(buf
, "%m/%d/%y", tm
, convp
, locale
);
179 flags
|= FLAG_MONTH
| FLAG_MDAY
| FLAG_YEAR
;
180 flags
&= ~(FLAG_CENTURY
| FLAG_YEAR_IN_CENTURY
);
184 if (Ealternative
|| Oalternative
)
187 if (*ptr
== '%') return (NULL
);
191 if (Ealternative
|| Oalternative
)
194 if (*ptr
== '%') return (NULL
);
198 buf
= _strptime(buf
, "%Y-%m-%d", tm
, convp
, locale
);
201 flags
|= FLAG_MONTH
| FLAG_MDAY
| FLAG_YEAR
;
202 flags
&= ~(FLAG_CENTURY
| FLAG_YEAR_IN_CENTURY
);
206 buf
= _strptime(buf
, "%H:%M", tm
, convp
, locale
);
212 buf
= _strptime(buf
, tptr
->ampm_fmt
, tm
, convp
, locale
);
218 buf
= _strptime(buf
, "%H:%M:%S", tm
, convp
, locale
);
224 buf
= _strptime(buf
, tptr
->X_fmt
, tm
, convp
, locale
);
230 buf
= _strptime(buf
, tptr
->x_fmt
, tm
, convp
, locale
);
233 flags
|= FLAG_MONTH
| FLAG_MDAY
| FLAG_YEAR
;
234 flags
&= ~(FLAG_CENTURY
| FLAG_YEAR_IN_CENTURY
);
238 if (!isdigit_l((unsigned char)*buf
, locale
))
242 for (i
= 0; len
&& *buf
!= 0 &&
243 isdigit_l((unsigned char)*buf
, locale
); buf
++){
248 if (i
< 1 || i
> 366)
259 isspace_l((unsigned char)*buf
, locale
))
262 if (!isdigit_l((unsigned char)*buf
, locale
))
266 for (i
= 0; len
&& *buf
!= 0 &&
267 isdigit_l((unsigned char)*buf
, locale
); buf
++){
290 * Of these, %l is the only specifier explicitly
291 * documented as not being zero-padded. However,
292 * there is no harm in allowing zero-padding.
294 * XXX The %l specifier may gobble one too many
295 * digits if used incorrectly.
297 if (!isdigit_l((unsigned char)*buf
, locale
))
301 for (i
= 0; len
&& *buf
!= 0 &&
302 isdigit_l((unsigned char)*buf
, locale
); buf
++) {
307 if (c
== 'H' || c
== 'k') {
319 * XXX This is bogus if parsed before hour-related
322 if (tm
->tm_hour
> 12)
325 len
= strlen(tptr
->am
);
326 if (strncasecmp_l(buf
, tptr
->am
, len
, locale
) == 0) {
327 if (tm
->tm_hour
== 12)
333 len
= strlen(tptr
->pm
);
334 if (strncasecmp_l(buf
, tptr
->pm
, len
, locale
) == 0) {
335 if (tm
->tm_hour
!= 12)
345 for (i
= 0; i
< asizeof(tptr
->weekday
); i
++) {
346 len
= strlen(tptr
->weekday
[i
]);
347 if (strncasecmp_l(buf
, tptr
->weekday
[i
],
350 len
= strlen(tptr
->wday
[i
]);
351 if (strncasecmp_l(buf
, tptr
->wday
[i
],
355 if (i
== asizeof(tptr
->weekday
))
363 case 'U': /* Sunday week */
364 case 'V': /* ISO 8601 week */
365 case 'W': /* Monday week */
366 if (!isdigit_l((unsigned char)*buf
, locale
))
370 for (i
= 0; len
&& *buf
!= 0 &&
371 isdigit_l((unsigned char)*buf
, locale
); buf
++) {
378 if (c
== WEEK_V
&& i
< 1)
387 case 'u': /* [1,7] */
388 case 'w': /* [0,6] */
389 if (!isdigit_l((unsigned char)*buf
, locale
))
393 if (i
< 0 || i
> 7 || (c
== 'u' && i
< 1) ||
405 * With %e format, our strftime(3) adds a blank space
406 * before single digits.
409 isspace_l((unsigned char)*buf
, locale
))
414 * The %e specifier was once explicitly documented as
415 * not being zero-padded but was later changed to
416 * equivalent to %d. There is no harm in allowing
419 * XXX The %e specifier may gobble one too many
420 * digits if used incorrectly.
422 /* Leading space is ok if date is single digit */
424 if (isspace_l((unsigned char)buf
[0], locale
) &&
425 isdigit_l((unsigned char)buf
[1], locale
) &&
426 !isdigit_l((unsigned char)buf
[2], locale
)) {
430 if (!isdigit_l((unsigned char)*buf
, locale
))
433 for (i
= 0; len
&& *buf
!= 0 &&
434 isdigit_l((unsigned char)*buf
, locale
); buf
++) {
450 for (i
= 0; i
< asizeof(tptr
->month
); i
++) {
453 len
= strlen(tptr
->alt_month
[i
]);
454 if (strncasecmp_l(buf
,
460 len
= strlen(tptr
->month
[i
]);
461 if (strncasecmp_l(buf
, tptr
->month
[i
],
467 * Try the abbreviated month name if the full name
468 * wasn't found and Oalternative was not requested.
470 if (i
== asizeof(tptr
->month
) && !Oalternative
) {
471 for (i
= 0; i
< asizeof(tptr
->month
); i
++) {
472 len
= strlen(tptr
->mon
[i
]);
473 if (strncasecmp_l(buf
, tptr
->mon
[i
],
478 if (i
== asizeof(tptr
->month
))
488 if (!isdigit_l((unsigned char)*buf
, locale
))
492 for (i
= 0; len
&& *buf
!= 0 &&
493 isdigit_l((unsigned char)*buf
, locale
); buf
++) {
515 n
= strtol_l(buf
, &cp
, 10, locale
);
516 if (errno
== ERANGE
|| (long)(t
= n
) != n
) {
522 if (gmtime_r(&t
, tm
) == NULL
)
524 *convp
= CONVERT_GMT
;
525 flags
|= FLAG_YDAY
| FLAG_WDAY
| FLAG_MONTH
|
526 FLAG_MDAY
| FLAG_YEAR
;
527 flags
&= ~(FLAG_CENTURY
| FLAG_YEAR_IN_CENTURY
);
534 isspace_l((unsigned char)*buf
, locale
))
537 if (!isdigit_l((unsigned char)*buf
, locale
))
543 const char *savebuf
= buf
;
547 for (len
= 0; *buf
!= 0 && isdigit_l((unsigned char)*buf
, locale
); buf
++) {
560 * Conformance requires %Y to be more then 4
561 * digits. However, there are several cases
562 * where %Y is immediately followed by other
563 * digits values. So we do the conformance
564 * case first (as many digits as possible),
565 * and if we fail, we backup and try just 4
568 if (len
> 4 && !overflow
) {
569 struct tm savetm
= *tm
;
570 int saveconv
= *convp
;
571 const char *saveptr
= ptr
;
577 tm
->tm_year
= i64
- 1900;
579 if (*buf
!= 0 && isspace_l((unsigned char)*buf
, locale
))
580 while (*ptr
!= 0 && !isspace_l((unsigned char)*ptr
, locale
) && *ptr
!= '%')
582 ret
= _strptime0(buf
, ptr
, tm
, convp
, locale
, flags
, week_number
, week_kind
);
584 /* Failed, so try 4-digit year */
593 #else /* !__DARWIN_UNIX03 */
594 len
= (c
== 'Y') ? 4 : 2;
595 #endif /* __DARWIN_UNIX03 */
597 for (i
= 0; len
&& *buf
!= 0 &&
598 isdigit_l((unsigned char)*buf
, locale
); buf
++) {
605 #endif /* __DARWIN_UNIX03 */
612 } else if (c
== 'y' && flags
& FLAG_CENTURY
) {
613 i
= tm
->tm_year
+ (i
% 100);
614 flags
&= ~FLAG_CENTURY
;
615 } else if (c
== 'y'){
616 if (i
< 69) i
+= 100;
617 flags
|= FLAG_YEAR_IN_CENTURY
;
623 flags
&= ~(FLAG_CENTURY
| FLAG_YEAR_IN_CENTURY
);
633 for (cp
= buf
; *cp
&&
634 isupper_l((unsigned char)*cp
, locale
); ++cp
) {
638 if (len
== 3 && strncmp(buf
, "GMT", 3) == 0) {
639 *convp
= CONVERT_GMT
;
645 tzlen
= strlen(tzname
[0]);
646 if (len
== tzlen
&& strncmp(buf
, tzname
[0], tzlen
) == 0) {
651 tzlen
= strlen(tzname
[1]);
652 if (len
== tzlen
&& strncmp(buf
, tzname
[1], tzlen
) == 0) {
664 if ((buf
[0] != '+' && buf
[0] != '-')
665 || !isdigit_l((unsigned char)buf
[1], locale
)
666 || !isdigit_l((unsigned char)buf
[2], locale
)
667 || !isdigit_l((unsigned char)buf
[3], locale
)
668 || !isdigit_l((unsigned char)buf
[4], locale
))
670 sscanf(buf
, "%c%2d%2d", &sign
, &hr
, &min
);
671 *convp
= CONVERT_ZONE
;
672 tm
->tm_gmtoff
= 60 * (60 * hr
+ min
);
674 tm
->tm_gmtoff
= -tm
->tm_gmtoff
;
681 if (!isspace((unsigned char)*buf
))
683 while (isspace_l((unsigned char)*buf
, locale
))
692 if (!(flags
& FLAG_YDAY
) && (flags
& FLAG_YEAR
)) {
693 if ((flags
& (FLAG_MONTH
| FLAG_MDAY
)) ==
694 (FLAG_MONTH
| FLAG_MDAY
)) {
695 tm
->tm_yday
= start_of_month
[isleap(tm
->tm_year
+
696 TM_YEAR_BASE
)][tm
->tm_mon
] + (tm
->tm_mday
- 1);
698 } else if (flags
& FLAG_WEEK
){
699 int day_offset
= week_kind
== WEEK_U
? TM_SUNDAY
: TM_MONDAY
;
700 int fwo
= first_wday_of(tm
->tm_year
+ TM_YEAR_BASE
);
702 /* No incomplete week (week 0). */
703 if (week_number
== 0 && fwo
== day_offset
)
706 if (!(flags
& FLAG_WDAY
)) {
708 * Set the date to the first Sunday (or Monday)
709 * of the specified week of the year.
711 tm
->tm_wday
= day_offset
;
716 * Start our yday at the first day of the relevant week type.
718 int tmpyday
= (7 - fwo
+ day_offset
) % 7;
721 * ISO Weeks start counting from the first week with at least
722 * four days. If our first week had that, subtract off a week.
724 if (week_kind
== WEEK_V
&& fwo
> TM_MONDAY
&& fwo
<= TM_THURSDAY
) {
727 /* Advance the relevant number of weeks */
728 tmpyday
+= (week_number
- 1) * 7;
729 /* And go to the right day of week */
730 tmpyday
+= (tm
->tm_wday
- day_offset
+ 7) % 7;
732 /* Impossible yday for incomplete week (week 0). */
734 if (flags
& FLAG_WDAY
)
738 tm
->tm_yday
= tmpyday
;
743 if ((flags
& (FLAG_YEAR
| FLAG_YDAY
)) == (FLAG_YEAR
| FLAG_YDAY
)) {
744 if (!(flags
& FLAG_MONTH
)) {
746 while (tm
->tm_yday
>=
747 start_of_month
[isleap(tm
->tm_year
+
753 start_of_month
[isleap(tm
->tm_year
+
760 if (!(flags
& FLAG_MDAY
)) {
761 tm
->tm_mday
= tm
->tm_yday
-
762 start_of_month
[isleap(tm
->tm_year
+ TM_YEAR_BASE
)]
766 if (!(flags
& FLAG_WDAY
)) {
767 wday_offset
= first_wday_of(tm
->tm_year
+ TM_YEAR_BASE
);
768 tm
->tm_wday
= (wday_offset
+ tm
->tm_yday
) % 7;
773 return ((char *)buf
);
778 strptime(const char * __restrict buf
, const char * __restrict fmt
,
779 struct tm
* __restrict tm
)
781 return strptime_l(buf
, fmt
, tm
, __current_locale());
784 extern time_t timeoff(struct tm
*, long);
787 strptime_l(const char * __restrict buf
, const char * __restrict fmt
,
788 struct tm
* __restrict tm
, locale_t loc
)
793 NORMALIZE_LOCALE(loc
);
796 ret
= _strptime(buf
, fmt
, tm
, &conv
, loc
);
807 long offset
= tm
->tm_gmtoff
;
809 t
= timeoff(tm
, offset
);