From 68634d58fe5682f5124a5284b4fc5b4e8de99197 Mon Sep 17 00:00:00 2001 From: Ryan Kendra Date: Thu, 5 Dec 2024 14:12:28 -0500 Subject: [PATCH 1/3] Added test that exposes bug in span_range --- tests/test_arrow.py | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/tests/test_arrow.py b/tests/test_arrow.py index 5afe9baa..086f5cf2 100644 --- a/tests/test_arrow.py +++ b/tests/test_arrow.py @@ -1185,6 +1185,28 @@ def test_month(self): (arrow.Arrow(2013, 4, 1), arrow.Arrow(2013, 4, 30, 23, 59, 59, 999999)), ] + def test_month_end(self): + result = list( + arrow.Arrow.span_range( + "month", datetime(2013, 1, 31), datetime(2014, 1, 31), exact=True + ) + ) + + assert result == [ + (arrow.Arrow(2013, 1, 31), arrow.Arrow(2013, 2, 27, 23, 59, 59, 999999)), + (arrow.Arrow(2013, 2, 28), arrow.Arrow(2013, 3, 30, 23, 59, 59, 999999)), + (arrow.Arrow(2013, 3, 31), arrow.Arrow(2013, 4, 29, 23, 59, 59, 999999)), + (arrow.Arrow(2013, 4, 30), arrow.Arrow(2013, 5, 30, 23, 59, 59, 999999)), + (arrow.Arrow(2013, 5, 31), arrow.Arrow(2013, 6, 29, 23, 59, 59, 999999)), + (arrow.Arrow(2013, 6, 30), arrow.Arrow(2013, 7, 30, 23, 59, 59, 999999)), + (arrow.Arrow(2013, 7, 31), arrow.Arrow(2013, 8, 30, 23, 59, 59, 999999)), + (arrow.Arrow(2013, 8, 31), arrow.Arrow(2013, 9, 29, 23, 59, 59, 999999)), + (arrow.Arrow(2013, 9, 30), arrow.Arrow(2013, 10, 30, 23, 59, 59, 999999)), + (arrow.Arrow(2013, 10, 31), arrow.Arrow(2013, 11, 29, 23, 59, 59, 999999)), + (arrow.Arrow(2013, 11, 30), arrow.Arrow(2013, 12, 30, 23, 59, 59, 999999)), + (arrow.Arrow(2013, 12, 31), arrow.Arrow(2014, 1, 30, 23, 59, 59, 999999)), + ] + def test_week(self): result = list( arrow.Arrow.span_range("week", datetime(2013, 2, 2), datetime(2013, 2, 28)) From 30249505267d676767777f033f930184d291e282 Mon Sep 17 00:00:00 2001 From: Ryan Kendra Date: Thu, 5 Dec 2024 17:09:05 -0500 Subject: [PATCH 2/3] Fixed span_range omitting dates when frame is 'month', exact is True, and start.day is 31 (#1185) --- arrow/arrow.py | 10 ++++++++++ tests/test_arrow.py | 16 +++++++++++++++- 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/arrow/arrow.py b/arrow/arrow.py index 9d1f5e30..8fbc1e76 100644 --- a/arrow/arrow.py +++ b/arrow/arrow.py @@ -699,7 +699,17 @@ def span_range( yield r.span(frame, bounds=bounds, exact=exact) for r in _range: + day_is_clipped = False floor, ceil = r.span(frame, bounds=bounds, exact=exact) + next = ceil.shift(microseconds=+1) + if frame == "month" and next.day < start.day: + day_is_clipped = True + if day_is_clipped and not next._is_last_day_of_month(next): + days_to_shift = ( + min(start.day, calendar.monthrange(next.year, next.month)[1]) + - next.day + ) + ceil = ceil.shift(days=days_to_shift) if ceil > end: ceil = end if bounds[1] == ")": diff --git a/tests/test_arrow.py b/tests/test_arrow.py index 086f5cf2..caaf9e51 100644 --- a/tests/test_arrow.py +++ b/tests/test_arrow.py @@ -1185,7 +1185,7 @@ def test_month(self): (arrow.Arrow(2013, 4, 1), arrow.Arrow(2013, 4, 30, 23, 59, 59, 999999)), ] - def test_month_end(self): + def test_month_exact(self): result = list( arrow.Arrow.span_range( "month", datetime(2013, 1, 31), datetime(2014, 1, 31), exact=True @@ -1207,6 +1207,20 @@ def test_month_end(self): (arrow.Arrow(2013, 12, 31), arrow.Arrow(2014, 1, 30, 23, 59, 59, 999999)), ] + def test_month_exact_leap(self): + result = list( + arrow.Arrow.span_range( + "month", datetime(2012, 1, 31), datetime(2012, 5, 31), exact=True + ) + ) + + assert result == [ + (arrow.Arrow(2012, 1, 31), arrow.Arrow(2012, 2, 28, 23, 59, 59, 999999)), + (arrow.Arrow(2012, 2, 29), arrow.Arrow(2012, 3, 30, 23, 59, 59, 999999)), + (arrow.Arrow(2012, 3, 31), arrow.Arrow(2012, 4, 29, 23, 59, 59, 999999)), + (arrow.Arrow(2012, 4, 30), arrow.Arrow(2012, 5, 30, 23, 59, 59, 999999)), + ] + def test_week(self): result = list( arrow.Arrow.span_range("week", datetime(2013, 2, 2), datetime(2013, 2, 28)) From 0f434cb28e73979a6ee0db60e89c6470940b8e7f Mon Sep 17 00:00:00 2001 From: Ryan Kendra Date: Thu, 5 Dec 2024 17:15:47 -0500 Subject: [PATCH 3/3] Added documentation --- arrow/arrow.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/arrow/arrow.py b/arrow/arrow.py index 8fbc1e76..36e6a7c1 100644 --- a/arrow/arrow.py +++ b/arrow/arrow.py @@ -701,6 +701,8 @@ def span_range( for r in _range: day_is_clipped = False floor, ceil = r.span(frame, bounds=bounds, exact=exact) + + # check that no dates are lost (#1185) next = ceil.shift(microseconds=+1) if frame == "month" and next.day < start.day: day_is_clipped = True @@ -710,6 +712,7 @@ def span_range( - next.day ) ceil = ceil.shift(days=days_to_shift) + if ceil > end: ceil = end if bounds[1] == ")":