Skip to content

Commit ce12f78

Browse files
committed
Support for Orthodox Easter
1 parent c067bcd commit ce12f78

File tree

2 files changed

+59
-3
lines changed

2 files changed

+59
-3
lines changed

pandas/_libs/tslibs/offsets.pyx

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4520,6 +4520,12 @@ cdef class Easter(SingleConstructorOffset):
45204520
The number of years represented.
45214521
normalize : bool, default False
45224522
Normalize start/end dates to midnight before generating date range.
4523+
method : int, default 3
4524+
The method used to calculate the date of Easter. Valid options are:
4525+
- 1 (EASTER_JULIAN): Original calculation in Julian calendar
4526+
- 2 (EASTER_ORTHODOX): Original method, with date converted to Gregorian calendar
4527+
- 3 (EASTER_WESTERN): Revised method, in Gregorian calendar
4528+
These constants are defined in the `dateutil.easter` module.
45234529
45244530
See Also
45254531
--------
@@ -4532,15 +4538,28 @@ cdef class Easter(SingleConstructorOffset):
45324538
Timestamp('2022-04-17 00:00:00')
45334539
"""
45344540

4541+
_attributes = tuple(["n", "normalize", "method"])
4542+
4543+
cdef readonly:
4544+
int method
4545+
4546+
from dateutil.easter import EASTER_WESTERN
4547+
4548+
def __init__(self, n=1, normalize=False, method=EASTER_WESTERN):
4549+
BaseOffset.__init__(self, n, normalize)
4550+
4551+
self.method = method
4552+
45354553
cpdef __setstate__(self, state):
45364554
self.n = state.pop("n")
45374555
self.normalize = state.pop("normalize")
4556+
self.method = state.pop("method")
45384557

45394558
@apply_wraps
45404559
def _apply(self, other: datetime) -> datetime:
45414560
from dateutil.easter import easter
45424561

4543-
current_easter = easter(other.year)
4562+
current_easter = easter(other.year, method=self.method)
45444563
current_easter = datetime(
45454564
current_easter.year, current_easter.month, current_easter.day
45464565
)
@@ -4555,7 +4574,7 @@ cdef class Easter(SingleConstructorOffset):
45554574

45564575
# NOTE: easter returns a datetime.date so we have to convert to type of
45574576
# other
4558-
new = easter(other.year + n)
4577+
new = easter(other.year + n, method=self.method)
45594578
new = datetime(
45604579
new.year,
45614580
new.month,
@@ -4573,7 +4592,7 @@ cdef class Easter(SingleConstructorOffset):
45734592

45744593
from dateutil.easter import easter
45754594

4576-
return date(dt.year, dt.month, dt.day) == easter(dt.year)
4595+
return date(dt.year, dt.month, dt.day) == easter(dt.year, method=self.method)
45774596

45784597

45794598
# ----------------------------------------------------------------------

pandas/tests/tseries/offsets/test_easter.py

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
from pandas.tests.tseries.offsets.common import assert_offset_equal
1313

1414
from pandas.tseries.offsets import Easter
15+
from dateutil.easter import EASTER_WESTERN, EASTER_ORTHODOX
1516

1617

1718
class TestEaster:
@@ -32,3 +33,39 @@ class TestEaster:
3233
)
3334
def test_offset(self, offset, date, expected):
3435
assert_offset_equal(offset, date, expected)
36+
37+
@pytest.mark.parametrize(
38+
"offset,date,expected",
39+
[
40+
(Easter(method=EASTER_WESTERN), datetime(2010, 1, 1), datetime(2010, 4, 4)),
41+
(Easter(method=EASTER_WESTERN), datetime(2010, 4, 5), datetime(2011, 4, 24)),
42+
(Easter(2, method=EASTER_WESTERN), datetime(2010, 1, 1), datetime(2011, 4, 24)),
43+
(Easter(method=EASTER_WESTERN), datetime(2010, 4, 4), datetime(2011, 4, 24)),
44+
(Easter(2, method=EASTER_WESTERN), datetime(2010, 4, 4), datetime(2012, 4, 8)),
45+
(-Easter(method=EASTER_WESTERN), datetime(2011, 1, 1), datetime(2010, 4, 4)),
46+
(-Easter(method=EASTER_WESTERN), datetime(2010, 4, 5), datetime(2010, 4, 4)),
47+
(-Easter(2, method=EASTER_WESTERN), datetime(2011, 1, 1), datetime(2009, 4, 12)),
48+
(-Easter(method=EASTER_WESTERN), datetime(2010, 4, 4), datetime(2009, 4, 12)),
49+
(-Easter(2, method=EASTER_WESTERN), datetime(2010, 4, 4), datetime(2008, 3, 23)),
50+
],
51+
)
52+
def test_western_easter_offset(self, offset, date, expected):
53+
assert_offset_equal(offset, date, expected)
54+
55+
@pytest.mark.parametrize(
56+
"offset,date,expected",
57+
[
58+
(Easter(method=EASTER_ORTHODOX), datetime(2010, 1, 1), datetime(2010, 4, 4)),
59+
(Easter(method=EASTER_ORTHODOX), datetime(2010, 4, 5), datetime(2011, 4, 24)),
60+
(Easter(2, method=EASTER_ORTHODOX), datetime(2010, 1, 1), datetime(2011, 4, 24)),
61+
(Easter(method=EASTER_ORTHODOX), datetime(2010, 4, 4), datetime(2011, 4, 24)),
62+
(Easter(2, method=EASTER_ORTHODOX), datetime(2010, 4, 4), datetime(2012, 4, 15)),
63+
(-Easter(method=EASTER_ORTHODOX), datetime(2011, 1, 1), datetime(2010, 4, 4)),
64+
(-Easter(method=EASTER_ORTHODOX), datetime(2010, 4, 5), datetime(2010, 4, 4)),
65+
(-Easter(2, method=EASTER_ORTHODOX), datetime(2011, 1, 1), datetime(2009, 4, 19)),
66+
(-Easter(method=EASTER_ORTHODOX), datetime(2010, 4, 4), datetime(2009, 4, 19)),
67+
(-Easter(2, method=EASTER_ORTHODOX), datetime(2010, 4, 4), datetime(2008, 4, 27)),
68+
],
69+
)
70+
def test_orthodox_easter_offset(self, offset, date, expected):
71+
assert_offset_equal(offset, date, expected)

0 commit comments

Comments
 (0)