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

Added JSON converter for TimeSpan #51492

Closed
wants to merge 5 commits into from

Conversation

YohDeadfall
Copy link
Contributor

Fixes #29932.

@dotnet-issue-labeler
Copy link

Note regarding the new-api-needs-documentation label:

This serves as a reminder for when your PR is modifying a ref *.cs file and adding/modifying public APIs, to please make sure the API implementation in the src *.cs file is documented with triple slash comments, so the PR reviewers can sign off that change.

@ghost
Copy link

ghost commented Apr 19, 2021

Tagging subscribers to this area: @eiriktsarpalis, @layomia
See info in area-owners.md if you want to be subscribed.

Issue Details

Fixes #29932.

Author: YohDeadfall
Assignees: -
Labels:

area-System.Text.Json, new-api-needs-documentation

Milestone: -

Comment on lines +10 to +11
private const ulong TicksPerYear = TimeSpan.TicksPerDay * 365;
private const ulong TicksPerMonth = TimeSpan.TicksPerDay * 30;
Copy link
Member

Choose a reason for hiding this comment

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

I presume these values are convention specified by the duration spec? I couldn't find anything definitive on the matter.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Copy link
Contributor

Choose a reason for hiding this comment

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

I don't think this makes sense. A duration of 48 hours is not the same as a duration of 2 days per ISO 8601. Example: adding 48 hours to 2021-11-06 07:00 in North Carolina is not the same as adding 2 days to the same date. Likewise, I would not expect adding 30 days to 2020-02-01 being the same as adding 1 month to the same date.

Copy link
Member

Choose a reason for hiding this comment

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

https://github.com/dotnet/corefx/blob/6d723b8e5ae3129c0a94252292322fc19673478f/src/System.Private.Xml/src/System/Xml/Schema/XsdDuration.cs#L229-L236

Just because we're doing it already doesn't mean it is devoid of flaws 😄. Is this backed by the specification?

Copy link
Contributor Author

@YohDeadfall YohDeadfall Apr 20, 2021

Choose a reason for hiding this comment

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

Have no original documents, so can't answer your question, but to correctly handle such things the month and year parts should be avoided, but they still can be received and it will be a valid input. The problem is that it's not compatible with .NET date and time representations.

For example, PostgreSQL has interval which stores a duration in three parts: months, days, microseconds. And to handle arithmetic operations properly it deconstructs a timestamp into parts, adds an interval to it and then assembles a new timestamp again. timestamp is a 64 bit integer measuring microseconds.

Unfortunately, TimeSpan stores ticks and can't be correctly constructed from any input. For that reason it lacks of Years and Months properties.

Therefore, while I agree with @watfordgnf that it's a wrong logic and serialization should not include years and months, deserialization of TimeSpan will cause issues if these parts are present. Surely, I can just throw a NotSupportedException for such cases and it will solve the problem.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

NodaTime provides a pattern for JSON, but have no idea is it a standard or at least wide spread practice:

https://github.com/nodatime/nodatime/blob/af257385d785f597fc8be67c84f2cf714fbe4203/src/NodaTime/Text/DurationPattern.cs

Copy link
Contributor

Choose a reason for hiding this comment

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

I believe it would be the least confusing for users if TimeSpan was serialized using its native .NET format, rather than a standard it doesn't meet (it represents a subset of it at best). Perhaps not the most convenient when interoperating with other services, but if they expect ISO 8601 durations you couldn't use them reliably anyway (bidirectionally).

Copy link
Member

Choose a reason for hiding this comment

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

After conversation with @layomia we have concluded that ISO 8601 durations is not the appropriate format for encoding TimeSpan. As @watfordgnf points out TimeSpan represents a subset of the standard so deserialization cannot be implemented without substantial sacrifices in precision.

The converter should instead use the standard .NET format for timespan. This has the benefit of being compatible with json.net (which can be an issue with, say, with document databases like cosmos currently using json.net).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

How should tests look like then? Do the same thing as for DateTime?

Copy link
Member

Choose a reason for hiding this comment

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

Yeah I think DateTime tests might be a good starting point. Given that the parsing will be pushed out of the json layer the coverage doesn't need to be as extensive.

static int WriteDigits(Span<byte> span, ulong value)
{
int digits = value switch
{
Copy link
Member

Choose a reason for hiding this comment

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

the code for this seems currently less efficient: #52730

@eiriktsarpalis
Copy link
Member

Hi @YohDeadfall, what is the progress of this PR? As discussed we would be looking to not format TimeSpan using ISO8601 durations but use the default .NET format instead.

@YohDeadfall
Copy link
Contributor Author

Hi, I feel burned out, so no progress since the last discussion. I may finish it next week when will have some time off.

@eiriktsarpalis
Copy link
Member

That's fine, no pressure. I'll see if I can find some time later to work on your changes.

@layomia layomia self-assigned this Jun 1, 2021
@YohDeadfall YohDeadfall closed this Jun 6, 2021
@YohDeadfall YohDeadfall deleted the json-timespan branch June 6, 2021 14:11
@ghost ghost locked as resolved and limited conversation to collaborators Jul 6, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

JsonSerializer support for TimeSpan in 3.0?
5 participants