diff --git a/sdk/core/azure-core/CHANGELOG.md b/sdk/core/azure-core/CHANGELOG.md index 811230155bbb..97e8df98391e 100644 --- a/sdk/core/azure-core/CHANGELOG.md +++ b/sdk/core/azure-core/CHANGELOG.md @@ -4,6 +4,7 @@ ### Features Added +- `azure.core.serialization.AzureJSONEncoder` (introduced in 1.17.0) serializes `datetime.datetime` objects in ISO 8601 format, conforming to RFC 3339's specification. #20190 - We now use `azure.core.serialization.AzureJSONEncoder` to serialize `json` input to `azure.core.rest.HttpRequest`. ### Breaking Changes in the Provisional `azure.core.rest` package diff --git a/sdk/core/azure-core/azure/core/serialization.py b/sdk/core/azure-core/azure/core/serialization.py index a7022aad131b..ef388f14cf26 100644 --- a/sdk/core/azure-core/azure/core/serialization.py +++ b/sdk/core/azure-core/azure/core/serialization.py @@ -105,8 +105,11 @@ def default(self, o): # pylint: disable=too-many-return-statements if hasattr(o, "year") and hasattr(o, "hour"): # astimezone() fails for naive times in Python 2.7, so make make sure o is aware (tzinfo is set) if not o.tzinfo: - return o.replace(tzinfo=TZ_UTC).isoformat() - return o.astimezone(TZ_UTC).isoformat() + iso_formatted = o.replace(tzinfo=TZ_UTC).isoformat() + else: + iso_formatted = o.astimezone(TZ_UTC).isoformat() + # Replace the trailing "+00:00" UTC offset with "Z" (RFC 3339: https://www.ietf.org/rfc/rfc3339.txt) + return iso_formatted.replace("+00:00", "Z") # Next try datetime.date or datetime.time return o.isoformat() except AttributeError: diff --git a/sdk/core/azure-core/tests/test_serialization.py b/sdk/core/azure-core/tests/test_serialization.py index e7a4980cdc9f..41f2914cf343 100644 --- a/sdk/core/azure-core/tests/test_serialization.py +++ b/sdk/core/azure-core/tests/test_serialization.py @@ -150,7 +150,7 @@ def test_dictionary_datetime(json_dumps_with_encoder): expected = { "timedelta": "P1DT00H00M00S", "date": "2021-05-12", - "datetime": '2012-02-24T00:53:52.780000+00:00', + "datetime": '2012-02-24T00:53:52.780000Z', 'time': '11:12:13', } assert json.loads(json_dumps_with_encoder(test_obj)) == expected @@ -167,7 +167,7 @@ def __init__(self): expected_dict = { "timedelta": "P1DT00H00M00S", "date": "2021-05-12", - "datetime": '2012-02-24T00:53:52.780000+00:00', + "datetime": '2012-02-24T00:53:52.780000Z', 'time': '11:12:13', } assert json.loads(json_dumps_with_encoder(expected.to_dict())) == expected_dict @@ -205,10 +205,10 @@ def __init__(self): expected_dict = { "_attributes": { "enabled": True, - "not_before": "2012-02-24T00:53:52.780000+00:00", - "expires": "2032-02-24T00:53:52.780000+00:00", - "created": "2020-02-24T00:53:52.780000+00:00", - "updated": "2021-02-24T00:53:52.780000+00:00", + "not_before": "2012-02-24T00:53:52.780000Z", + "expires": "2032-02-24T00:53:52.780000Z", + "created": "2020-02-24T00:53:52.780000Z", + "updated": "2021-02-24T00:53:52.780000Z", }, "_id": "id", "_vault_id": { @@ -229,39 +229,39 @@ def test_serialize_datetime(json_dumps_with_encoder): date_obj = datetime.strptime('2015-01-01T00:00:00', "%Y-%m-%dT%H:%M:%S") date_str = json_dumps_with_encoder(date_obj) - assert date_str == '"2015-01-01T00:00:00+00:00"' + assert date_str == '"2015-01-01T00:00:00Z"' date_obj = datetime.strptime('1999-12-31T23:59:59', "%Y-%m-%dT%H:%M:%S").replace(tzinfo=NegativeUtcOffset()) date_str = json_dumps_with_encoder(date_obj) - assert date_str == '"2000-01-01T11:59:59+00:00"' + assert date_str == '"2000-01-01T11:59:59Z"' date_obj = datetime.strptime("2015-06-01T16:10:08.0121", "%Y-%m-%dT%H:%M:%S.%f").replace(tzinfo=PositiveUtcOffset()) date_str = json_dumps_with_encoder(date_obj) - assert date_str == '"2015-06-01T04:10:08.012100+00:00"' + assert date_str == '"2015-06-01T04:10:08.012100Z"' date_obj = datetime.min date_str = json_dumps_with_encoder(date_obj) - assert date_str == '"0001-01-01T00:00:00+00:00"' + assert date_str == '"0001-01-01T00:00:00Z"' date_obj = datetime.max date_str = json_dumps_with_encoder(date_obj) - assert date_str == '"9999-12-31T23:59:59.999999+00:00"' + assert date_str == '"9999-12-31T23:59:59.999999Z"' date_obj = datetime.strptime('2012-02-24T00:53:52.000001Z', "%Y-%m-%dT%H:%M:%S.%fZ") date_str = json_dumps_with_encoder(date_obj) - assert date_str == '"2012-02-24T00:53:52.000001+00:00"' + assert date_str == '"2012-02-24T00:53:52.000001Z"' date_obj = datetime.strptime('2012-02-24T00:53:52.780Z', "%Y-%m-%dT%H:%M:%S.%fZ") date_str = json_dumps_with_encoder(date_obj) - assert date_str == '"2012-02-24T00:53:52.780000+00:00"' + assert date_str == '"2012-02-24T00:53:52.780000Z"' def test_serialize_datetime_subclass(json_dumps_with_encoder): date_obj = DatetimeSubclass.strptime('2012-02-24T00:53:52.780Z', "%Y-%m-%dT%H:%M:%S.%fZ") date_str = json_dumps_with_encoder(date_obj) - assert date_str == '"2012-02-24T00:53:52.780000+00:00"' + assert date_str == '"2012-02-24T00:53:52.780000Z"' def test_serialize_time(json_dumps_with_encoder):