Repository: ermakov-oleg/serpyco-rs (language: python)

Issue:
Can't parse datetime from string with high millisecond precision
Hi, I'm getting an error when parsing dates with high millisecond precision.

**Example**

```python
from datetime import datetime

from serpyco_rs import Serializer

serializer = Serializer(datetime)
obj = serializer.load('2024-04-02T12:21:53.725421224')
print(obj)
```

**Expected**
print datetime object

**Actual**
```
serpyco_rs.SchemaValidationError: Schema validation failed:
- "2024-04-02T12:21:53.725421224" is not of type "datetime" (instance_path='')
```

**Info**:
serpyco-rs version: 1.6.0
python: 3.12.2
os: macos 14.3.1 (m1)


**Comment**
string "2024-04-02T12:21:53.725421224" get from Java springframework which use LocalDateTime.now()


Discussion / hints:

Fix patch:
diff --git a/src/python/dateutil.rs b/src/python/dateutil.rs
index 07ef0cc..3be6859 100644
--- a/src/python/dateutil.rs
+++ b/src/python/dateutil.rs
@@ -5,16 +5,24 @@ use pyo3::types::{
 };
 use pyo3::{Bound, PyErr, PyResult, Python};
 use pyo3_ffi::PyTimeZone_FromOffset;
-use speedate::{Date, DateTime, ParseError, Time};
+use speedate::{
+    Date, DateTime, MicrosecondsPrecisionOverflowBehavior, ParseError, Time, TimeConfig,
+};
 
 use crate::errors::{ToPyErr, ValidationError};
 
+const TIME_CONFIG: TimeConfig = TimeConfig {
+    microseconds_precision_overflow_behavior: MicrosecondsPrecisionOverflowBehavior::Truncate,
+    unix_timestamp_offset: None,
+};
+
 #[inline]
 pub(crate) fn parse_datetime<'py>(
     py: Python<'py>,
     value: &str,
 ) -> PyResult<Bound<'py, PyDateTime>> {
-    let datetime = DateTime::parse_str(value).map_err(InnerParseError::from)?;
+    let datetime = DateTime::parse_bytes_rfc3339_with_config(value.as_ref(), &TIME_CONFIG)
+        .map_err(InnerParseError::from)?;
     PyDateTime::new_bound(
         py,
         datetime.date.year.into(),
@@ -30,7 +38,8 @@ pub(crate) fn parse_datetime<'py>(
 
 #[inline]
 pub(crate) fn parse_time<'py>(py: Python<'py>, value: &str) -> PyResult<Bound<'py, PyTime>> {
-    let time = Time::parse_str(value).map_err(InnerParseError::from)?;
+    let time = Time::parse_bytes_with_config(value.as_ref(), &TIME_CONFIG)
+        .map_err(InnerParseError::from)?;
     PyTime::new_bound(
         py,
         time.hour,


Test patch:
diff --git a/tests/test_encoders.py b/tests/test_encoders.py
index 563ab07..bdee0f7 100644
--- a/tests/test_encoders.py
+++ b/tests/test_encoders.py
@@ -149,6 +149,10 @@ def test_datetime_dump__naive_datetime_to_utc__expect_utc():
             '2022-10-10T14:23:43+02:00',
             datetime(2022, 10, 10, 14, 23, 43, tzinfo=ZoneInfo('Europe/Berlin')),
         ),
+        (
+            '2024-04-02T12:21:53.725421224',
+            datetime(2024, 4, 2, 12, 21, 53, 725421),
+        ),
     ),
 )
 def test_datetime_load(value, expected):
@@ -162,6 +166,7 @@ def test_datetime_load(value, expected):
         ('12:34', time(12, 34)),
         ('12:34:56', time(12, 34, 56)),
         ('12:34:56.000078', time(12, 34, 56, 78)),
+        ('12:34:57.000095987', time(12, 34, 57, 95)),
         (
             '12:34:56.000078+03:00',
             time(12, 34, 56, 78, tzinfo=tzoffset(None, timedelta(hours=3))),