Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 13 additions & 1 deletion src/pendulum/datetime.py
Original file line number Diff line number Diff line change
Expand Up @@ -826,7 +826,19 @@ def _start_of_day(self) -> Self:
"""
Reset the time to 00:00:00.
"""
return self.at(0, 0, 0, 0)
dt = self.at(0, 0, 0, 0)

# In zones that spring forward exactly at midnight, 00:00:00 does not
# exist. When ``fold`` is 0, resolving that nonexistent time shifts it
# backward into the previous day, which is not the start of this day.
# Detect that the day changed and rebuild the first valid instant of
# the day by resolving forward (fold=1) instead.
if dt.day != self.day:
dt = self.__class__.create(
self.year, self.month, self.day, 0, 0, 0, 0, tz=self.tz, fold=1
)

return dt

def _end_of_day(self) -> Self:
"""
Expand Down
15 changes: 15 additions & 0 deletions tests/datetime/test_start_end_of.py
Original file line number Diff line number Diff line change
Expand Up @@ -323,3 +323,18 @@ def test_end_of_on_date_after_transition():
assert d.end_of("day").offset == 3600
assert d.end_of("month").offset == 3600
assert d.end_of("year").offset == 3600


def test_start_of_day_when_midnight_does_not_exist():
# Chile/Continental springs forward exactly at midnight on 2025-09-07,
# so 00:00:00 does not exist that day. start_of("day") must resolve to the
# first valid instant of the day (01:00:00-03:00), not fall back into the
# previous day. See issue #915.
d = pendulum.datetime(2025, 9, 6, 0, 0, tz="Chile/Continental").add(days=1)
new = d.start_of("day")

assert new.day == 7
assert_datetime(new, 2025, 9, 7, 1, 0, 0, 0)
assert new.offset == -3 * 3600
# start_of("day") must be idempotent even across the midnight gap.
assert new == new.start_of("day")