Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Core] Follow RFC 3339 datetime formatting for AzureJSONEncoder #20346

Merged
merged 3 commits into from
Aug 31, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions sdk/core/azure-core/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
7 changes: 5 additions & 2 deletions sdk/core/azure-core/azure/core/serialization.py
Original file line number Diff line number Diff line change
Expand Up @@ -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()
mccoyp marked this conversation as resolved.
Show resolved Hide resolved
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")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is I think tangential to this specific PR, but I was looking into the msrest rfc serialization and we don't currently support that. Is this something I want to add?

msrest serialize rfc output:

serialize_rfc(isodate.parse_datetime("0001-01-01T00:00:00Z")) == 'Mon, 01 Jan 0001 00:00:00 GMT'

azure.core.serialization

json.dumps(isodate.parse_datetime("0001-01-01T00:00:00Z"), cls=AzureJSONEncoder) == "0001-01-01T00:00:00Z"

cc @lmazuel

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just to clear, your question has indeed mothing to do with @mccoyp 's PR :p . But yes, we could think about having a RFC HTTP header format serializer (not same RFC than here)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mccoyp i'm totally wrong here please ignore me

# Next try datetime.date or datetime.time
return o.isoformat()
except AttributeError:
Expand Down
28 changes: 14 additions & 14 deletions sdk/core/azure-core/tests/test_serialization.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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": {
Expand All @@ -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):

Expand Down