Repository: marshmallow-code/marshmallow

Issue:
ISO8601 DateTimes ending with Z considered not valid in 2.19.4
Probably related to #1247 and #1234 - in marshmallow `2.19.4`, with `python-dateutil` _not_ installed, it seems that loading a datetime in ISO8601 that ends in `Z` (UTC time) results in an error:

```python
class Foo(Schema):
    date = DateTime(required=True)


foo_schema = Foo(strict=True)

a_date_with_z = '2019-06-17T00:57:41.000Z'
foo_schema.load({'date': a_date_with_z})
```

```
marshmallow.exceptions.ValidationError: {'date': ['Not a valid datetime.']}
```

Digging a bit deeper, it seems [`from_iso_datetime`](https://github.com/marshmallow-code/marshmallow/blob/dev/src/marshmallow/utils.py#L213-L215) is failing with a `unconverted data remains: Z` - my understanding of the spec is rather limited, but it seems that they are indeed valid ISO8601 dates (and in `marshmallow==2.19.3` and earlier, the previous snippet seems to work without raising validation errors).


Discussion:

Patch:
diff --git a/src/marshmallow/utils.py b/src/marshmallow/utils.py
index 113906d6..87bba69c 100755
--- a/src/marshmallow/utils.py
+++ b/src/marshmallow/utils.py
@@ -285,6 +285,9 @@ def from_iso(datestring, use_dateutil=True):
         # Strip off timezone info.
         if '.' in datestring:
             # datestring contains microseconds
+            (dt_nomstz, mstz) = datestring.split('.')
+            ms_notz = mstz[:len(mstz) - len(mstz.lstrip('0123456789'))]
+            datestring = '.'.join((dt_nomstz, ms_notz))
             return datetime.datetime.strptime(datestring[:26], '%Y-%m-%dT%H:%M:%S.%f')
         return datetime.datetime.strptime(datestring[:19], '%Y-%m-%dT%H:%M:%S')
 


Test patch:
diff --git a/tests/test_utils.py b/tests/test_utils.py
index a5d68621..dff83171 100644
--- a/tests/test_utils.py
+++ b/tests/test_utils.py
@@ -200,6 +200,15 @@ def test_from_iso_datetime(use_dateutil, timezone):
     assert type(result) == dt.datetime
     assert_datetime_equal(result, d)
 
+    # Test with 3-digit only microseconds
+    # Regression test for https://github.com/marshmallow-code/marshmallow/issues/1251
+    d = dt.datetime.now(tz=timezone).replace(microsecond=123000)
+    formatted = d.isoformat()
+    formatted = formatted[:23] + formatted[26:]
+    result = utils.from_iso(formatted, use_dateutil=use_dateutil)
+    assert type(result) == dt.datetime
+    assert_datetime_equal(result, d)
+
 def test_from_iso_with_tz():
     d = central.localize(dt.datetime.now())
     formatted = d.isoformat()