Skip to content
Merged
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
13 changes: 10 additions & 3 deletions src/borg/helpers/time.py
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ def calculate_relative_offset(format_string, from_ts, earlier=False):

match unit:
case "y":
return from_ts.replace(year=from_ts.year + offset)
return offset_n_months(from_ts, offset * 12)
case "m":
return offset_n_months(from_ts, offset)
case "w":
Expand Down Expand Up @@ -173,8 +173,15 @@ def get_month_and_year_from_total(total_completed_months):
following_month, year_of_following_month = get_month_and_year_from_total(total_months + 1)
max_days_in_month = (datetime(year_of_following_month, following_month, 1) - timedelta(1)).day

return datetime(day=min(from_ts.day, max_days_in_month), month=target_month, year=target_year).replace(
tzinfo=from_ts.tzinfo
return datetime(
day=min(from_ts.day, max_days_in_month),
month=target_month,
year=target_year,
hour=from_ts.hour,
minute=from_ts.minute,
second=from_ts.second,
microsecond=from_ts.microsecond,
tzinfo=from_ts.tzinfo,
)


Expand Down
17 changes: 16 additions & 1 deletion src/borg/testsuite/helpers/time_test.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import pytest
from datetime import datetime, timezone

from ...helpers.time import safe_ns, safe_s, SUPPORT_32BIT_PLATFORMS
from ...helpers.time import safe_ns, safe_s, SUPPORT_32BIT_PLATFORMS, calculate_relative_offset


def utcfromtimestamp(timestamp):
Expand Down Expand Up @@ -36,3 +36,18 @@ def test_safe_timestamps():
utcfromtimestamp(beyond_y10k)
assert utcfromtimestamp(safe_s(beyond_y10k)) > datetime(2262, 1, 1)
assert utcfromtimestamp(safe_ns(beyond_y10k) / 1000000000) > datetime(2262, 1, 1)


def test_calculate_relative_offset_year_from_leap_day():
# regression test for #9967: year offset from Feb 29 must not crash on non-leap target year.
leap_day = datetime(2024, 2, 29, tzinfo=timezone.utc)
assert calculate_relative_offset("1y", leap_day, earlier=False) == datetime(2025, 2, 28, tzinfo=timezone.utc)
assert calculate_relative_offset("1y", leap_day, earlier=True) == datetime(2023, 2, 28, tzinfo=timezone.utc)
# target year is also a leap year -> keep Feb 29.
assert calculate_relative_offset("4y", leap_day, earlier=False) == datetime(2028, 2, 29, tzinfo=timezone.utc)


def test_calculate_relative_offset_year_regular():
ts = datetime(2024, 6, 15, 12, 30, 45, tzinfo=timezone.utc)
assert calculate_relative_offset("2y", ts, earlier=False) == datetime(2026, 6, 15, 12, 30, 45, tzinfo=timezone.utc)
assert calculate_relative_offset("2y", ts, earlier=True) == datetime(2022, 6, 15, 12, 30, 45, tzinfo=timezone.utc)
Loading