diff --git a/Lib/_pydatetime.py b/Lib/_pydatetime.py index 50126d039e830a..062f31427ab2dd 100644 --- a/Lib/_pydatetime.py +++ b/Lib/_pydatetime.py @@ -38,6 +38,10 @@ def _get_class_module(self): _DAYS_IN_MONTH = [-1, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31] _DAYS_BEFORE_MONTH = [-1] # -1 is a placeholder for indexing purposes. + +# This additional list stores the number of odd days before each month in the year. +_ODD_DAYS_BEFORE_MONTH = [-1, 0, 3, 3, 6, 1, 4, 6, 2, 5, 0, 3, 5] #-1 is a placeholder for indexing purposes. + dbm = 0 for dim in _DAYS_IN_MONTH[1:]: _DAYS_BEFORE_MONTH.append(dbm) @@ -53,6 +57,13 @@ def _days_before_year(year): y = year - 1 return y*365 + y//4 - y//100 + y//400 +def _odd_days_before_year(year): + "year -> number of odd days before January 1st of year." + y = year - 1 + r = y%400 + m100 = y%100 + return r//300 + (r%300)//200*3 + ((r%300)%200)//100*5 + (m100//4)*2 + (m100 - m100//4) + def _days_in_month(year, month): "year, month -> number of days in that month in that year." assert 1 <= month <= 12, f"month must be in 1..12, not {month}" @@ -74,6 +85,17 @@ def _ymd2ord(year, month, day): _days_before_month(year, month) + day) +# This additional function computes the number of odd days before the start of the given year way more reduced here than just multplying years with 365 & 366 and month values. +def _odd_ymd2ord(year, month, day): + "year, month, day -> ordinal, considering 01-Jan-0001 as day 1." + assert 1 <= month <= 12, f"month must be in 1..12, not {month}" + dim = _days_in_month(year, month) + assert 1 <= day <= dim, f"day must be in 1..{dim}, not {day}" + # return (_days_before_year(year) + + # _days_before_month(year, month) + + # day) + return (_odd_days_before_year(year) + _ODD_DAYS_BEFORE_MONTH[month - 1] + day + 1) if(_is_leap(year) and month > 2) else (_odd_days_before_year(year) + _ODD_DAYS_BEFORE_MONTH[month - 1] + day) + _DI400Y = _days_before_year(401) # number of days in 400 years _DI100Y = _days_before_year(101) # " " " " 100 " _DI4Y = _days_before_year(5) # " " " " 4 " @@ -1181,6 +1203,14 @@ def toordinal(self): """ return _ymd2ord(self._year, self._month, self._day) + def to_odd_ordinal(self): + """Return reduced number of proleptic Gregorian ordinal for the year, month and day. + + January 1 of year 1 is day 1. Only the year, month and day values + contribute to the result. + """ + return _odd_ymd2ord(self._year, self._month, self._day) + def replace(self, year=None, month=None, day=None): """Return a new date with new values for the specified fields.""" if year is None: @@ -1258,7 +1288,7 @@ def __sub__(self, other): def weekday(self): "Return day of the week, where Monday == 0 ... Sunday == 6." - return (self.toordinal() + 6) % 7 + return (self.to_odd_ordinal()) % 7 # Day-of-the-week and week-of-the-year, according to ISO diff --git a/Modules/_datetimemodule.c b/Modules/_datetimemodule.c index bd76b3bd81cce4..f3f1e4ede3eceb 100644 --- a/Modules/_datetimemodule.c +++ b/Modules/_datetimemodule.c @@ -424,6 +424,12 @@ static const int _days_before_month[] = { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 }; +/* This additional array stores the number of odd days before each month in the year. */ +static const int _odd_days_before_month[] = { + 0, /* unused; this vector uses 1-based indexing */ + 0, 3, 3, 6, 1, 4, 6, 2, 5, 0, 3, 5 +}; + /* year -> 1 if leap year, else 0. */ static int is_leap(int year) @@ -478,6 +484,21 @@ days_before_year(int year) return y*365 + y/4 - y/100 + y/400; } +/*This additional function here computes the number of odd days before the start of the given year.*/ +static int +odd_days_before_year(int year) +{ + int y = year - 1; + /* This is incorrect if year <= 0; we really want the floor + * here. But so long as MINYEAR is 1, the smallest year this + * can see is 1. + */ + assert (year >= 1); + int temp = (y%400); + int mod100 = (y%100); + return temp/300+(temp%300)/200*3+((temp%300)%200)/100*5 + (mod100/4)*2 + (mod100-mod100/4); +} + /* Number of days in 4, 100, and 400 year cycles. That these have * the correct values is asserted in the module init function. */ @@ -576,11 +597,19 @@ ymd_to_ord(int year, int month, int day) return days_before_year(year) + days_before_month(year, month) + day; } +/* This additional function computes the number of odd days before the start of the given year way more reduced here than just multplying years with 365 & 366 and month values. */ +/* year, month, day -> ordinal, considering 01-Jan-0001 as day 1. */ +static int +odd_ymd_to_ord(int year, int month, int day) +{ + return (is_leap(year) && month > 2) ? (odd_days_before_year(year) + _odd_days_before_month[month - 1] + day+1) : (odd_days_before_year(year) + _odd_days_before_month[month - 1] + day); +} + /* Day of week, where Monday==0, ..., Sunday==6. 1/1/1 was a Monday. */ static int weekday(int year, int month, int day) { - return (ymd_to_ord(year, month, day) + 6) % 7; + return (odd_ymd_to_ord(year, month, day)) % 7; } /* Ordinal of the Monday starting week 1 of the ISO year. Week 1 is the