fix: accept datetime ranges in parse_date_range
With period.dynamic: true and period.precision: datetime, running without --date crashed: compute_next_period() returns a datetime range (YYYY-MM-DDTHH:MM:SS), which parse_date_range() rejected with a fullmatch against YYYY-MM-DD only. parse_date_range now accepts YYYY-MM-DDTHH:MM:SS--YYYY-MM-DDTHH:MM:SS in addition to plain date ranges, returning datetime strings unchanged (pass-through, matching compute_next_period output). Mixed-precision ranges, invalid times and reversed ranges are still rejected with the existing error messages; date-range behavior is unchanged. Closes #59
This commit is contained in:
@@ -25,20 +25,27 @@ def parse_date_range(date_arg: str) -> tuple[str, str]:
|
||||
|
||||
from_date, to_date = parts[0].strip(), parts[1].strip()
|
||||
date_pattern = r"\d{4}-\d{2}-\d{2}"
|
||||
if not re.fullmatch(date_pattern, from_date) or not re.fullmatch(
|
||||
date_pattern, to_date
|
||||
datetime_pattern = r"\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}"
|
||||
if re.fullmatch(date_pattern, from_date) and re.fullmatch(date_pattern, to_date):
|
||||
fmt = "%Y-%m-%d"
|
||||
elif re.fullmatch(datetime_pattern, from_date) and re.fullmatch(
|
||||
datetime_pattern, to_date
|
||||
):
|
||||
fmt = "%Y-%m-%dT%H:%M:%S"
|
||||
else:
|
||||
raise ValueError("Date range must be in format YYYY-MM-DD--YYYY-MM-DD")
|
||||
|
||||
try:
|
||||
start = datetime.strptime(from_date, "%Y-%m-%d").date()
|
||||
end = datetime.strptime(to_date, "%Y-%m-%d").date()
|
||||
start = datetime.strptime(from_date, fmt)
|
||||
end = datetime.strptime(to_date, fmt)
|
||||
except ValueError as e:
|
||||
raise ValueError("Date range contains invalid calendar date") from e
|
||||
|
||||
if start > end:
|
||||
raise ValueError("Date range start must be less than or equal to end")
|
||||
|
||||
if fmt == "%Y-%m-%d":
|
||||
return start.date().isoformat(), end.date().isoformat()
|
||||
return start.isoformat(), end.isoformat()
|
||||
|
||||
|
||||
|
||||
@@ -30,6 +30,10 @@ def _reset_config_overrides():
|
||||
[
|
||||
("2026-01-01--2026-01-31", ("2026-01-01", "2026-01-31")),
|
||||
(" 2026-01-01 -- 2026-01-31 ", ("2026-01-01", "2026-01-31")),
|
||||
(
|
||||
"2026-06-01T00:00:00--2026-06-30T23:59:59",
|
||||
("2026-06-01T00:00:00", "2026-06-30T23:59:59"),
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_parse_date_range_valid(date_arg, expected):
|
||||
@@ -43,6 +47,9 @@ def test_parse_date_range_valid(date_arg, expected):
|
||||
"2026-1-01--2026-01-31",
|
||||
"2026-02-30--2026-03-01",
|
||||
"2026-02-01--2026-01-31",
|
||||
"2026-06-01T25:00:00--2026-06-30T23:59:59",
|
||||
"2026-06-01--2026-06-30T23:59:59",
|
||||
"2026-06-30T23:59:59--2026-06-01T00:00:00",
|
||||
],
|
||||
)
|
||||
def test_parse_date_range_invalid(date_arg):
|
||||
@@ -1372,11 +1379,6 @@ class TestReportNoTimeIntegration:
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
@pytest.mark.xfail(
|
||||
strict=True,
|
||||
reason="bug #59: parse_date_range rejects datetime range produced by "
|
||||
"dynamic period with precision=datetime when --date is omitted",
|
||||
)
|
||||
@mock.patch.dict(os.environ, VALID_ENV, clear=True)
|
||||
@mock.patch("redmine_reporter.cli.fetch_issues_with_spent_time")
|
||||
def test_cli_dynamic_datetime_period_without_date(mock_fetch, tmp_path):
|
||||
|
||||
Reference in New Issue
Block a user