-
-
Notifications
You must be signed in to change notification settings - Fork 18.4k
standalone implementation of ccalendar #18540
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 21 commits
4e942f5
6cbb4be
7f22c6d
94557a7
ce8e3d5
344252e
a084fb3
1b74668
bc4da9e
fdf280e
f8cc945
0cfd9a4
a29950b
3d07a2a
b16f443
9ec475a
b221710
d91ec05
8f78992
32be4f4
3fda037
03a854d
d0f8b13
505fa9e
0add03e
b66caa6
7cc0824
6b245cf
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
# -*- coding: utf-8 -*- | ||
# cython: profile=False | ||
|
||
from cython cimport Py_ssize_t | ||
|
||
from numpy cimport int64_t, int32_t | ||
|
||
|
||
cpdef monthrange(int64_t year, Py_ssize_t month) | ||
|
||
cdef int dayofweek(int y, int m, int m) nogil | ||
cdef bint is_leapyear(int64_t year) nogil | ||
cpdef int32_t get_days_in_month(int year, Py_ssize_t month) nogil | ||
cpdef int32_t get_week_of_year(int year, int month, int day) nogil |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,194 @@ | ||
# -*- coding: utf-8 -*- | ||
# cython: profile=False | ||
# cython: boundscheck=False | ||
""" | ||
Cython implementations of functions resembling the stdlib calendar module | ||
""" | ||
|
||
cimport cython | ||
from cython cimport Py_ssize_t | ||
|
||
import numpy as np | ||
cimport numpy as np | ||
from numpy cimport int64_t, int32_t | ||
np.import_array() | ||
|
||
|
||
# ---------------------------------------------------------------------- | ||
# Constants | ||
|
||
# Slightly more performant cython lookups than a 2D table | ||
# The first 12 entries correspond to month lengths for non-leap years. | ||
# The remaining 12 entries give month lengths for leap years | ||
cdef int32_t* days_per_month_array = [ | ||
31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, | ||
31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31] | ||
|
||
cdef int* sakamoto_arr = [0, 3, 2, 5, 0, 3, 5, 1, 4, 6, 2, 4] | ||
|
||
# The first 12 entries give the month days elapsed as of the first of month N | ||
# in non-leap years. The remaining 12 entries give the days elapsed in leap | ||
# years. | ||
cdef int32_t* _month_offset = [ | ||
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] | ||
|
||
# ---------------------------------------------------------------------- | ||
|
||
|
||
@cython.wraparound(False) | ||
@cython.boundscheck(False) | ||
cpdef inline int32_t get_days_in_month(int year, Py_ssize_t month) nogil: | ||
"""Return the number of days in the given month of the given year. | ||
|
||
Parameters | ||
---------- | ||
year : int | ||
month : int | ||
|
||
Returns | ||
------- | ||
days_in_month : int | ||
|
||
Notes | ||
----- | ||
Assumes that the arguments are valid. Passing a month not between 1 and 12 | ||
risks a segfault. | ||
""" | ||
return days_per_month_array[12 * is_leapyear(year) + month - 1] | ||
|
||
|
||
@cython.wraparound(False) | ||
@cython.boundscheck(False) | ||
cpdef monthrange(int64_t year, Py_ssize_t month): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. why do we need this as a function that returns a tuple. its it simply better to split into 2, e.g. monthday and dayofweek? its 2 functions, but then these become all nogil There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Those two separate functions also exist here. There's not much reason to retain monthrange, you're right. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. are you changing this in this PR? |
||
""" | ||
Return tuple containing the weekday of the first day of the month and | ||
the number of days in the month. | ||
|
||
Parameters | ||
---------- | ||
year : int | ||
month : int | ||
|
||
Returns | ||
------- | ||
weekday : int | ||
days_in_month : int | ||
|
||
Raises | ||
------ | ||
ValueError if month is invalid | ||
""" | ||
cdef: | ||
int32_t days | ||
|
||
if month < 1 or month > 12: | ||
raise ValueError("bad month number 0; must be 1-12") | ||
|
||
days = get_days_in_month(year, month) | ||
return (dayofweek(year, month, 1), days) | ||
|
||
|
||
@cython.wraparound(False) | ||
@cython.boundscheck(False) | ||
@cython.cdivision | ||
cdef int dayofweek(int y, int m, int d) nogil: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It seems you are duplicating things from There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
@jorisvandenbossche think porting less than duplicating. The np_datetime.c version is never actually used in src/datetime (will remove old version in follow-up). As long as perf doesn't take a hit, the default should by python/cython right? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sorry I don't understand your explanation. Previously it was imported it from np_datetime.c, and you reimplemented it here and changed that import. That seems like duplication rather than porting?
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
You're right. The intention is to remove the np_datetime.c version in a follow-up PR.
* The name overlap between np_datetime.c and tslibs.np_datetime is not my favorite. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would rather include extra deps than duplicate code. mark this for a followup. |
||
"""Find the day of week for the date described by the Y/M/D triple y, m, d | ||
using Sakamoto's method, from wikipedia. | ||
|
||
0 represents Monday. See [1]_. | ||
|
||
Parameters | ||
---------- | ||
y : int | ||
m : int | ||
d : int | ||
|
||
Returns | ||
------- | ||
weekday : int | ||
|
||
Notes | ||
----- | ||
Assumes that y, m, d, represents a valid date. | ||
|
||
See Also | ||
-------- | ||
[1] https://docs.python.org/3.6/library/calendar.html#calendar.weekday | ||
|
||
[2] https://en.wikipedia.org/wiki/\ | ||
Determination_of_the_day_of_the_week#Sakamoto.27s_methods | ||
""" | ||
cdef: | ||
int day | ||
|
||
y -= m < 3 | ||
day = (y + y / 4 - y / 100 + y / 400 + sakamoto_arr[m - 1] + d) % 7 | ||
# convert to python day | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. add a link to python dayofweek docs (e.g. its sunday 0, -6) |
||
return (day + 6) % 7 | ||
|
||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. on get_week_of_year you have boundscheck/wraparound but not here, do we need in either? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is this comment about There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. yea |
||
cdef bint is_leapyear(int64_t year) nogil: | ||
"""Returns 1 if the given year is a leap year, 0 otherwise. | ||
|
||
Parameters | ||
---------- | ||
year : int | ||
|
||
Returns | ||
------- | ||
is_leap : bool | ||
""" | ||
return ((year & 0x3) == 0 and # year % 4 == 0 | ||
((year % 100) != 0 or (year % 400) == 0)) | ||
|
||
|
||
@cython.wraparound(False) | ||
@cython.boundscheck(False) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. you changing the boundscheck/wraparound in this PR? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hadn't planned on it. Are they harmful? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. prob not harmful but extra code and confusing. pls change. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Well in There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. where are the array lookups? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Line 174 lookup in month_offset |
||
cpdef int32_t get_week_of_year(int year, int month, int day) nogil: | ||
"""Return the ordinal week-of-year for the given day. | ||
|
||
Parameters | ||
---------- | ||
year : int | ||
month : int | ||
day : int | ||
|
||
Returns | ||
------- | ||
week_of_year : int32_t | ||
|
||
Notes | ||
----- | ||
Assumes the inputs describe a valid date. | ||
""" | ||
cdef: | ||
bint isleap, isleap_prev | ||
int32_t mo_off | ||
int32_t doy, dow | ||
int woy | ||
|
||
isleap = is_leapyear(year) | ||
isleap_prev = is_leapyear(year - 1) | ||
|
||
mo_off = _month_offset[isleap * 13 + month - 1] | ||
|
||
doy = mo_off + day | ||
dow = dayofweek(year, month, day) | ||
|
||
# estimate | ||
woy = (doy - 1) - dow + 3 | ||
if woy >= 0: | ||
woy = woy / 7 + 1 | ||
|
||
# verify | ||
if woy < 0: | ||
if (woy > -2) or (woy == -2 and isleap_prev): | ||
woy = 53 | ||
else: | ||
woy = 52 | ||
elif woy == 53: | ||
if 31 - day + dow < 3: | ||
woy = 1 | ||
|
||
return woy |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is this only used in fields? no-where else?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Only implemented in fields. Haven't updated others yet. A couple places in offsets, a few more fastpaths in Timestamp.