From 2c6e563591a3689818daee6a08557d7f901239ea Mon Sep 17 00:00:00 2001 From: Paul Gerber Date: Mon, 6 Jul 2026 15:00:17 +0200 Subject: [PATCH] model.datatypes: Fix xs:gYear and xs:gYearMonth rejecting valid XSD year values Previously, `_parse_xsd_gyear` and `_parse_xsd_gyearmonth` in `datatypes.py` reject values with more than 4 digits or negative signs. Now, the regexes support an optional minus sign and 4+ digits, and the parsers correctly handle the sign from `match[1]`. Unit tests were added. Fixes #566 --- sdk/basyx/aas/model/datatypes.py | 14 ++++++++++---- sdk/test/model/test_datatypes.py | 8 ++++++++ 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/sdk/basyx/aas/model/datatypes.py b/sdk/basyx/aas/model/datatypes.py index e33b3e0b..2331637b 100644 --- a/sdk/basyx/aas/model/datatypes.py +++ b/sdk/basyx/aas/model/datatypes.py @@ -638,10 +638,10 @@ def _parse_xsd_bool(value: str) -> Boolean: raise ValueError("Invalid literal for XSD bool type") -GYEAR_RE = re.compile(r'^(\d\d\d\d)([+\-]\d\d:\d\d|Z)?$') +GYEAR_RE = re.compile(r'^(-?)(\d{4,})([+\-]\d\d:\d\d|Z)?$') GMONTH_RE = re.compile(r'^--(\d\d)([+\-]\d\d:\d\d|Z)?$') GDAY_RE = re.compile(r'^---(\d\d)([+\-]\d\d:\d\d|Z)?$') -GYEARMONTH_RE = re.compile(r'^(\d\d\d\d)-(\d\d)([+\-]\d\d:\d\d|Z)?$') +GYEARMONTH_RE = re.compile(r'^(-?)(\d{4,})-(\d\d)([+\-]\d\d:\d\d|Z)?$') GMONTHDAY_RE = re.compile(r'^--(\d\d)-(\d\d)([+\-]\d\d:\d\d|Z)?$') @@ -649,7 +649,10 @@ def _parse_xsd_gyear(value: str) -> GYear: match = GYEAR_RE.match(value) if not match: raise ValueError("Value is not a valid XSD GYear string") - return GYear(int(match[1]), _parse_xsd_date_tzinfo(match[2])) + year = int(match[2]) + if match[1]: + year = -year + return GYear(year, _parse_xsd_date_tzinfo(match[3])) def _parse_xsd_gmonth(value: str) -> GMonth: @@ -670,7 +673,10 @@ def _parse_xsd_gyearmonth(value: str) -> GYearMonth: match = GYEARMONTH_RE.match(value) if not match: raise ValueError("Value is not a valid XSD GYearMonth string") - return GYearMonth(int(match[1]), int(match[2]), _parse_xsd_date_tzinfo(match[3])) + year = int(match[2]) + if match[1]: + year = -year + return GYearMonth(year, int(match[3]), _parse_xsd_date_tzinfo(match[4])) def _parse_xsd_gmonthday(value: str) -> GMonthDay: diff --git a/sdk/test/model/test_datatypes.py b/sdk/test/model/test_datatypes.py index f314d302..3da6750b 100644 --- a/sdk/test/model/test_datatypes.py +++ b/sdk/test/model/test_datatypes.py @@ -156,10 +156,18 @@ def test_serialize_date(self) -> None: def test_parse_partial_dates(self) -> None: self.assertEqual(model.datatypes.GYear(2019), model.datatypes.from_xsd("2019", model.datatypes.GYear)) + self.assertEqual(model.datatypes.GYear(-2001), + model.datatypes.from_xsd("-2001", model.datatypes.GYear)) + self.assertEqual(model.datatypes.GYear(20000), + model.datatypes.from_xsd("20000", model.datatypes.GYear)) self.assertEqual(model.datatypes.GMonth(7), model.datatypes.from_xsd("--07", model.datatypes.GMonth)) self.assertEqual(model.datatypes.GYearMonth(2020, 5), model.datatypes.from_xsd("2020-05", model.datatypes.GYearMonth)) + self.assertEqual(model.datatypes.GYearMonth(-2001, 10), + model.datatypes.from_xsd("-2001-10", model.datatypes.GYearMonth)) + self.assertEqual(model.datatypes.GYearMonth(20000, 5), + model.datatypes.from_xsd("20000-05", model.datatypes.GYearMonth)) self.assertEqual(model.datatypes.GMonthDay(12, 6), model.datatypes.from_xsd("--12-06", model.datatypes.GMonthDay)) self.assertEqual(model.datatypes.GDay(23),