Sync strptime.c with upstream.
We still have local differences, but this minimizes (and documents) them.
Bug: http://b/167569813
Test: treehugger
Change-Id: Ib90e6ccc5ec1224e7ee89224a51b87fc48c9931f
diff --git a/libc/tzcode/strptime.c b/libc/tzcode/strptime.c
index 7e8e234..fe9e10f 100644
--- a/libc/tzcode/strptime.c
+++ b/libc/tzcode/strptime.c
@@ -1,8 +1,7 @@
-/* $OpenBSD: strptime.c,v 1.11 2005/08/08 08:05:38 espie Exp $ */
-/* $NetBSD: strptime.c,v 1.12 1998/01/20 21:39:40 mycroft Exp $ */
-
+/* $OpenBSD: strptime.c,v 1.30 2019/05/12 12:49:52 schwarze Exp $ */
+/* $NetBSD: strptime.c,v 1.12 1998/01/20 21:39:40 mycroft Exp $ */
/*-
- * Copyright (c) 1997, 1998 The NetBSD Foundation, Inc.
+ * Copyright (c) 1997, 1998, 2005, 2008 The NetBSD Foundation, Inc.
* All rights reserved.
*
* This code was contributed to The NetBSD Foundation by Klaus Klein.
@@ -15,13 +14,6 @@
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
- * 3. All advertising materials mentioning features or use of this software
- * must display the following acknowledgement:
- * This product includes software developed by the NetBSD
- * Foundation, Inc. and its contributors.
- * 4. Neither the name of The NetBSD Foundation nor the names of its
- * contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
@@ -36,65 +28,40 @@
* POSSIBILITY OF SUCH DAMAGE.
*/
-//#include <sys/localedef.h>
#include <ctype.h>
-#include <errno.h>
#include <locale.h>
-#include <stdlib.h>
+#include <stdint.h>
#include <string.h>
#include <time.h>
+
+#include "localedef.h"
+#include "private.h"
#include "tzfile.h"
-static const struct {
- const char *abday[7];
- const char *day[7];
- const char *abmon[12];
- const char *mon[12];
- const char *am_pm[2];
- const char *d_t_fmt;
- const char *d_fmt;
- const char *t_fmt;
- const char *t_fmt_ampm;
-} _DefaultTimeLocale = {
- {
- "Sun","Mon","Tue","Wed","Thu","Fri","Sat",
- },
- {
- "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday",
- "Friday", "Saturday"
- },
- {
- "Jan", "Feb", "Mar", "Apr", "May", "Jun",
- "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
- },
- {
- "January", "February", "March", "April", "May", "June", "July",
- "August", "September", "October", "November", "December"
- },
- {
- "AM", "PM"
- },
- "%a %b %d %H:%M:%S %Y",
- "%m/%d/%y",
- "%H:%M:%S",
- "%I:%M:%S %p"
-};
+// Android: ignore OpenBSD's DEF_WEAK() stuff.
+#define DEF_WEAK(sym) /* */
+// Android: this code is not pointer-sign clean.
+#pragma clang diagnostic ignored "-Wpointer-sign"
+#pragma clang diagnostic ignored "-Wunused-function"
-#define _ctloc(x) (_DefaultTimeLocale.x)
+#define _ctloc(x) (_CurrentTimeLocale->x)
/*
* We do not implement alternate representations. However, we always
* check whether a given modifier is allowed for a certain conversion.
*/
-#define _ALT_E 0x01
-#define _ALT_O 0x02
-#define _LEGAL_ALT(x) { if (alt_format & ~(x)) return (0); }
+#define _ALT_E 0x01
+#define _ALT_O 0x02
+#define _LEGAL_ALT(x) { if (alt_format & ~(x)) return (0); }
-
-struct century_relyear {
- int century;
- int relyear;
-};
+/*
+ * We keep track of some of the fields we set in order to compute missing ones.
+ */
+#define FIELD_TM_MON (1 << 0)
+#define FIELD_TM_MDAY (1 << 1)
+#define FIELD_TM_WDAY (1 << 2)
+#define FIELD_TM_YDAY (1 << 3)
+#define FIELD_TM_YEAR (1 << 4)
static char gmt[] = { "GMT" };
static char utc[] = { "UTC" };
@@ -106,9 +73,15 @@
"EDT", "CDT", "MDT", "PDT", "\0\0\0"
};
-static int _conv_num(const unsigned char **, int *, int, int);
-static unsigned char *_strptime(const unsigned char *, const char *, struct tm *,
- struct century_relyear *);
+static const int mon_lengths[2][MONSPERYEAR] = {
+ { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 },
+ { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
+};
+
+static int _conv_num64(const unsigned char **, int64_t *, int64_t, int64_t);
+static int _conv_num(const unsigned char **, int *, int, int);
+static int leaps_thru_end_of(const int y);
+static char *_strptime(const char *, const char *, struct tm *, int);
static const u_char *_find_string(const u_char *, int *, const char * const *,
const char * const *, int);
@@ -116,335 +89,353 @@
char *
strptime(const char *buf, const char *fmt, struct tm *tm)
{
- struct century_relyear cr;
- cr.century = TM_YEAR_BASE;
- cr.relyear = -1;
- return (char*)(_strptime((const unsigned char*)buf, fmt, tm, &cr));
+ return(_strptime(buf, fmt, tm, 1));
}
+DEF_WEAK(strptime);
-static unsigned char *
-_strptime(const unsigned char *buf, const char *fmt, struct tm *tm, struct century_relyear *cr)
+static char *
+_strptime(const char *buf, const char *fmt, struct tm *tm, int initialize)
{
- unsigned char c;
- const unsigned char *bp, *ep;
- size_t len = 0;
- int alt_format, i, offs;
- int neg = 0;
+ unsigned char c;
+ const unsigned char *bp, *ep;
+ size_t len;
+ int alt_format, i, offs;
+ int neg = 0;
+ static int century, relyear, fields;
- bp = (unsigned char *)buf;
- while ((c = *fmt) != '\0') {
- /* Clear `alternate' modifier prior to new conversion. */
- alt_format = 0;
+ if (initialize) {
+ century = TM_YEAR_BASE;
+ relyear = -1;
+ fields = 0;
+ }
- /* Eat up white-space. */
- if (isspace(c)) {
- while (isspace(*bp))
- bp++;
+ bp = (const unsigned char *)buf;
+ while ((c = *fmt) != '\0') {
+ /* Clear `alternate' modifier prior to new conversion. */
+ alt_format = 0;
- fmt++;
- continue;
- }
+ /* Eat up white-space. */
+ if (isspace(c)) {
+ while (isspace(*bp))
+ bp++;
- if ((c = *fmt++) != '%')
- goto literal;
+ fmt++;
+ continue;
+ }
+
+ if ((c = *fmt++) != '%')
+ goto literal;
-again: switch (c = *fmt++) {
- case '%': /* "%%" is converted to "%". */
+again: switch (c = *fmt++) {
+ case '%': /* "%%" is converted to "%". */
literal:
- if (c != *bp++)
- return (NULL);
+ if (c != *bp++)
+ return (NULL);
- break;
+ break;
- /*
- * "Alternative" modifiers. Just set the appropriate flag
- * and start over again.
- */
- case 'E': /* "%E?" alternative conversion modifier. */
- _LEGAL_ALT(0);
- alt_format |= _ALT_E;
- goto again;
+ /*
+ * "Alternative" modifiers. Just set the appropriate flag
+ * and start over again.
+ */
+ case 'E': /* "%E?" alternative conversion modifier. */
+ _LEGAL_ALT(0);
+ alt_format |= _ALT_E;
+ goto again;
- case 'O': /* "%O?" alternative conversion modifier. */
- _LEGAL_ALT(0);
- alt_format |= _ALT_O;
- goto again;
+ case 'O': /* "%O?" alternative conversion modifier. */
+ _LEGAL_ALT(0);
+ alt_format |= _ALT_O;
+ goto again;
- /*
- * "Complex" conversion rules, implemented through recursion.
- */
- case 'c': /* Date and time, using the locale's format. */
- _LEGAL_ALT(_ALT_E);
- if (!(bp = _strptime(bp, _ctloc(d_t_fmt), tm, cr)))
- return (NULL);
- break;
+ /*
+ * "Complex" conversion rules, implemented through recursion.
+ */
+ case 'c': /* Date and time, using the locale's format. */
+ _LEGAL_ALT(_ALT_E);
+ if (!(bp = _strptime(bp, _ctloc(d_t_fmt), tm, 0)))
+ return (NULL);
+ break;
- case 'D': /* The date as "%m/%d/%y". */
- _LEGAL_ALT(0);
- if (!(bp = _strptime(bp, "%m/%d/%y", tm, cr)))
- return (NULL);
- break;
+ case 'D': /* The date as "%m/%d/%y". */
+ _LEGAL_ALT(0);
+ if (!(bp = _strptime(bp, "%m/%d/%y", tm, 0)))
+ return (NULL);
+ break;
- case 'F': /* The date as "%Y-%m-%d". */
- _LEGAL_ALT(0);
- if (!(bp = _strptime(bp, "%Y-%m-%d", tm, cr)))
- return (NULL);
- continue;
+ case 'F': /* The date as "%Y-%m-%d". */
+ _LEGAL_ALT(0);
+ if (!(bp = _strptime(bp, "%Y-%m-%d", tm, 0)))
+ return (NULL);
+ continue;
- case 'R': /* The time as "%H:%M". */
- _LEGAL_ALT(0);
- if (!(bp = _strptime(bp, "%H:%M", tm, cr)))
- return (NULL);
- break;
+ case 'R': /* The time as "%H:%M". */
+ _LEGAL_ALT(0);
+ if (!(bp = _strptime(bp, "%H:%M", tm, 0)))
+ return (NULL);
+ break;
- case 'r': /* The time as "%I:%M:%S %p". */
- _LEGAL_ALT(0);
- if (!(bp = _strptime(bp, "%I:%M:%S %p", tm, cr)))
- return (NULL);
- break;
+ case 'r': /* The time as "%I:%M:%S %p". */
+ _LEGAL_ALT(0);
+ if (!(bp = _strptime(bp, "%I:%M:%S %p", tm, 0)))
+ return (NULL);
+ break;
- case 'T': /* The time as "%H:%M:%S". */
- _LEGAL_ALT(0);
- if (!(bp = _strptime(bp, "%H:%M:%S", tm, cr)))
- return (NULL);
- break;
+ case 'T': /* The time as "%H:%M:%S". */
+ _LEGAL_ALT(0);
+ if (!(bp = _strptime(bp, "%H:%M:%S", tm, 0)))
+ return (NULL);
+ break;
- case 'v': /* The date as "%e-%b-%Y". */
- _LEGAL_ALT(0);
- if (!(bp = _strptime(bp, "%e-%b-%Y", tm, cr)))
- return (NULL);
- break;
+ case 'v': /* Android: the date as "%e-%b-%Y" for strftime() compat; glibc does this too. */
+ _LEGAL_ALT(0);
+ if (!(bp = _strptime(bp, "%e-%b-%Y", tm, 0)))
+ return (NULL);
+ break;
- case 'X': /* The time, using the locale's format. */
- _LEGAL_ALT(_ALT_E);
- if (!(bp = _strptime(bp, _ctloc(t_fmt), tm, cr)))
- return (NULL);
- break;
+ case 'X': /* The time, using the locale's format. */
+ _LEGAL_ALT(_ALT_E);
+ if (!(bp = _strptime(bp, _ctloc(t_fmt), tm, 0)))
+ return (NULL);
+ break;
- case 'x': /* The date, using the locale's format. */
- _LEGAL_ALT(_ALT_E);
- if (!(bp = _strptime(bp, _ctloc(d_fmt), tm, cr)))
- return (NULL);
- break;
+ case 'x': /* The date, using the locale's format. */
+ _LEGAL_ALT(_ALT_E);
+ if (!(bp = _strptime(bp, _ctloc(d_fmt), tm, 0)))
+ return (NULL);
+ break;
- /*
- * "Elementary" conversion rules.
- */
- case 'A': /* The day of week, using the locale's form. */
- case 'a':
- _LEGAL_ALT(0);
- for (i = 0; i < 7; i++) {
- /* Full name. */
- len = strlen(_ctloc(day[i]));
- if (strncasecmp(_ctloc(day[i]), (const char*)bp, len) == 0)
- break;
+ /*
+ * "Elementary" conversion rules.
+ */
+ case 'A': /* The day of week, using the locale's form. */
+ case 'a':
+ _LEGAL_ALT(0);
+ for (i = 0; i < 7; i++) {
+ /* Full name. */
+ len = strlen(_ctloc(day[i]));
+ if (strncasecmp(_ctloc(day[i]), bp, len) == 0)
+ break;
- /* Abbreviated name. */
- len = strlen(_ctloc(abday[i]));
- if (strncasecmp(_ctloc(abday[i]), (const char*)bp, len) == 0)
- break;
- }
+ /* Abbreviated name. */
+ len = strlen(_ctloc(abday[i]));
+ if (strncasecmp(_ctloc(abday[i]), bp, len) == 0)
+ break;
+ }
- /* Nothing matched. */
- if (i == 7)
- return (NULL);
+ /* Nothing matched. */
+ if (i == 7)
+ return (NULL);
- tm->tm_wday = i;
- bp += len;
- break;
+ tm->tm_wday = i;
+ bp += len;
+ fields |= FIELD_TM_WDAY;
+ break;
- case 'B': /* The month, using the locale's form. */
- case 'b':
- case 'h':
- _LEGAL_ALT(0);
- for (i = 0; i < 12; i++) {
- /* Full name. */
- len = strlen(_ctloc(mon[i]));
- if (strncasecmp(_ctloc(mon[i]), (const char*)bp, len) == 0)
- break;
+ case 'B': /* The month, using the locale's form. */
+ case 'b':
+ case 'h':
+ _LEGAL_ALT(0);
+ for (i = 0; i < 12; i++) {
+ /* Full name. */
+ len = strlen(_ctloc(mon[i]));
+ if (strncasecmp(_ctloc(mon[i]), bp, len) == 0)
+ break;
- /* Abbreviated name. */
- len = strlen(_ctloc(abmon[i]));
- if (strncasecmp(_ctloc(abmon[i]), (const char*)bp, len) == 0)
- break;
- }
+ /* Abbreviated name. */
+ len = strlen(_ctloc(abmon[i]));
+ if (strncasecmp(_ctloc(abmon[i]), bp, len) == 0)
+ break;
+ }
- /* Nothing matched. */
- if (i == 12)
- return (NULL);
+ /* Nothing matched. */
+ if (i == 12)
+ return (NULL);
- tm->tm_mon = i;
- bp += len;
- break;
+ tm->tm_mon = i;
+ bp += len;
+ fields |= FIELD_TM_MON;
+ break;
- case 'C': /* The century number. */
- _LEGAL_ALT(_ALT_E);
- if (!(_conv_num(&bp, &i, 0, 99)))
- return (NULL);
+ case 'C': /* The century number. */
+ _LEGAL_ALT(_ALT_E);
+ if (!(_conv_num(&bp, &i, 0, 99)))
+ return (NULL);
- cr->century = i * 100;
- break;
+ century = i * 100;
+ break;
- case 'd': /* The day of month. */
- case 'e':
- _LEGAL_ALT(_ALT_O);
- if (!(_conv_num(&bp, &tm->tm_mday, 1, 31)))
- return (NULL);
- break;
+ case 'e': /* The day of month. */
+ if (isspace(*bp))
+ bp++;
+ /* FALLTHROUGH */
+ case 'd':
+ _LEGAL_ALT(_ALT_O);
+ if (!(_conv_num(&bp, &tm->tm_mday, 1, 31)))
+ return (NULL);
+ fields |= FIELD_TM_MDAY;
+ break;
- case 'k': /* The hour (24-hour clock representation). */
- _LEGAL_ALT(0);
- /* FALLTHROUGH */
- case 'H':
- _LEGAL_ALT(_ALT_O);
- if (!(_conv_num(&bp, &tm->tm_hour, 0, 23)))
- return (NULL);
- break;
+ case 'k': /* The hour (24-hour clock representation). */
+ _LEGAL_ALT(0);
+ /* FALLTHROUGH */
+ case 'H':
+ _LEGAL_ALT(_ALT_O);
+ if (!(_conv_num(&bp, &tm->tm_hour, 0, 23)))
+ return (NULL);
+ break;
- case 'l': /* The hour (12-hour clock representation). */
- _LEGAL_ALT(0);
- /* FALLTHROUGH */
- case 'I':
- _LEGAL_ALT(_ALT_O);
- if (!(_conv_num(&bp, &tm->tm_hour, 1, 12)))
- return (NULL);
- break;
+ case 'l': /* The hour (12-hour clock representation). */
+ _LEGAL_ALT(0);
+ /* FALLTHROUGH */
+ case 'I':
+ _LEGAL_ALT(_ALT_O);
+ if (!(_conv_num(&bp, &tm->tm_hour, 1, 12)))
+ return (NULL);
+ break;
- case 'j': /* The day of year. */
- _LEGAL_ALT(0);
- if (!(_conv_num(&bp, &tm->tm_yday, 1, 366)))
- return (NULL);
- tm->tm_yday--;
- break;
+ case 'j': /* The day of year. */
+ _LEGAL_ALT(0);
+ if (!(_conv_num(&bp, &tm->tm_yday, 1, 366)))
+ return (NULL);
+ tm->tm_yday--;
+ fields |= FIELD_TM_YDAY;
+ break;
- case 'M': /* The minute. */
- _LEGAL_ALT(_ALT_O);
- if (!(_conv_num(&bp, &tm->tm_min, 0, 59)))
- return (NULL);
- break;
+ case 'M': /* The minute. */
+ _LEGAL_ALT(_ALT_O);
+ if (!(_conv_num(&bp, &tm->tm_min, 0, 59)))
+ return (NULL);
+ break;
- case 'm': /* The month. */
- _LEGAL_ALT(_ALT_O);
- if (!(_conv_num(&bp, &tm->tm_mon, 1, 12)))
- return (NULL);
- tm->tm_mon--;
- break;
+ case 'm': /* The month. */
+ _LEGAL_ALT(_ALT_O);
+ if (!(_conv_num(&bp, &tm->tm_mon, 1, 12)))
+ return (NULL);
+ tm->tm_mon--;
+ fields |= FIELD_TM_MON;
+ break;
- case 'P':
- case 'p': /* The locale's equivalent of AM/PM. */
- _LEGAL_ALT(0);
- /* AM? */
- len = strlen(_ctloc(am_pm[0]));
- if (strncasecmp(_ctloc(am_pm[0]), (const char*)bp, len) == 0) {
- if (tm->tm_hour > 12) /* i.e., 13:00 AM ?! */
- return (NULL);
- else if (tm->tm_hour == 12)
- tm->tm_hour = 0;
+ case 'P': /* Android addition for strftime() compat; glibc does this too. */
+ case 'p': /* The locale's equivalent of AM/PM. */
+ _LEGAL_ALT(0);
+ /* AM? */
+ len = strlen(_ctloc(am_pm[0]));
+ if (strncasecmp(_ctloc(am_pm[0]), bp, len) == 0) {
+ if (tm->tm_hour > 12) /* i.e., 13:00 AM ?! */
+ return (NULL);
+ else if (tm->tm_hour == 12)
+ tm->tm_hour = 0;
- bp += len;
- break;
- }
- /* PM? */
- len = strlen(_ctloc(am_pm[1]));
- if (strncasecmp(_ctloc(am_pm[1]), (const char*)bp, len) == 0) {
- if (tm->tm_hour > 12) /* i.e., 13:00 PM ?! */
- return (NULL);
- else if (tm->tm_hour < 12)
- tm->tm_hour += 12;
+ bp += len;
+ break;
+ }
+ /* PM? */
+ len = strlen(_ctloc(am_pm[1]));
+ if (strncasecmp(_ctloc(am_pm[1]), bp, len) == 0) {
+ if (tm->tm_hour > 12) /* i.e., 13:00 PM ?! */
+ return (NULL);
+ else if (tm->tm_hour < 12)
+ tm->tm_hour += 12;
- bp += len;
- break;
- }
+ bp += len;
+ break;
+ }
- /* Nothing matched. */
- return (NULL);
+ /* Nothing matched. */
+ return (NULL);
- case 'S': /* The seconds. */
- _LEGAL_ALT(_ALT_O);
- if (!(_conv_num(&bp, &tm->tm_sec, 0, 61)))
- return (NULL);
- break;
+ case 'S': /* The seconds. */
+ _LEGAL_ALT(_ALT_O);
+ if (!(_conv_num(&bp, &tm->tm_sec, 0, 60)))
+ return (NULL);
+ break;
+ case 's': /* Seconds since epoch */
+ {
+ // Android change based on FreeBSD's implementation.
+ int saved_errno = errno;
+ errno = 0;
+ const unsigned char* old_bp = bp;
+ long n = strtol((const char*) bp, (char**) &bp, 10);
+ errno = saved_errno;
+ time_t t = n;
+ if (bp == old_bp || errno == ERANGE ||
+ ((long) t) != n) return NULL;
- case 's':
- {
- // Android addition, based on FreeBSD's implementation.
- int saved_errno = errno;
- errno = 0;
- const unsigned char* old_bp = bp;
- long n = strtol((const char*) bp, (char**) &bp, 10);
- time_t t = n;
- if (bp == old_bp || errno == ERANGE || ((long) t) != n) {
- errno = saved_errno;
- return NULL;
- }
- errno = saved_errno;
+ if (localtime_r(&t, tm) == NULL) return NULL;
- if (localtime_r(&t, tm) == NULL) return NULL;
- }
- break;
+ //int64_t i64;
+ //if (!(_conv_num64(&bp, &i64, 0, INT64_MAX)))
+ // return (NULL);
+ //if (!gmtime_r(&i64, tm))
+ // return (NULL);
+ fields = 0xffff; /* everything */
+ }
+ break;
+ case 'U': /* The week of year, beginning on sunday. */
+ case 'W': /* The week of year, beginning on monday. */
+ _LEGAL_ALT(_ALT_O);
+ /*
+ * XXX This is bogus, as we can not assume any valid
+ * information present in the tm structure at this
+ * point to calculate a real value, so just check the
+ * range for now.
+ */
+ if (!(_conv_num(&bp, &i, 0, 53)))
+ return (NULL);
+ break;
+ case 'w': /* The day of week, beginning on sunday. */
+ _LEGAL_ALT(_ALT_O);
+ if (!(_conv_num(&bp, &tm->tm_wday, 0, 6)))
+ return (NULL);
+ fields |= FIELD_TM_WDAY;
+ break;
- case 'U': /* The week of year, beginning on sunday. */
- case 'W': /* The week of year, beginning on monday. */
- _LEGAL_ALT(_ALT_O);
- /*
- * XXX This is bogus, as we can not assume any valid
- * information present in the tm structure at this
- * point to calculate a real value, so just check the
- * range for now.
- */
- if (!(_conv_num(&bp, &i, 0, 53)))
- return (NULL);
- break;
+ case 'u': /* The day of week, monday = 1. */
+ _LEGAL_ALT(_ALT_O);
+ if (!(_conv_num(&bp, &i, 1, 7)))
+ return (NULL);
+ tm->tm_wday = i % 7;
+ fields |= FIELD_TM_WDAY;
+ continue;
- case 'w': /* The day of week, beginning on sunday. */
- _LEGAL_ALT(_ALT_O);
- if (!(_conv_num(&bp, &tm->tm_wday, 0, 6)))
- return (NULL);
- break;
+ case 'g': /* The year corresponding to the ISO week
+ * number but without the century.
+ */
+ if (!(_conv_num(&bp, &i, 0, 99)))
+ return (NULL);
+ continue;
- case 'u': /* The day of week, monday = 1. */
- _LEGAL_ALT(_ALT_O);
- if (!(_conv_num(&bp, &i, 1, 7)))
- return (NULL);
- tm->tm_wday = i % 7;
- continue;
+ case 'G': /* The year corresponding to the ISO week
+ * number with century.
+ */
+ do
+ bp++;
+ while (isdigit(*bp));
+ continue;
- case 'g': /* The year corresponding to the ISO week
- * number but without the century.
- */
- if (!(_conv_num(&bp, &i, 0, 99)))
- return (NULL);
- continue;
+ case 'V': /* The ISO 8601:1988 week number as decimal */
+ if (!(_conv_num(&bp, &i, 0, 53)))
+ return (NULL);
+ continue;
- case 'G': /* The year corresponding to the ISO week
- * number with century.
- */
- do
- bp++;
- while (isdigit(*bp));
- continue;
+ case 'Y': /* The year. */
+ _LEGAL_ALT(_ALT_E);
+ if (!(_conv_num(&bp, &i, 0, 9999)))
+ return (NULL);
- case 'V': /* The ISO 8601:1988 week number as decimal */
- if (!(_conv_num(&bp, &i, 0, 53)))
- return (NULL);
- continue;
+ relyear = -1;
+ tm->tm_year = i - TM_YEAR_BASE;
+ fields |= FIELD_TM_YEAR;
+ break;
- case 'Y': /* The year. */
- _LEGAL_ALT(_ALT_E);
- if (!(_conv_num(&bp, &i, 0, 9999)))
- return (NULL);
-
- cr->relyear = -1;
- tm->tm_year = i - TM_YEAR_BASE;
- break;
-
- case 'y': /* The year within the century (2 digits). */
- _LEGAL_ALT(_ALT_E | _ALT_O);
- if (!(_conv_num(&bp, &cr->relyear, 0, 99)))
- return (NULL);
- break;
+ case 'y': /* The year within the century (2 digits). */
+ _LEGAL_ALT(_ALT_E | _ALT_O);
+ if (!(_conv_num(&bp, &relyear, 0, 99)))
+ return (NULL);
+ break;
case 'Z':
tzset();
@@ -548,42 +539,78 @@
tm->tm_zone = NULL; /* XXX */
continue;
- /*
- * Miscellaneous conversions.
- */
- case 'n': /* Any kind of white-space. */
- case 't':
- _LEGAL_ALT(0);
- while (isspace(*bp))
- bp++;
- break;
+ /*
+ * Miscellaneous conversions.
+ */
+ case 'n': /* Any kind of white-space. */
+ case 't':
+ _LEGAL_ALT(0);
+ while (isspace(*bp))
+ bp++;
+ break;
- default: /* Unknown/unsupported conversion. */
- return (NULL);
- }
+ default: /* Unknown/unsupported conversion. */
+ return (NULL);
+ }
- }
+ }
- /*
- * We need to evaluate the two digit year spec (%y)
- * last as we can get a century spec (%C) at any time.
- */
- if (cr->relyear != -1) {
- if (cr->century == TM_YEAR_BASE) {
- if (cr->relyear <= 68)
- tm->tm_year = cr->relyear + 2000 - TM_YEAR_BASE;
- else
- tm->tm_year = cr->relyear + 1900 - TM_YEAR_BASE;
- } else {
- tm->tm_year = cr->relyear + cr->century - TM_YEAR_BASE;
- }
- }
+ /*
+ * We need to evaluate the two digit year spec (%y)
+ * last as we can get a century spec (%C) at any time.
+ */
+ if (relyear != -1) {
+ if (century == TM_YEAR_BASE) {
+ if (relyear <= 68)
+ tm->tm_year = relyear + 2000 - TM_YEAR_BASE;
+ else
+ tm->tm_year = relyear + 1900 - TM_YEAR_BASE;
+ } else {
+ tm->tm_year = relyear + century - TM_YEAR_BASE;
+ }
+ fields |= FIELD_TM_YEAR;
+ }
- return (unsigned char*)bp;
+ /* Compute some missing values when possible. */
+ if (fields & FIELD_TM_YEAR) {
+ const int year = tm->tm_year + TM_YEAR_BASE;
+ const int *mon_lens = mon_lengths[isleap(year)];
+ if (!(fields & FIELD_TM_YDAY) &&
+ (fields & FIELD_TM_MON) && (fields & FIELD_TM_MDAY)) {
+ tm->tm_yday = tm->tm_mday - 1;
+ for (i = 0; i < tm->tm_mon; i++)
+ tm->tm_yday += mon_lens[i];
+ fields |= FIELD_TM_YDAY;
+ }
+ if (fields & FIELD_TM_YDAY) {
+ int days = tm->tm_yday;
+ if (!(fields & FIELD_TM_WDAY)) {
+ tm->tm_wday = EPOCH_WDAY +
+ ((year - EPOCH_YEAR) % DAYSPERWEEK) *
+ (DAYSPERNYEAR % DAYSPERWEEK) +
+ leaps_thru_end_of(year - 1) -
+ leaps_thru_end_of(EPOCH_YEAR - 1) +
+ tm->tm_yday;
+ tm->tm_wday %= DAYSPERWEEK;
+ if (tm->tm_wday < 0)
+ tm->tm_wday += DAYSPERWEEK;
+ }
+ if (!(fields & FIELD_TM_MON)) {
+ tm->tm_mon = 0;
+ while (tm->tm_mon < MONSPERYEAR && days >= mon_lens[tm->tm_mon])
+ days -= mon_lens[tm->tm_mon++];
+ }
+ if (!(fields & FIELD_TM_MDAY))
+ tm->tm_mday = days + 1;
+ }
+ }
+
+ return ((char *)bp);
}
+
static int
_conv_num(const unsigned char **buf, int *dest, int llim, int ulim)
{
@@ -607,6 +634,29 @@
return (1);
}
+static int
+_conv_num64(const unsigned char **buf, int64_t *dest, int64_t llim, int64_t ulim)
+{
+ int result = 0;
+ int64_t rulim = ulim;
+
+ if (**buf < '0' || **buf > '9')
+ return (0);
+
+ /* we use rulim to break out of the loop when we run out of digits */
+ do {
+ result *= 10;
+ result += *(*buf)++ - '0';
+ rulim /= 10;
+ } while ((result * 10 <= ulim) && rulim && **buf >= '0' && **buf <= '9');
+
+ if (result < llim || result > ulim)
+ return (0);
+
+ *dest = result;
+ return (1);
+}
+
static const u_char *
_find_string(const u_char *bp, int *tgt, const char * const *n1,
const char * const *n2, int c)
@@ -629,6 +679,9 @@
return NULL;
}
-char* strptime_l(const char* buf, const char* fmt, struct tm* tm, locale_t l) {
- return strptime(buf, fmt, tm);
+static int
+leaps_thru_end_of(const int y)
+{
+ return (y >= 0) ? (y / 4 - y / 100 + y / 400) :
+ -(leaps_thru_end_of(-(y + 1)) + 1);
}