Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files. =================================================================== RCS file: /ftp/cvs/cvsroot/src/lib/libc/time/strptime.c,v rcsdiff: /ftp/cvs/cvsroot/src/lib/libc/time/strptime.c,v: warning: Unknown phrases like `commitid ...;' are present. retrieving revision 1.39 retrieving revision 1.40 diff -u -p -r1.39 -r1.40 --- src/lib/libc/time/strptime.c 2015/04/06 14:38:22 1.39 +++ src/lib/libc/time/strptime.c 2015/07/03 13:06:54 1.40 @@ -1,4 +1,4 @@ -/* $NetBSD: strptime.c,v 1.39 2015/04/06 14:38:22 ginsbach Exp $ */ +/* $NetBSD: strptime.c,v 1.40 2015/07/03 13:06:54 christos Exp $ */ /*- * Copyright (c) 1997, 1998, 2005, 2008 The NetBSD Foundation, Inc. @@ -31,11 +31,13 @@ #include #if defined(LIBC_SCCS) && !defined(lint) -__RCSID("$NetBSD: strptime.c,v 1.39 2015/04/06 14:38:22 ginsbach Exp $"); +__RCSID("$NetBSD: strptime.c,v 1.40 2015/07/03 13:06:54 christos Exp $"); #endif #include "namespace.h" #include +#include +#include #include #include #include @@ -60,6 +62,12 @@ __weak_alias(strptime_l, _strptime_l) #define ALT_O 0x02 #define LEGAL_ALT(x) { if (alt_format & ~(x)) return NULL; } +#define FLAG_YEAR (1 << 0) +#define FLAG_MONTH (1 << 1) +#define FLAG_YDAY (1 << 2) +#define FLAG_MDAY (1 << 3) +#define FLAG_WDAY (1 << 4) + static char gmt[] = { "GMT" }; static char utc[] = { "UTC" }; /* RFC-822/RFC-2822 */ @@ -74,6 +82,25 @@ static const u_char *conv_num(const unsi static const u_char *find_string(const u_char *, int *, const char * const *, const char * const *, int); +static const int start_of_month[2][13] = { + { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 }, + { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 } +}; + +/* + * Calculate the week day of the first day of a year. Valid for + * the Gregorian calendar, which began Sept 14, 1752 in the UK + * and its colonies. Ref: + * http://en.wikipedia.org/wiki/Determination_of_the_day_of_the_week + */ + +static int +first_wday_of(int yr) +{ + return ((2 * (3 - (yr / 100) % 4)) + (yr % 100) + ((yr % 100) / 4) + + (is_leap_year(yr) ? 6 : 0) + 1) % 7; +} + char * strptime(const char *buf, const char *fmt, struct tm *tm) { @@ -85,7 +112,8 @@ strptime_l(const char *buf, const char * { unsigned char c; const unsigned char *bp, *ep; - int alt_format, i, split_year = 0, neg = 0, offs; + int alt_format, i, split_year = 0, neg = 0, flags = 0, + day_offset = -1, week_offset = 0, offs; const char *new_fmt; bp = (const u_char *)buf; @@ -133,16 +161,20 @@ literal: */ case 'c': /* Date and time, using the locale's format. */ new_fmt = _TIME_LOCALE(loc)->d_t_fmt; + flags |= FLAG_WDAY | FLAG_MONTH | FLAG_MDAY | + FLAG_YEAR; goto recurse; case 'D': /* The date as "%m/%d/%y". */ new_fmt = "%m/%d/%y"; LEGAL_ALT(0); + flags |= FLAG_MONTH | FLAG_MDAY | FLAG_YEAR; goto recurse; case 'F': /* The date as "%Y-%m-%d". */ new_fmt = "%Y-%m-%d"; LEGAL_ALT(0); + flags |= FLAG_MONTH | FLAG_MDAY | FLAG_YEAR; goto recurse; case 'R': /* The time as "%H:%M". */ @@ -166,6 +198,7 @@ literal: case 'x': /* The date, using the locale's format. */ new_fmt = _TIME_LOCALE(loc)->d_fmt; + flags |= FLAG_MONTH | FLAG_MDAY | FLAG_YEAR; recurse: bp = (const u_char *)strptime((const char *)bp, new_fmt, tm); @@ -180,6 +213,7 @@ literal: bp = find_string(bp, &tm->tm_wday, _TIME_LOCALE(loc)->day, _TIME_LOCALE(loc)->abday, 7); LEGAL_ALT(0); + flags |= FLAG_WDAY; continue; case 'B': /* The month, using the locale's form. */ @@ -189,6 +223,7 @@ literal: _TIME_LOCALE(loc)->mon, _TIME_LOCALE(loc)->abmon, 12); LEGAL_ALT(0); + flags |= FLAG_MONTH; continue; case 'C': /* The century number. */ @@ -201,12 +236,14 @@ literal: split_year = 1; tm->tm_year = i; LEGAL_ALT(ALT_E); + flags |= FLAG_YEAR; continue; case 'd': /* The day of month. */ case 'e': bp = conv_num(bp, &tm->tm_mday, 1, 31); LEGAL_ALT(ALT_O); + flags |= FLAG_MDAY; continue; case 'k': /* The hour (24-hour clock representation). */ @@ -232,6 +269,7 @@ literal: bp = conv_num(bp, &i, 1, 366); tm->tm_yday = i - 1; LEGAL_ALT(0); + flags |= FLAG_YDAY; continue; case 'M': /* The minute. */ @@ -244,6 +282,7 @@ literal: bp = conv_num(bp, &i, 1, 12); tm->tm_mon = i - 1; LEGAL_ALT(ALT_O); + flags |= FLAG_MONTH; continue; case 'p': /* The locale's equivalent of AM/PM. */ @@ -287,6 +326,9 @@ literal: if (localtime_r(&sse, tm) == NULL) bp = NULL; + else + flags |= FLAG_YDAY | FLAG_WDAY | + FLAG_MONTH | FLAG_MDAY | FLAG_YEAR; } continue; @@ -298,13 +340,19 @@ literal: * point to calculate a real value, so just check the * range for now. */ - bp = conv_num(bp, &i, 0, 53); - LEGAL_ALT(ALT_O); - continue; + bp = conv_num(bp, &i, 0, 53); + LEGAL_ALT(ALT_O); + if (c == 'U') + day_offset = TM_SUNDAY; + else + day_offset = TM_MONDAY; + week_offset = i; + continue; case 'w': /* The day of week, beginning on sunday. */ bp = conv_num(bp, &tm->tm_wday, 0, 6); LEGAL_ALT(ALT_O); + flags |= FLAG_WDAY; continue; case 'u': /* The day of week, monday = 1. */ @@ -336,6 +384,7 @@ literal: bp = conv_num(bp, &i, 0, 9999); tm->tm_year = i - TM_YEAR_BASE; LEGAL_ALT(ALT_E); + flags |= FLAG_YEAR; continue; case 'y': /* The year within 100 years of the epoch. */ @@ -353,6 +402,7 @@ literal: i = i + 1900 - TM_YEAR_BASE; } tm->tm_year = i; + flags |= FLAG_YEAR; continue; case 'Z': @@ -425,6 +475,8 @@ literal: continue; case '+': neg = 0; + flags |= FLAG_WDAY | FLAG_MONTH | FLAG_MDAY | + FLAG_YEAR; break; case '-': neg = 1; @@ -529,6 +581,61 @@ literal: } } + if (!(flags & FLAG_YDAY) && (flags & FLAG_YEAR)) { + if ((flags & (FLAG_MONTH | FLAG_MDAY)) == + (FLAG_MONTH | FLAG_MDAY)) { + tm->tm_yday = start_of_month[is_leap_year(tm->tm_year + + TM_YEAR_BASE)][tm->tm_mon] + (tm->tm_mday - 1); + flags |= FLAG_YDAY; + } else if (day_offset != -1) { + /* Set the date to the first Sunday (or Monday) + * of the specified week of the year. + */ + if (!(flags & FLAG_WDAY)) { + tm->tm_wday = day_offset; + flags |= FLAG_WDAY; + } + tm->tm_yday = (7 - + first_wday_of(tm->tm_year + TM_YEAR_BASE) + + day_offset) % 7 + (week_offset - 1) * 7 + + tm->tm_wday - day_offset; + flags |= FLAG_YDAY; + } + } + + if ((flags & (FLAG_YEAR | FLAG_YDAY)) == (FLAG_YEAR | FLAG_YDAY)) { + int isleap; + if (!(flags & FLAG_MONTH)) { + i = 0; + isleap = is_leap_year(tm->tm_year + TM_YEAR_BASE); + while (tm->tm_yday >= start_of_month[isleap][i]) + i++; + if (i > 12) { + i = 1; + tm->tm_yday -= start_of_month[isleap][12]; + tm->tm_year++; + } + tm->tm_mon = i - 1; + flags |= FLAG_MONTH; + } + if (!(flags & FLAG_MDAY)) { + isleap = is_leap_year(tm->tm_year + TM_YEAR_BASE); + tm->tm_mday = tm->tm_yday - + start_of_month[isleap][tm->tm_mon] + 1; + flags |= FLAG_MDAY; + } + if (!(flags & FLAG_WDAY)) { + i = 0; + week_offset = first_wday_of(tm->tm_year); + while (i++ <= tm->tm_yday) { + if (week_offset++ >= 6) + week_offset = 0; + } + tm->tm_wday = week_offset; + flags |= FLAG_WDAY; + } + } + return __UNCONST(bp); }