Skip to content

Commit e41ac9f

Browse files
committed
switch to gauss algo
1 parent 6f51d03 commit e41ac9f

File tree

3 files changed

+40
-13
lines changed

3 files changed

+40
-13
lines changed

pandas/_libs/tslibs/ccalendar.pyx

Lines changed: 32 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"""
33
Cython implementations of functions resembling the stdlib calendar module
44
"""
5-
5+
from libc.stdlib cimport abs as c_abs
66
cimport cython
77
from numpy cimport (
88
int32_t,
@@ -20,6 +20,7 @@ cdef int32_t* days_per_month_array = [
2020
31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
2121

2222
cdef int* sakamoto_arr = [0, 3, 2, 5, 0, 3, 5, 1, 4, 6, 2, 4]
23+
cdef int* em = [0 ,0 ,31 ,59 ,90 ,120 ,151 ,181 ,212 ,243 ,273 ,304 ,334]
2324

2425
# The first 13 entries give the month days elapsed as of the first of month N
2526
# (or the total number of days in the year for N=13) in non-leap years.
@@ -73,14 +74,23 @@ cpdef int32_t get_days_in_month(int year, Py_ssize_t month) noexcept nogil:
7374
"""
7475
return days_per_month_array[12 * is_leapyear(year) + month - 1]
7576

77+
@cython.wraparound(False)
78+
@cython.boundscheck(False)
79+
@cython.cdivision(True)
80+
cdef long quot(long a , long b) noexcept nogil:
81+
cdef long x
82+
x = a/b
83+
if ( a < 0 ):
84+
x -= ( a % b != 0 )
85+
return x
7686

7787
@cython.wraparound(False)
7888
@cython.boundscheck(False)
79-
@cython.cdivision
89+
@cython.cdivision(True)
8090
cdef int dayofweek(int y, int m, int d) noexcept nogil:
8191
"""
8292
Find the day of week for the date described by the Y/M/D triple y, m, d
83-
using Sakamoto's method, from wikipedia.
93+
using Gauss' method, from wikipedia.
8494
8595
0 represents Monday. See [1]_.
8696
@@ -103,16 +113,26 @@ cdef int dayofweek(int y, int m, int d) noexcept nogil:
103113
[1] https://docs.python.org/3/library/calendar.html#calendar.weekday
104114
105115
[2] https://en.wikipedia.org/wiki/\
106-
Determination_of_the_day_of_the_week#Sakamoto.27s_methods
116+
Determination_of_the_day_of_the_week#Gauss's_algorithm
107117
"""
108-
cdef:
109-
int day
110-
111-
y -= m < 3
112-
day = (y + y / 4 - y / 100 + y / 400 + sakamoto_arr[m - 1] + d) % 7
113-
# convert to python day
114-
return (day + 6) % 7
115-
118+
119+
cdef:
120+
long c
121+
int g
122+
int f
123+
int e
124+
125+
if (m < 3):
126+
y -= 1
127+
128+
c = quot(y, 100)
129+
g = y - quot(y, 100) * 100
130+
f = 5 * (c - quot(c, 4) * 4)
131+
e = em[m]
132+
133+
if (m > 2):
134+
e -= 1;
135+
return (-1 + d + e + f + g + g/4) % 7
116136

117137
cdef bint is_leapyear(int64_t year) noexcept nogil:
118138
"""

pandas/_libs/tslibs/timestamps.pyx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2463,7 +2463,7 @@ default 'raise'
24632463
Return the day of the week represented by the date.
24642464
24652465
Monday == 0 ... Sunday == 6.
2466-
"""
2466+
"""
24672467
# same as super().weekday(), but that breaks because of how
24682468
# we have overridden year, see note in create_timestamp_from_ts
24692469
return ccalendar.dayofweek(self.year, self.month, self.day)

pandas/tests/scalar/timestamp/test_timestamp.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,13 @@ def test_resolution(self):
223223
assert dt.as_unit("ms").resolution == Timedelta(milliseconds=1)
224224
assert dt.as_unit("s").resolution == Timedelta(seconds=1)
225225

226+
@pytest.mark.parametrize("date_string,expected", [("0000-2-29", 1), ("0000-3-1", 2), ("1582-10-14", 3), ("1582-10-15", 4)])
227+
def test_dow_historic(self, date_string, expected):
228+
# GH 53738
229+
dt = Timestamp(date_string)
230+
dow = dt.weekday()
231+
assert dow == expected
232+
226233

227234
class TestTimestamp:
228235
def test_default_to_stdlib_utc(self):

0 commit comments

Comments
 (0)