1 /* $NetBSD: strptime.c,v 1.28 2008/04/28 20:23:01 martin 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.
36 #include <sys/cdefs.h>
37 #if defined(LIBC_SCCS) && !defined(lint)
38 __RCSID("$NetBSD: strptime.c,v 1.28 2008/04/28 20:23:01 martin Exp $");
42 #include "../locale/setlocale.h"
44 #include "namespace.h"
45 #include <sys/localedef.h>
55 # define TM_GMTOFF __TM_GMTOFF
58 # define TM_ZONE __TM_ZONE
62 __weak_alias(strptime
,_strptime
)
65 #define _ctloc(x) (_CurrentTimeLocale->x)
69 #define LEGAL_ALT(x) { if (alt_format & ~(x)) return NULL; }
71 static const int _DAYS_BEFORE_MONTH
[12] =
72 {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334};
79 #define SET_YMD (SET_YEAR | SET_MON | SET_MDAY)
81 static const char gmt
[4] = { "GMT" };
83 typedef struct _era_info_t
{
84 size_t num
; /* Only in first entry: Number of entries,
86 int dir
; /* Direction */
87 long offset
; /* Number of year closest to start_date in the era. */
88 struct tm start
; /* Start date of era */
89 struct tm end
; /* End date of era */
90 CHAR
*era_C
; /* Era string */
91 CHAR
*era_Y
; /* Replacement for %EY */
95 free_era_info (era_info_t
*era_info
)
97 size_t num
= era_info
->num
;
99 for (size_t i
= 0; i
< num
; ++i
)
101 free (era_info
[i
].era_C
);
102 free (era_info
[i
].era_Y
);
108 get_era_info (const char *era
, locale_t locale
)
111 era_info_t
*ei
= NULL
;
112 size_t num
= 0, cur
= 0, len
;
117 era_info_t
*tmp
= (era_info_t
*) realloc (ei
, num
* sizeof (era_info_t
));
126 ei
[cur
].dir
= (*era
== '+') ? 1 : -1;
128 ei
[cur
].offset
= strtol_l (era
, &c
, 10, locale
);
130 ei
[cur
].start
.tm_year
= strtol_l (era
, &c
, 10, locale
);
131 /* Adjust offset for negative gregorian dates. */
132 if (ei
[cur
].start
.tm_year
< 0)
133 ++ei
[cur
].start
.tm_year
;
134 ei
[cur
].start
.tm_mon
= strtol_l (c
+ 1, &c
, 10, locale
);
135 ei
[cur
].start
.tm_mday
= strtol_l (c
+ 1, &c
, 10, locale
);
136 ei
[cur
].start
.tm_hour
= ei
[cur
].start
.tm_min
= ei
[cur
].start
.tm_sec
= 0;
138 if (era
[0] == '-' && era
[1] == '*')
140 ei
[cur
].end
= ei
[cur
].start
;
141 ei
[cur
].start
.tm_year
= INT_MIN
;
142 ei
[cur
].start
.tm_mon
= ei
[cur
].start
.tm_mday
= ei
[cur
].start
.tm_hour
143 = ei
[cur
].start
.tm_min
= ei
[cur
].start
.tm_sec
= 0;
146 else if (era
[0] == '+' && era
[1] == '*')
148 ei
[cur
].end
.tm_year
= INT_MAX
;
149 ei
[cur
].end
.tm_mon
= 12;
150 ei
[cur
].end
.tm_mday
= 31;
151 ei
[cur
].end
.tm_hour
= 23;
152 ei
[cur
].end
.tm_min
= ei
[cur
].end
.tm_sec
= 59;
157 ei
[cur
].end
.tm_year
= strtol_l (era
, &c
, 10, locale
);
158 /* Adjust offset for negative gregorian dates. */
159 if (ei
[cur
].end
.tm_year
< 0)
160 ++ei
[cur
].end
.tm_year
;
161 ei
[cur
].end
.tm_mon
= strtol_l (c
+ 1, &c
, 10, locale
);
162 ei
[cur
].end
.tm_mday
= strtol_l (c
+ 1, &c
, 10, locale
);
163 ei
[cur
].end
.tm_mday
= 31;
164 ei
[cur
].end
.tm_hour
= 23;
165 ei
[cur
].end
.tm_min
= ei
[cur
].end
.tm_sec
= 59;
169 c
= strchr (era
, ':');
171 ei
[cur
].era_C
= (CHAR
*) malloc ((len
+ 1) * sizeof (CHAR
));
178 strncpy (ei
[cur
].era_C
, era
, len
);
180 ei
[cur
].era_C
[len
] = '\0';
183 c
= strchr (era
, ';');
185 c
= strchr (era
, '\0');
187 ei
[cur
].era_Y
= (CHAR
*) malloc ((len
+ 1) * sizeof (CHAR
));
190 free (ei
[cur
].era_C
);
195 strncpy (ei
[cur
].era_Y
, era
, len
);
197 ei
[cur
].era_Y
[len
] = '\0';
206 typedef struct _alt_digits_t
{
212 static alt_digits_t
*
213 get_alt_digits (const char *alt_digits
)
220 adi
= (alt_digits_t
*) calloc (1, sizeof (alt_digits_t
));
224 /* Compute number of alt_digits. */
226 for (a
= alt_digits
; (e
= strchr (a
, ';')) != NULL
; a
= e
+ 1)
228 /* Allocate the `digit' array, which is an array of `num' pointers into
230 adi
->digit
= (CHAR
**) calloc (adi
->num
, sizeof (CHAR
**));
236 /* Compute memory required for `buffer'. */
237 len
= strlen (alt_digits
);
239 adi
->buffer
= (CHAR
*) malloc ((len
+ 1) * sizeof (CHAR
));
246 /* Store digits in it. */
247 strcpy (adi
->buffer
, alt_digits
);
248 /* Store the pointers into `buffer' into the appropriate `digit' slot. */
249 for (len
= 0, aa
= adi
->buffer
; (ae
= strchr (aa
, ';')) != NULL
;
253 adi
->digit
[len
] = aa
;
255 adi
->digit
[len
] = aa
;
260 free_alt_digits (alt_digits_t
*adi
)
267 static const unsigned char *
268 find_alt_digits (const unsigned char *bp
, alt_digits_t
*adi
, uint
*pval
)
270 /* This is rather error-prone, but the entire idea of alt_digits
271 isn't thought out well. If you start to look for matches at the
272 start, there's a high probability that you find short matches but
273 the entire translation is wrong. So we scan the alt_digits array
274 from the highest to the lowest digits instead, hoping that it's
275 more likely to catch digits consisting of multiple characters. */
276 for (int i
= (int) adi
->num
- 1; i
>= 0; --i
)
278 size_t len
= strlen (adi
->digit
[i
]);
279 if (!strncmp ((const char *) bp
, adi
->digit
[i
], len
))
289 is_leap_year (int year
)
291 return (year
% 4) == 0 && ((year
% 100) != 0 || (year
% 400) == 0);
299 while (--year
>= 1970)
300 ret
= (ret
+ 365 + is_leap_year (year
)) % 7;
304 /* This simplifies the calls to __conv_num enormously. */
305 #define ALT_DIGITS ((alt_format & ALT_O) ? *alt_digits : NULL)
307 #define conv_num(_b,_d,_l,_u,_a) \
309 const unsigned char *_ret; \
310 _ret = __conv_num((_b),(_d),(_l),(_u),(_a)); \
316 static const unsigned char *__conv_num(const unsigned char *, int *, uint
, uint
,
318 static const unsigned char *find_string(const unsigned char *, int *,
319 const char * const *,
320 const char * const *, int,
324 __strptime(const char *buf
, const char *fmt
, struct tm
*tm
,
325 era_info_t
**era_info
, alt_digits_t
**alt_digits
,
329 const unsigned char *bp
;
330 int alt_format
, i
, split_year
= 0;
331 era_info_t
*era
= NULL
;
332 int era_offset
, got_eoff
= 0;
341 bp
= (const unsigned char *)buf
;
342 const struct lc_time_T
*_CurrentTimeLocale
= __get_time_locale (locale
);
344 while (bp
!= NULL
&& (c
= *fmt
++) != '\0') {
345 /* Clear `alternate' modifier prior to new conversion. */
351 /* Eat up white-space. */
352 if (isspace_l(c
, locale
)) {
353 while (isspace_l(*bp
, locale
))
362 again
: switch (c
= *fmt
++) {
363 case '%': /* "%%" is converted to "%". */
371 * "Alternative" modifiers. Just set the appropriate flag
372 * and start over again.
374 case 'E': /* "%E?" alternative conversion modifier. */
377 if (!*era_info
&& *_CurrentTimeLocale
->era
)
378 *era_info
= get_era_info (_CurrentTimeLocale
->era
,
382 case 'O': /* "%O?" alternative conversion modifier. */
385 if (!*alt_digits
&& *_CurrentTimeLocale
->alt_digits
)
387 get_alt_digits (_CurrentTimeLocale
->alt_digits
);
396 case '1': case '2': case '3': case '4': case '5':
397 case '6': case '7': case '8': case '9':
398 /* POSIX-1.2008 maximum field width. Per POSIX,
399 the width is only defined for the 'C', 'F', and 'Y'
400 conversion specifiers. */
404 width
= strtoul_l (fmt
- 1, &end
, 10, locale
);
405 fmt
= (const char *) end
;
409 * "Complex" conversion rules, implemented through recursion.
411 case 'c': /* Date and time, using the locale's format. */
412 new_fmt
= (alt_format
& ALT_E
)
413 ? _ctloc (era_d_t_fmt
) : _ctloc(c_fmt
);
415 ymd
|= SET_WDAY
| SET_YMD
;
418 case 'D': /* The date as "%m/%d/%y". */
419 new_fmt
= "%m/%d/%y";
424 case 'F': /* The date as "%Y-%m-%d". */
427 char *tmp
= __strptime ((const char *) bp
, "%Y-%m-%d",
428 tm
, era_info
, alt_digits
,
430 /* width max chars converted, default 10, < 6 => 6 */
431 if (tmp
&& (char *) bp
+
432 (!width
? 10 : width
< 6 ? 6 : width
) < tmp
)
434 bp
= (const unsigned char *) tmp
;
439 case 'R': /* The time as "%H:%M". */
444 case 'r': /* The time in 12-hour clock representation. */
445 new_fmt
=_ctloc(ampm_fmt
);
449 case 'T': /* The time as "%H:%M:%S". */
450 new_fmt
= "%H:%M:%S";
454 case 'X': /* The time, using the locale's format. */
455 new_fmt
= (alt_format
& ALT_E
)
456 ? _ctloc (era_t_fmt
) : _ctloc(X_fmt
);
460 case 'x': /* The date, using the locale's format. */
461 new_fmt
= (alt_format
& ALT_E
)
462 ? _ctloc (era_d_fmt
) : _ctloc(x_fmt
);
466 bp
= (const unsigned char *)
467 __strptime((const char *)bp
, new_fmt
, tm
,
468 era_info
, alt_digits
, locale
);
472 * "Elementary" conversion rules.
474 case 'A': /* The day of week, using the locale's form. */
476 bp
= find_string(bp
, &tm
->tm_wday
, _ctloc(weekday
),
477 _ctloc(wday
), 7, locale
);
482 case 'B': /* The month, using the locale's form. */
485 bp
= find_string(bp
, &tm
->tm_mon
, _ctloc(month
),
486 _ctloc(mon
), 12, locale
);
491 case 'C': /* The century number. */
494 if ((alt_format
& ALT_E
) && *era_info
)
496 /* With E modifier, an era. We potentially
497 don't know the era offset yet, so we have to
498 store the value in a local variable.
499 The final computation of tm_year is only done
500 right before this function returns. */
501 size_t num
= (*era_info
)->num
;
502 for (size_t i
= 0; i
< num
; ++i
)
503 if (!strncmp ((const char *) bp
,
504 (*era_info
)[i
].era_C
,
505 strlen ((*era_info
)[i
].era_C
)))
507 era
= (*era_info
) + i
;
508 bp
+= strlen (era
->era_C
);
516 for (ulim
= 99; width
&& width
< 2; ++width
)
518 bp
= conv_num(bp
, &i
, 0, ulim
, NULL
);
520 i
= i
* 100 - TM_YEAR_BASE
;
522 i
+= tm
->tm_year
% 100;
529 case 'd': /* The day of month. */
533 bp
= conv_num(bp
, &tm
->tm_mday
, 1, 31, ALT_DIGITS
);
536 case 'k': /* The hour (24-hour clock representation). */
541 bp
= conv_num(bp
, &tm
->tm_hour
, 0, 23, ALT_DIGITS
);
545 case 'l': /* The hour (12-hour clock representation). */
550 bp
= conv_num(bp
, &tm
->tm_hour
, 1, 12, ALT_DIGITS
);
551 if (tm
->tm_hour
== 12)
556 case 'j': /* The day of year. */
558 bp
= conv_num(bp
, &i
, 1, 366, NULL
);
564 case 'M': /* The minute. */
566 bp
= conv_num(bp
, &tm
->tm_min
, 0, 59, ALT_DIGITS
);
569 case 'm': /* The month. */
573 bp
= conv_num(bp
, &i
, 1, 12, ALT_DIGITS
);
577 case 'p': /* The locale's equivalent of AM/PM. */
578 bp
= find_string(bp
, &i
, _ctloc(am_pm
), NULL
, 2,
584 case 'q': /* The quarter year. GNU extension. */
587 bp
= conv_num(bp
, &i
, 1, 4, ALT_DIGITS
);
588 tm
->tm_mon
= (i
- 1)*3;
592 case 'S': /* The seconds. */
594 bp
= conv_num(bp
, &tm
->tm_sec
, 0, 61, ALT_DIGITS
);
597 case 's' : /* The seconds since Unix epoch - GNU extension */
605 sec
= strtoll_l ((char *)bp
, &end
, 10, locale
);
607 if (end
== (char *)bp
610 || localtime_r (&t
, tm
) != tm
)
612 bp
= (const unsigned char *)end
;
613 ymd
|= SET_YDAY
| SET_WDAY
| SET_YMD
;
617 case 'U': /* The week of year, beginning on sunday. */
618 case 'W': /* The week of year, beginning on monday. */
620 * XXX This is bogus, as we can not assume any valid
621 * information present in the tm structure at this
622 * point to calculate a real value, so just check the
626 bp
= conv_num(bp
, &i
, 0, 53, ALT_DIGITS
);
629 case 'u': /* The day of week, beginning on monday. */
632 bp
= conv_num(bp
, &i
, 1, 7, ALT_DIGITS
);
635 case 'w': /* The day of week, beginning on sunday. */
638 bp
= conv_num(bp
, &tm
->tm_wday
, 0, 6, ALT_DIGITS
);
641 case 'Y': /* The year. */
644 if ((alt_format
& ALT_E
) && *era_info
)
647 size_t num
= (*era_info
)->num
;
648 (*era_info
)->num
= 1;
649 for (size_t i
= 0; i
< num
; ++i
)
651 era_info_t
*tmp_ei
= (*era_info
) + i
;
652 char *tmp
= __strptime ((const char *) bp
,
658 bp
= (const unsigned char *) tmp
;
663 (*era_info
)->num
= num
;
668 i
= TM_YEAR_BASE
; /* just for data sanity... */
669 for (ulim
= 9999; width
&& width
< 4; ++width
)
671 bp
= conv_num(bp
, &i
, 0, ulim
, NULL
);
672 tm
->tm_year
= i
- TM_YEAR_BASE
;
677 case 'y': /* The year within 100 years of the century or era. */
678 /* LEGAL_ALT(ALT_E | ALT_O); */
680 if ((alt_format
& ALT_E
) && *era_info
)
682 /* With E modifier, the offset to the start date
683 of the era specified with %EC. We potentially
684 don't know the era yet, so we have to store the
685 value in a local variable, just like era itself.
686 The final computation of tm_year is only done
687 right before this function returns. */
688 bp
= conv_num(bp
, &era_offset
, 0, UINT_MAX
, NULL
);
692 bp
= conv_num(bp
, &i
, 0, 99, ALT_DIGITS
);
694 if (split_year
) /* preserve century */
695 i
+= (tm
->tm_year
/ 100) * 100;
699 i
= i
+ 2000 - TM_YEAR_BASE
;
701 i
= i
+ 1900 - TM_YEAR_BASE
;
710 if (strncmp((const char *)bp
, gmt
, 3) == 0) {
713 if (CYGWIN_VERSION_CHECK_FOR_EXTRA_TM_MEMBERS
)
717 if (CYGWIN_VERSION_CHECK_FOR_EXTRA_TM_MEMBERS
)
722 const unsigned char *ep
;
724 ep
= find_string(bp
, &i
,
725 (const char * const *)tzname
,
730 if (CYGWIN_VERSION_CHECK_FOR_EXTRA_TM_MEMBERS
)
731 tm
->TM_GMTOFF
= -(timezone
);
734 if (CYGWIN_VERSION_CHECK_FOR_EXTRA_TM_MEMBERS
)
735 tm
->TM_ZONE
= tzname
[i
];
743 * Miscellaneous conversions.
745 case 'n': /* Any kind of white-space. */
747 while (isspace_l(*bp
, locale
))
753 default: /* Unknown/unsupported conversion. */
762 if (bp
&& (era
|| got_eoff
))
764 /* Default to current era. */
767 /* Default to first year of era if offset is missing */
769 era_offset
= era
->offset
;
770 tm
->tm_year
= (era
->start
.tm_year
!= INT_MIN
771 ? era
->start
.tm_year
: era
->end
.tm_year
)
772 + (era_offset
- era
->offset
) * era
->dir
;
773 /* Check if year falls into the era. If not, it's an
774 invalid combination of era and offset. */
775 if (era
->start
.tm_year
> tm
->tm_year
776 || era
->end
.tm_year
< tm
->tm_year
)
778 tm
->tm_year
-= TM_YEAR_BASE
;
781 if ((ymd
& SET_YMD
) == SET_YMD
)
783 /* all of tm_year, tm_mon and tm_mday, but... */
784 if (!(ymd
& SET_YDAY
))
786 /* ...not tm_yday, so fill it in */
787 tm
->tm_yday
= _DAYS_BEFORE_MONTH
[tm
->tm_mon
] + tm
->tm_mday
;
788 if (!is_leap_year (tm
->tm_year
+ TM_YEAR_BASE
)
794 else if ((ymd
& (SET_YEAR
| SET_YDAY
)) == (SET_YEAR
| SET_YDAY
))
796 /* both of tm_year and tm_yday, but... */
797 if (!(ymd
& SET_MON
))
799 /* ...not tm_mon, so fill it in, and/or... */
800 if (tm
->tm_yday
< _DAYS_BEFORE_MONTH
[1])
804 int leap
= is_leap_year (tm
->tm_year
+ TM_YEAR_BASE
);
805 for (i
= 2; i
< 12; ++i
)
806 if (tm
->tm_yday
< _DAYS_BEFORE_MONTH
[i
] + leap
)
811 if (!(ymd
& SET_MDAY
))
813 /* ...not tm_mday, so fill it in */
814 tm
->tm_mday
= tm
->tm_yday
- _DAYS_BEFORE_MONTH
[tm
->tm_mon
];
815 if (!is_leap_year (tm
->tm_year
+ TM_YEAR_BASE
)
821 if ((ymd
& (SET_YEAR
| SET_YDAY
| SET_WDAY
)) == (SET_YEAR
| SET_YDAY
))
823 /* fill in tm_wday */
824 int fday
= first_day (tm
->tm_year
+ TM_YEAR_BASE
);
825 tm
->tm_wday
= (fday
+ tm
->tm_yday
) % 7;
831 strptime_l (const char *__restrict buf
, const char *__restrict fmt
,
832 struct tm
*__restrict tm
, locale_t locale
)
834 era_info_t
*era_info
= NULL
;
835 alt_digits_t
*alt_digits
= NULL
;
836 char *ret
= __strptime (buf
, fmt
, tm
, &era_info
, &alt_digits
, locale
);
838 free_era_info (era_info
);
840 free_alt_digits (alt_digits
);
845 strptime (const char *__restrict buf
, const char *__restrict fmt
,
846 struct tm
*__restrict tm
)
848 era_info_t
*era_info
= NULL
;
849 alt_digits_t
*alt_digits
= NULL
;
850 char *ret
= __strptime (buf
, fmt
, tm
, &era_info
, &alt_digits
,
851 __get_current_locale ());
853 free_era_info (era_info
);
855 free_alt_digits (alt_digits
);
859 static const unsigned char *
860 __conv_num(const unsigned char *buf
, int *dest
, uint llim
, uint ulim
,
861 alt_digits_t
*alt_digits
)
867 buf
= find_alt_digits (buf
, alt_digits
, &result
);
870 /* The limit also determines the number of valid digits. */
874 if (ch
< '0' || ch
> '9')
882 } while ((result
* 10 <= ulim
) && rulim
&& ch
>= '0' && ch
<= '9');
885 if (result
< llim
|| result
> ulim
)
892 static const unsigned char *
893 find_string(const unsigned char *bp
, int *tgt
, const char * const *n1
,
894 const char * const *n2
, int c
, locale_t locale
)
899 /* check full name - then abbreviated ones */
900 for (; n1
!= NULL
; n1
= n2
, n2
= NULL
) {
901 for (i
= 0; i
< c
; i
++, n1
++) {
903 if (strncasecmp_l(*n1
, (const char *)bp
, len
,
911 /* Nothing matched */