Skip to content

Commit a2ab4ad

Browse files
[3.15] gh-159714: Fix ZoneInfo Julian day 59 in leap years (GH-159721) (#160052)
(cherry picked from commit 16c0adc) Co-authored-by: Paul Ganssle <1377457+pganssle@users.noreply.github.com>
1 parent d0f6a5a commit a2ab4ad

4 files changed

Lines changed: 31 additions & 2 deletions

File tree

‎Lib/test/test_zoneinfo/test_zoneinfo.py‎

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1492,6 +1492,33 @@ def _add():
14921492
(datetime(2020, 12, 31, 23, 59, 59, 999999), AAA, NORMAL),
14931493
)
14941494

1495+
@call
1496+
def _add():
1497+
# Julian days 58, 59 and 60 always mean February 27, February 28
1498+
# and March 1, including in leap years.
1499+
AAA = ZoneOffset("AAA", ZERO)
1500+
BBB = ZoneOffset("BBB", ONE_H, ONE_H)
1501+
1502+
for start, end, start_date, end_date in (
1503+
(58, 59, (2, 27), (2, 28)),
1504+
(59, 60, (2, 28), (3, 1)),
1505+
):
1506+
tzstr = f"AAA0BBB,J{start}/12,J{end}/12"
1507+
cases[tzstr] = []
1508+
for year in (2019, 2020, 2000, 2100):
1509+
start_dt = datetime(year, *start_date, 12)
1510+
end_dt = datetime(year, *end_date, 12)
1511+
cases[tzstr].extend((
1512+
(start_dt - timedelta(seconds=1), AAA, NORMAL),
1513+
(start_dt, AAA, GAP),
1514+
(start_dt.replace(fold=1), BBB, GAP),
1515+
(start_dt + ONE_H, BBB, NORMAL),
1516+
(end_dt - ONE_H - timedelta(seconds=1), BBB, NORMAL),
1517+
(end_dt - ONE_H, BBB, FOLD),
1518+
((end_dt - ONE_H).replace(fold=1), AAA, FOLD),
1519+
(end_dt, AAA, NORMAL),
1520+
))
1521+
14951522
@call
14961523
def _add():
14971524
# Taken from America/Godthab, this rule has a transition on the

‎Lib/zoneinfo/_zoneinfo.py‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -535,7 +535,7 @@ def year_to_epoch(self, year):
535535
days_before_year = _post_epoch_days_before_year(year)
536536

537537
d = self.d
538-
if self.julian and d >= 59 and calendar.isleap(year):
538+
if self.julian and d >= 60 and calendar.isleap(year):
539539
d += 1
540540

541541
epoch = (days_before_year + d) * 86400
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Fix :class:`zoneinfo.ZoneInfo` interpreting the Julian day rule ``J59`` as
2+
February 29 in leap years instead of February 28.

‎Modules/_zoneinfo.c‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1436,7 +1436,7 @@ dayrule_year_to_timestamp(TransitionRuleType *base_self, int year)
14361436
// you would use a Julian day, e.g. J91 always refers to April 1st and J365
14371437
// always refers to December 31st.
14381438
uint16_t day = self->day;
1439-
if (self->julian && day >= 59 && is_leap_year(year)) {
1439+
if (self->julian && day >= 60 && is_leap_year(year)) {
14401440
day += 1;
14411441
}
14421442

0 commit comments

Comments
 (0)