diff --git a/.github/workflows/continuous_integration.yml b/.github/workflows/continuous_integration.yml index 3b9c38048..6255180bd 100644 --- a/.github/workflows/continuous_integration.yml +++ b/.github/workflows/continuous_integration.yml @@ -17,6 +17,10 @@ jobs: matrix: python-version: ["pypy-3.11", "3.8", "3.9", "3.10", "3.11", "3.12", "3.13", "3.14"] os: [ubuntu-latest, macos-latest, windows-latest] + exclude: + # PyPy Windows often lacks binary wheels (pyyaml/regex); source builds fail. + - os: windows-latest + python-version: pypy-3.11 include: - os: ubuntu-latest path: ~/.cache/pip diff --git a/arrow/parser.py b/arrow/parser.py index fc3774b09..b33dbb410 100644 --- a/arrow/parser.py +++ b/arrow/parser.py @@ -713,7 +713,8 @@ def _build_datetime(parts: _Parts) -> datetime: # day not given, default to 1 _day = 1 - date_string = f"{year}-{week}-{_day}" + # %G/%V need zero-padded year/week; int() alone drops leading zeros. + date_string = f"{year:04d}-{week:02d}-{_day}" # tokens for ISO 8601 weekdates dt = datetime.strptime(date_string, "%G-%V-%u") @@ -750,7 +751,7 @@ def _build_datetime(parts: _Parts) -> datetime: "Month component is not allowed with the DDD and DDDD tokens." ) - date_string = f"{_year}-{day_of_year}" + date_string = f"{_year:04d}-{int(day_of_year):03d}" try: dt = datetime.strptime(date_string, "%Y-%j") except ValueError: diff --git a/tests/test_parser.py b/tests/test_parser.py index 7038d880f..a08c8106c 100644 --- a/tests/test_parser.py +++ b/tests/test_parser.py @@ -542,6 +542,10 @@ def test_parse_YYYY_DDDD(self): assert self.parser.parse("1998-006", "YYYY-DDDD") == datetime(1998, 1, 6) + # Years before 1000 need zero-padded %Y for strptime + assert self.parser.parse("0001-001", "YYYY-DDDD") == datetime(1, 1, 1) + assert self.parser.parse("0999-365", "YYYY-DDDD") == datetime(999, 12, 31) + with pytest.raises(ParserError): self.parser.parse("1998-456", "YYYY-DDDD") @@ -736,6 +740,10 @@ def test_parse_W(self): assert self.parser.parse("2011W054T141701", "WTHHmmss") == datetime( 2011, 2, 3, 14, 17, 1 ) + # Years before 1000 need zero-padded %G for strptime + assert self.parser.parse("0001-W01-1", "W") == datetime(1, 1, 1) + assert self.parser.parse("0999-W01-1", "W") == datetime(998, 12, 31) + assert arrow.get("0001-W01-1") == arrow.Arrow(1, 1, 1, tzinfo=timezone.utc) bad_formats = [ "201W22",