diff --git a/ChangeLog.md b/ChangeLog.md index c3bca85714b75..8da4c95424426 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -20,6 +20,8 @@ See docs/process.md for more on how version tagging works. 3.1.63 (in development) ----------------------- +- Emscripten now uses `strptime` from musl rather than using a custom + JavaScript implementation. (#21379) 3.1.62 - 07/02/24 ----------------- diff --git a/src/library.js b/src/library.js index 130cd3c7383ba..3040e0bd56abf 100644 --- a/src/library.js +++ b/src/library.js @@ -728,270 +728,6 @@ addToLibrary({ return newDate; }, - strptime__deps: ['$isLeapYear', '$arraySum', '$addDays', '$MONTH_DAYS_REGULAR', '$MONTH_DAYS_LEAP', - '$jstoi_q', '$intArrayFromString' ], - strptime: (buf, format, tm) => { - // char *strptime(const char *restrict buf, const char *restrict format, struct tm *restrict tm); - // http://pubs.opengroup.org/onlinepubs/009695399/functions/strptime.html - var pattern = UTF8ToString(format); - - // escape special characters - // TODO: not sure we really need to escape all of these in JS regexps - var SPECIAL_CHARS = '\\!@#$^&*()+=-[]/{}|:<>?,.'; - for (var i=0, ii=SPECIAL_CHARS.length; i EQUIVALENT_MATCHERS[c] || m) - .replace(/%(.)/g, (_, c) => { - let pat = DATE_PATTERNS[c]; - if (pat){ - capture.push(c); - return `(${pat})`; - } else { - return c; - } - }) - .replace( // any number of space or tab characters match zero or more spaces - /\s+/g,'\\s*' - ); - - var matches = new RegExp('^'+pattern_out, "i").exec(UTF8ToString(buf)) - - function initDate() { - function fixup(value, min, max) { - return (typeof value != 'number' || isNaN(value)) ? min : (value>=min ? (value<=max ? value: max): min); - }; - return { - year: fixup({{{ makeGetValue('tm', C_STRUCTS.tm.tm_year, 'i32') }}} + 1900 , 1970, 9999), - month: fixup({{{ makeGetValue('tm', C_STRUCTS.tm.tm_mon, 'i32') }}}, 0, 11), - day: fixup({{{ makeGetValue('tm', C_STRUCTS.tm.tm_mday, 'i32') }}}, 1, 31), - hour: fixup({{{ makeGetValue('tm', C_STRUCTS.tm.tm_hour, 'i32') }}}, 0, 23), - min: fixup({{{ makeGetValue('tm', C_STRUCTS.tm.tm_min, 'i32') }}}, 0, 59), - sec: fixup({{{ makeGetValue('tm', C_STRUCTS.tm.tm_sec, 'i32') }}}, 0, 59), - gmtoff: 0 - }; - }; - - if (matches) { - var date = initDate(); - var value; - - var getMatch = (symbol) => { - var pos = capture.indexOf(symbol); - // check if symbol appears in regexp - if (pos >= 0) { - // return matched value or null (falsy!) for non-matches - return matches[pos+1]; - } - return; - }; - - // seconds - if ((value=getMatch('S'))) { - date.sec = jstoi_q(value); - } - - // minutes - if ((value=getMatch('M'))) { - date.min = jstoi_q(value); - } - - // hours - if ((value=getMatch('H'))) { - // 24h clock - date.hour = jstoi_q(value); - } else if ((value = getMatch('I'))) { - // AM/PM clock - var hour = jstoi_q(value); - if ((value=getMatch('p'))) { - hour += value.toUpperCase()[0] === 'P' ? 12 : 0; - } - date.hour = hour; - } - - // year - if ((value=getMatch('Y'))) { - // parse from four-digit year - date.year = jstoi_q(value); - } else if ((value=getMatch('y'))) { - // parse from two-digit year... - var year = jstoi_q(value); - if ((value=getMatch('C'))) { - // ...and century - year += jstoi_q(value)*100; - } else { - // ...and rule-of-thumb - year += year<69 ? 2000 : 1900; - } - date.year = year; - } - - // month - if ((value=getMatch('m'))) { - // parse from month number - date.month = jstoi_q(value)-1; - } else if ((value=getMatch('b'))) { - // parse from month name - date.month = MONTH_NUMBERS[value.substring(0,3).toUpperCase()] || 0; - // TODO: derive month from day in year+year, week number+day of week+year - } - - // day - if ((value=getMatch('d'))) { - // get day of month directly - date.day = jstoi_q(value); - } else if ((value=getMatch('j'))) { - // get day of month from day of year ... - var day = jstoi_q(value); - var leapYear = isLeapYear(date.year); - for (var month=0; month<12; ++month) { - var daysUntilMonth = arraySum(leapYear ? MONTH_DAYS_LEAP : MONTH_DAYS_REGULAR, month-1); - if (day<=daysUntilMonth+(leapYear ? MONTH_DAYS_LEAP : MONTH_DAYS_REGULAR)[month]) { - date.day = day-daysUntilMonth; - } - } - } else if ((value=getMatch('a'))) { - // get day of month from weekday ... - var weekDay = value.substring(0,3).toUpperCase(); - if ((value=getMatch('U'))) { - // ... and week number (Sunday being first day of week) - // Week number of the year (Sunday as the first day of the week) as a decimal number [00,53]. - // All days in a new year preceding the first Sunday are considered to be in week 0. - var weekDayNumber = DAY_NUMBERS_SUN_FIRST[weekDay]; - var weekNumber = jstoi_q(value); - - // January 1st - var janFirst = new Date(date.year, 0, 1); - var endDate; - if (janFirst.getDay() === 0) { - // Jan 1st is a Sunday, and, hence in the 1st CW - endDate = addDays(janFirst, weekDayNumber+7*(weekNumber-1)); - } else { - // Jan 1st is not a Sunday, and, hence still in the 0th CW - endDate = addDays(janFirst, 7-janFirst.getDay()+weekDayNumber+7*(weekNumber-1)); - } - date.day = endDate.getDate(); - date.month = endDate.getMonth(); - } else if ((value=getMatch('W'))) { - // ... and week number (Monday being first day of week) - // Week number of the year (Monday as the first day of the week) as a decimal number [00,53]. - // All days in a new year preceding the first Monday are considered to be in week 0. - var weekDayNumber = DAY_NUMBERS_MON_FIRST[weekDay]; - var weekNumber = jstoi_q(value); - - // January 1st - var janFirst = new Date(date.year, 0, 1); - var endDate; - if (janFirst.getDay()===1) { - // Jan 1st is a Monday, and, hence in the 1st CW - endDate = addDays(janFirst, weekDayNumber+7*(weekNumber-1)); - } else { - // Jan 1st is not a Monday, and, hence still in the 0th CW - endDate = addDays(janFirst, 7-janFirst.getDay()+1+weekDayNumber+7*(weekNumber-1)); - } - - date.day = endDate.getDate(); - date.month = endDate.getMonth(); - } - } - - // time zone - if ((value = getMatch('z'))) { - // GMT offset as either 'Z' or +-HH:MM or +-HH or +-HHMM - if (value.toLowerCase() === 'z'){ - date.gmtoff = 0; - } else { - var match = value.match(/^((?:\-|\+)\d\d):?(\d\d)?/); - date.gmtoff = match[1] * 3600; - if (match[2]) { - date.gmtoff += date.gmtoff >0 ? match[2] * 60 : -match[2] * 60 - } - } - } - - /* - tm_sec int seconds after the minute 0-61* - tm_min int minutes after the hour 0-59 - tm_hour int hours since midnight 0-23 - tm_mday int day of the month 1-31 - tm_mon int months since January 0-11 - tm_year int years since 1900 - tm_wday int days since Sunday 0-6 - tm_yday int days since January 1 0-365 - tm_isdst int Daylight Saving Time flag - tm_gmtoff long offset from GMT (seconds) - */ - - var fullDate = new Date(date.year, date.month, date.day, date.hour, date.min, date.sec, 0); - {{{ makeSetValue('tm', C_STRUCTS.tm.tm_sec, 'fullDate.getSeconds()', 'i32') }}}; - {{{ makeSetValue('tm', C_STRUCTS.tm.tm_min, 'fullDate.getMinutes()', 'i32') }}}; - {{{ makeSetValue('tm', C_STRUCTS.tm.tm_hour, 'fullDate.getHours()', 'i32') }}}; - {{{ makeSetValue('tm', C_STRUCTS.tm.tm_mday, 'fullDate.getDate()', 'i32') }}}; - {{{ makeSetValue('tm', C_STRUCTS.tm.tm_mon, 'fullDate.getMonth()', 'i32') }}}; - {{{ makeSetValue('tm', C_STRUCTS.tm.tm_year, 'fullDate.getFullYear()-1900', 'i32') }}}; - {{{ makeSetValue('tm', C_STRUCTS.tm.tm_wday, 'fullDate.getDay()', 'i32') }}}; - {{{ makeSetValue('tm', C_STRUCTS.tm.tm_yday, 'arraySum(isLeapYear(fullDate.getFullYear()) ? MONTH_DAYS_LEAP : MONTH_DAYS_REGULAR, fullDate.getMonth()-1)+fullDate.getDate()-1', 'i32') }}}; - {{{ makeSetValue('tm', C_STRUCTS.tm.tm_isdst, '0', 'i32') }}}; - {{{ makeSetValue('tm', C_STRUCTS.tm.tm_gmtoff, 'date.gmtoff', LONG_TYPE) }}}; - - // we need to convert the matched sequence into an integer array to take care of UTF-8 characters > 0x7F - // TODO: not sure that intArrayFromString handles all unicode characters correctly - return buf+intArrayFromString(matches[0]).length-1; - } - - return 0; - }, - strptime_l__deps: ['strptime'], - strptime_l: (buf, format, tm, locale) => { - return _strptime(buf, format, tm); // no locale support yet - }, - // ========================================================================== // setjmp.h // ========================================================================== diff --git a/src/library_sigs.js b/src/library_sigs.js index 0043bc0a6581b..49b7131231bbe 100644 --- a/src/library_sigs.js +++ b/src/library_sigs.js @@ -1536,8 +1536,6 @@ sigs = { rotozoomSurface__sig: 'ppddi', sched_yield__sig: 'i', setprotoent__sig: 'vi', - strptime__sig: 'pppp', - strptime_l__sig: 'ppppp', uuid_clear__sig: 'vp', uuid_compare__sig: 'ipp', uuid_copy__sig: 'vpp', diff --git a/system/lib/libc/musl/src/time/strptime.c b/system/lib/libc/musl/src/time/strptime.c index c54a0d8c4c52e..c2847ec64ef5a 100644 --- a/system/lib/libc/musl/src/time/strptime.c +++ b/system/lib/libc/musl/src/time/strptime.c @@ -1,3 +1,67 @@ +/* + * This file has been modified from upstream musl version and now includes + * some code derived from both Bionic libc and FreeBSD libc. + * + * The FreeBSD portions are coverted by the following copyright and license: + * + * https://github.com/freebsd/freebsd-src/blob/main/lib/libc/stdtime/strptime.c + * + * Copyright (c) 1997, 1998, 2005, 2008 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code was contributed to The NetBSD Foundation by Klaus Klein. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * The Bionic portions are coverted by the following copyright and license: + * + * https://github.com/aosp-mirror/platform_bionic/blob/main/libc/tzcode/strptime.c + * + * Copyright (c) 1997, 1998, 2005, 2008 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code was contributed to The NetBSD Foundation by Klaus Klein. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ #include #include #include @@ -6,8 +70,96 @@ #include #include +#ifdef __EMSCRIPTEN__ +#include +#include +#include "time_impl.h" + +enum { + TM_SUNDAY = 0, + TM_MONDAY = 1, + TM_YEAR_BASE = 1900, + TM_WDAY_BASE = 1 /* monday */, + EPOCH_YEAR = 1970, + EPOCH_WDAY = 4 /* thursday */, + DAYSPERWEEK = 7, + MONSPERYEAR = 12, + DAYSPERNYEAR = 365, + SECSPERMIN = 60, + MINSPERHOUR = 60, +}; + +int is_leap(int y) +{ + /* Avoid overflow */ + if (y>INT_MAX-1900) y -= 2000; + y += 1900; + return !(y%4) && ((y%100) || !(y%400)); +} + +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); +} + +/* RFC-822/RFC-2822 */ +static const char * const nast[5] = { + "EST", "CST", "MST", "PST", "\0\0\0" +}; +static const char * const nadt[5] = { + "EDT", "CDT", "MDT", "PDT", "\0\0\0" +}; + +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 const char * +_find_string(const char *bp, int *tgt, const char * const *n1, + const char * const *n2, int c) +{ + int i; + unsigned int len; + + /* check full name - then abbreviated ones */ + for (; n1 != NULL; n1 = n2, n2 = NULL) { + for (i = 0; i < c; i++, n1++) { + len = strlen(*n1); + if (strncasecmp(*n1, (const char *)bp, len) == 0) { + *tgt = i; + return bp + len; + } + } + } + + /* Nothing matched */ + return NULL; +} + +/* + * 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 year) +{ + return (((2 * (3 - (year / 100) % 4)) + (year % 100) + + ((year % 100) / 4) + (is_leap(year) ? 6 : 0) + 1) % 7); +} +#endif + char *strptime(const char *restrict s, const char *restrict f, struct tm *restrict tm) { +#ifdef __EMSCRIPTEN__ + int day_offset = -1, week_offset, offs; + int have_year = 0, have_yday = 0, have_wday = 0, have_mday = 0, have_mon = 0; +#endif int i, w, neg, adj, min, range, *dest, dummy; const char *ex; size_t len; @@ -33,11 +185,13 @@ char *strptime(const char *restrict s, const char *restrict f, struct tm *restri switch (*f++) { case 'a': case 'A': dest = &tm->tm_wday; + have_wday = 1; min = ABDAY_1; range = 7; goto symbolic_range; case 'b': case 'B': case 'h': dest = &tm->tm_mon; + have_mon = 1; min = ABMON_1; range = 12; goto symbolic_range; @@ -52,6 +206,7 @@ char *strptime(const char *restrict s, const char *restrict f, struct tm *restri goto numeric_digits; case 'd': case 'e': dest = &tm->tm_mday; + have_mday = 1; min = 1; range = 31; goto numeric_range; @@ -59,6 +214,12 @@ char *strptime(const char *restrict s, const char *restrict f, struct tm *restri s = strptime(s, "%m/%d/%y", tm); if (!s) return 0; break; +#if __EMSCRIPTEN__ + case 'F': // GNU extension + s = strptime(s, "%Y-%m-%d", tm); + if (!s) return 0; + break; +#endif case 'H': dest = &tm->tm_hour; min = 0; @@ -71,12 +232,14 @@ char *strptime(const char *restrict s, const char *restrict f, struct tm *restri goto numeric_range; case 'j': dest = &tm->tm_yday; + have_yday = 1; min = 1; range = 366; adj = 1; goto numeric_range; case 'm': dest = &tm->tm_mon; + have_mon = 1; min = 1; range = 12; adj = 1; @@ -125,13 +288,39 @@ char *strptime(const char *restrict s, const char *restrict f, struct tm *restri break; case 'U': case 'W': +#ifdef __EMSCRIPTEN__ + /* + * 'U' and 'W' handling code modified from FreeBSD + */ + if (!isdigit(*s)) + return 0; + + len = 2; + for (i = 0; len && *s != 0 && isdigit(*s); s++) { + i *= 10; + i += *s - '0'; + len--; + } + if (i > 53) + return (NULL); + + if (f[-1] == 'U') + day_offset = TM_SUNDAY; + else + day_offset = TM_MONDAY; + + week_offset = i; + break; +#else /* Throw away result, for now. (FIXME?) */ dest = &dummy; min = 0; range = 54; goto numeric_range; +#endif case 'w': dest = &tm->tm_wday; + have_wday = 1; min = 0; range = 7; goto numeric_range; @@ -150,10 +339,92 @@ char *strptime(const char *restrict s, const char *restrict f, struct tm *restri goto numeric_digits; case 'Y': dest = &tm->tm_year; + have_year = 1; if (w<0) w=4; adj = 1900; want_century = 0; goto numeric_digits; +#ifdef __EMSCRIPTEN__ + /* + * Timezone parsing code based on code from Bionic libc. + */ +#define SECSPERHOUR (SECSPERMIN * MINSPERHOUR) + case 'z': + /* + * We recognize all ISO 8601 formats: + * Z = Zulu time/UTC + * [+-]hhmm + * [+-]hh:mm + * [+-]hh + * We recognize all RFC-822/RFC-2822 formats: + * UT|GMT + * North American : UTC offsets + * E[DS]T = Eastern : -4 | -5 + * C[DS]T = Central : -5 | -6 + * M[DS]T = Mountain: -6 | -7 + * P[DS]T = Pacific : -7 | -8 + */ + while (isspace(*s)) + s++; + + switch (*s++) { + case 'G': + if (*s++ != 'M') + return NULL; + /*FALLTHROUGH*/ + case 'U': + if (*s++ != 'T') + return NULL; + /*FALLTHROUGH*/ + case 'Z': + tm->tm_isdst = 0; + tm->__tm_gmtoff = 0; + tm->__tm_zone = "UTC"; + continue; + case '+': + neg = 0; + break; + case '-': + neg = 1; + break; + default: + --s; + ex = _find_string(s, &i, nast, NULL, 4); + if (ex != NULL) { + tm->__tm_gmtoff = (-5 - i) * SECSPERHOUR; + tm->__tm_zone = (char *)nast[i]; + s = ex; + continue; + } + ex = _find_string(s, &i, nadt, NULL, 4); + if (ex != NULL) { + tm->tm_isdst = 1; + tm->__tm_gmtoff = (-4 - i) * SECSPERHOUR; + tm->__tm_zone = (char *)nadt[i]; + s = ex; + continue; + } + return NULL; + } + if (!isdigit(s[0]) || !isdigit(s[1])) + return NULL; + offs = ((s[0]-'0') * 10 + (s[1]-'0')) * SECSPERHOUR; + s += 2; + if (*s == ':') + s++; + if (isdigit(*s)) { + offs += (*s++ - '0') * 10 * SECSPERMIN; + if (!isdigit(*s)) + return NULL; + offs += (*s++ - '0') * SECSPERMIN; + } + if (neg) + offs = -offs; + tm->tm_isdst = 0; /* XXX */ + tm->__tm_gmtoff = offs; + tm->__tm_zone = NULL; /* XXX */ + break; +#endif case '%': if (*s++ != '%') return 0; break; @@ -199,8 +470,119 @@ char *strptime(const char *restrict s, const char *restrict f, struct tm *restri } if (want_century) { tm->tm_year = relyear; + have_year = 1; if (want_century & 2) tm->tm_year += century * 100 - 1900; else if (tm->tm_year <= 68) tm->tm_year += 100; } + +#ifdef __EMSCRIPTEN__ + /* + * Compute secondary fields based on day_offset/week_offset set + * in 'U'/'W' handlers + * Based on code in FreeBSD libc. + */ + if (!have_yday && day_offset != -1) { + int tmpwday, tmpyday, fwo; + + fwo = first_wday_of(tm->tm_year + TM_YEAR_BASE); + /* No incomplete week (week 0). */ + if (week_offset == 0 && fwo == day_offset) + return (NULL); + + /* Set the date to the first Sunday (or Monday) + * of the specified week of the year. + */ + tmpwday = have_wday ? tm->tm_wday : + day_offset; + tmpyday = (7 - fwo + day_offset) % 7 + + (week_offset - 1) * 7 + + (tmpwday - day_offset + 7) % 7; + /* Impossible yday for incomplete week (week 0). */ + if (tmpyday < 0) { + if (have_wday) + return 0; + tmpyday = 0; + } + tm->tm_yday = tmpyday; + have_yday = 1; + } + + if (have_yday && have_year) { + static 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} + }; + if (!have_mon) { + i = 0; + while (tm->tm_yday >= + start_of_month[is_leap(tm->tm_year + + TM_YEAR_BASE)][i]) + i++; + if (i > 12) { + i = 1; + tm->tm_yday -= + start_of_month[is_leap(tm->tm_year + + TM_YEAR_BASE)][12]; + tm->tm_year++; + } + tm->tm_mon = i - 1; + have_mon = 1; + } + if (!have_mday) { + tm->tm_mday = tm->tm_yday - + start_of_month[is_leap(tm->tm_year + TM_YEAR_BASE)] + [tm->tm_mon] + 1; + have_mday = 1; + } + if (!have_wday) { + i = 0; + int wday_offset = first_wday_of(tm->tm_year); + while (i++ <= tm->tm_yday) { + if (wday_offset++ >= 6) + wday_offset = 0; + } + tm->tm_wday = wday_offset; + have_wday = 1; + } + } + + /* + * Computed field code based on code from Bionic libc. + */ + + if (!have_mday && tm->tm_mday < 31) have_mday = 1; + if (!have_mon && tm->tm_mon < 12) have_mon = 1; + const int *mon_lens = mon_lengths[is_leap(tm->tm_year)]; + if (!have_yday && have_mon && have_mday) { + tm->tm_yday = tm->tm_mday - 1; + for (i = 0; i < tm->tm_mon; i++) + tm->tm_yday += mon_lens[i]; + have_yday = 1; + } + if (have_yday) { + int days = tm->tm_yday; + if (!have_wday) { + const int year = tm->tm_year + TM_YEAR_BASE; + 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 (!have_mon) { + tm->tm_mon = 0; + while (tm->tm_mon < MONSPERYEAR && days >= mon_lens[tm->tm_mon]) + days -= mon_lens[tm->tm_mon++]; + } + if (!have_mday) + tm->tm_mday = days + 1; + } +#endif + return (char *)s; } diff --git a/tools/system_libs.py b/tools/system_libs.py index ba9e3b36135b0..402d411df5863 100644 --- a/tools/system_libs.py +++ b/tools/system_libs.py @@ -1228,6 +1228,7 @@ def get_files(self): 'utime.c', '__map_file.c', 'strftime.c', + 'strptime.c', '__tz.c', '__tm_to_secs.c', '__year_to_secs.c',