git: 4285e024baa8 - main - strptime: Fix day-of-week calculation.
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Mon, 09 Dec 2024 12:38:39 UTC
The branch main has been updated by des: URL: https://cgit.FreeBSD.org/src/commit/?id=4285e024baa80f81d13cdcc016fdf0721fe57862 commit 4285e024baa80f81d13cdcc016fdf0721fe57862 Author: Dag-Erling Smørgrav <des@FreeBSD.org> AuthorDate: 2024-12-09 12:37:45 +0000 Commit: Dag-Erling Smørgrav <des@FreeBSD.org> CommitDate: 2024-12-09 12:38:22 +0000 strptime: Fix day-of-week calculation. The day-of-week calculation used the raw year value without adjusting for TM_YEAR_BASE, so it was off by one for 300 years out of every 400; it just happened to be correct for 1901 through 2000. It also used a loop where a simple addition would have sufficed. While here, simplify our version of Gauss's algorithm, and document that we assume the Gregorian calendar. MFC after: 1 week PR: 282916 Reviewed by: imp, allanjude, philip Differential Revision: https://reviews.freebsd.org/D47977 --- etc/mtree/BSD.tests.dist | 2 ++ lib/libc/stdtime/strptime.3 | 7 ++++- lib/libc/stdtime/strptime.c | 22 ++++++--------- lib/libc/tests/Makefile | 1 + lib/libc/tests/stdtime/Makefile | 7 +++++ lib/libc/tests/stdtime/strptime_test.c | 50 ++++++++++++++++++++++++++++++++++ 6 files changed, 74 insertions(+), 15 deletions(-) diff --git a/etc/mtree/BSD.tests.dist b/etc/mtree/BSD.tests.dist index 1762bbfb7bdc..e0c16bd5e570 100644 --- a/etc/mtree/BSD.tests.dist +++ b/etc/mtree/BSD.tests.dist @@ -382,6 +382,8 @@ .. stdlib .. + stdtime + .. string .. sys diff --git a/lib/libc/stdtime/strptime.3 b/lib/libc/stdtime/strptime.3 index 0dfa33aa29cb..7df73d2d080a 100644 --- a/lib/libc/stdtime/strptime.3 +++ b/lib/libc/stdtime/strptime.3 @@ -23,7 +23,7 @@ .\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF .\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. .\" " -.Dd October 2, 2014 +.Dd December 9, 2024 .Dt STRPTIME 3 .Os .Sh NAME @@ -135,6 +135,11 @@ function has been contributed by Powerdog Industries. .Pp This man page was written by .An J\(:org Wunsch . +.Sh CAVEATS +The +.Fn strptime +function assumes the Gregorian calendar and will produce incorrect +results for dates prior to its introduction. .Sh BUGS Both the .Fa %e diff --git a/lib/libc/stdtime/strptime.c b/lib/libc/stdtime/strptime.c index c988d968d580..5f1293c7a267 100644 --- a/lib/libc/stdtime/strptime.c +++ b/lib/libc/stdtime/strptime.c @@ -62,17 +62,16 @@ static char * _strptime(const char *, const char *, struct tm *, int *, locale_t #define FLAG_WDAY (1 << 5) /* - * 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 + * Gauss's algorithm for the day of the week of the first day of any year + * in the Gregorian calendar. */ - static int first_wday_of(int year) { - return (((2 * (3 - (year / 100) % 4)) + (year % 100) + - ((year % 100) / 4) + (isleap(year) ? 6 : 0) + 1) % 7); + return ((1 + + 5 * ((year - 1) % 4) + + 4 * ((year - 1) % 100) + + 6 * ((year - 1) % 400)) % 7); } static char * @@ -674,13 +673,8 @@ label: flags |= FLAG_MDAY; } if (!(flags & FLAG_WDAY)) { - i = 0; - 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; + wday_offset = first_wday_of(tm->tm_year + TM_YEAR_BASE); + tm->tm_wday = (wday_offset + tm->tm_yday) % 7; flags |= FLAG_WDAY; } } diff --git a/lib/libc/tests/Makefile b/lib/libc/tests/Makefile index 76a79a9f578b..975c895770ee 100644 --- a/lib/libc/tests/Makefile +++ b/lib/libc/tests/Makefile @@ -16,6 +16,7 @@ TESTS_SUBDIRS+= secure TESTS_SUBDIRS+= setjmp TESTS_SUBDIRS+= stdio TESTS_SUBDIRS+= stdlib +TESTS_SUBDIRS+= stdtime TESTS_SUBDIRS+= string TESTS_SUBDIRS+= sys TESTS_SUBDIRS+= termios diff --git a/lib/libc/tests/stdtime/Makefile b/lib/libc/tests/stdtime/Makefile new file mode 100644 index 000000000000..c7a7f5b9436f --- /dev/null +++ b/lib/libc/tests/stdtime/Makefile @@ -0,0 +1,7 @@ +.include <bsd.own.mk> + +ATF_TESTS_C+= strptime_test + +TESTSDIR:= ${TESTSBASE}/${RELDIR:C/libc\/tests/libc/} + +.include <bsd.test.mk> diff --git a/lib/libc/tests/stdtime/strptime_test.c b/lib/libc/tests/stdtime/strptime_test.c new file mode 100644 index 000000000000..79a97764999c --- /dev/null +++ b/lib/libc/tests/stdtime/strptime_test.c @@ -0,0 +1,50 @@ +/*- + * Copyright (c) 2024 Dag-Erling Smørgrav + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include <time.h> + +#include <atf-c.h> + +ATF_TC_WITHOUT_HEAD(dayofweek); +ATF_TC_BODY(dayofweek, tc) +{ + static const struct { + const char *str; + int wday; + } cases[] = { + { "1582-12-20", 1 }, + { "1700-03-01", 1 }, + { "1752-09-14", 4 }, + { "1800-12-31", 3 }, + { "1801-01-01", 4 }, + { "1900-12-31", 1 }, + { "1901-01-01", 2 }, + { "2000-12-31", 0 }, + { "2001-01-01", 1 }, + { "2100-12-31", 5 }, + { "2101-01-01", 6 }, + { "2200-12-31", 3 }, + { "2201-01-01", 4 }, + { }, + }; + struct tm tm; + + for (unsigned int i = 0; cases[i].str != NULL; i++) { + if (strptime(cases[i].str, "%Y-%m-%d", &tm) == NULL) { + atf_tc_fail_nonfatal("failed to parse %s", + cases[i].str); + } else if (tm.tm_wday != cases[i].wday) { + atf_tc_fail_nonfatal("expected %d for %s, got %d", + cases[i].wday, cases[i].str, tm.tm_wday); + } + } +} + +ATF_TP_ADD_TCS(tp) +{ + ATF_TP_ADD_TC(tp, dayofweek); + return (atf_no_error()); +}