diff --git a/src/py_avro_schema/_schemas.py b/src/py_avro_schema/_schemas.py index 9f59742..ad21090 100644 --- a/src/py_avro_schema/_schemas.py +++ b/src/py_avro_schema/_schemas.py @@ -255,6 +255,15 @@ def _schema_obj(py_type: Type, namespace: Optional[str] = None, options: Option _UUID_PATTERN = re.compile(r"^[0-9a-f]{8}(?:-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})?$", re.IGNORECASE) +def is_valid_datetime(string: str) -> bool: + """Checks is a given string is a valid datetime timestamp""" + try: + datetime.datetime.fromisoformat(string) + return True + except ValueError: + return False + + def validate_name(value: str) -> str: """Validate (and return) whether a given string is a valid Avro name""" if not re.match(_AVRO_NAME_PATTERN, value): @@ -1186,7 +1195,7 @@ def data(self, names: NamesType) -> JSONObj: if ( Option.DETERMINISTIC_DEFAULTS in self.options and isinstance(default_value, str) - and _UUID_PATTERN.match(default_value) + and (_UUID_PATTERN.match(default_value) or is_valid_datetime(default_value)) ): default_value = "" field_data["default"] = default_value diff --git a/tests/test_dataclass.py b/tests/test_dataclass.py index 701776c..de3da3b 100644 --- a/tests/test_dataclass.py +++ b/tests/test_dataclass.py @@ -941,3 +941,28 @@ class PyType: ], } assert_schema(PyType, expected, options=pas.Option.DETERMINISTIC_DEFAULTS) + + +def test_deterministic_defaults_timestamp(): + + def timestamp_millis() -> str: + microsecond_time = datetime.datetime.now(tz=datetime.UTC) + microsecond_time = microsecond_time.strftime("%Y-%m-%dT%H:%M:%S.%fZ") + return microsecond_time[:-4] + microsecond_time[-1] + + @dataclasses.dataclass + class PyType: + time: str = dataclasses.field(default_factory=lambda: timestamp_millis()) + + expected = { + "type": "record", + "name": "PyType", + "fields": [ + { + "name": "time", + "type": "string", + "default": "", + }, + ], + } + assert_schema(PyType, expected, options=pas.Option.DETERMINISTIC_DEFAULTS)