From 24041130368f4781a8ce6adcd8759165c96bded8 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 5 Aug 2020 09:23:11 +1000 Subject: [PATCH] Handle ISO8601 basic format offsets (#4910) (#4925) This commit handles ISO8601 basic format offsets of the form: - [+-]hh - [+-]hhmm in addition to the extended format offset form - [+-]hh:mm Fixes #4876 Co-authored-by: Russ Cam --- .../Utf8Json/Formatters/DateTimeFormatter.cs | 29 +++++-- tests/Tests.Reproduce/GitHubIssue4876.cs | 86 +++++++++++++++++++ 2 files changed, 109 insertions(+), 6 deletions(-) create mode 100644 tests/Tests.Reproduce/GitHubIssue4876.cs diff --git a/src/Elasticsearch.Net/Utf8Json/Formatters/DateTimeFormatter.cs b/src/Elasticsearch.Net/Utf8Json/Formatters/DateTimeFormatter.cs index 9e9ddb9657d..94a0d44da84 100644 --- a/src/Elasticsearch.Net/Utf8Json/Formatters/DateTimeFormatter.cs +++ b/src/Elasticsearch.Net/Utf8Json/Formatters/DateTimeFormatter.cs @@ -359,14 +359,22 @@ public DateTime Deserialize(ref JsonReader reader, IJsonFormatterResolver format } else if (i < to && array[i] == '-' || array[i] == '+') { - if (!(i + 5 < to)) goto ERROR; + if (len != 30 && len != 32 && len != 33) goto ERROR; kind = DateTimeKind.Local; var minus = array[i++] == '-'; var h = (array[i++] - (byte)'0') * 10 + (array[i++] - (byte)'0'); - i++; - var m = (array[i++] - (byte)'0') * 10 + (array[i++] - (byte)'0'); + var m = 0; + if (i < to) + { + if (len == 33) + { + if (array[i] != ':') goto ERROR; + i++; + } + m = (array[i++] - (byte)'0') * 10 + (array[i++] - (byte)'0'); + } var offset = new TimeSpan(h, m, 0); if (minus) offset = offset.Negate(); @@ -705,13 +713,22 @@ public DateTimeOffset Deserialize(ref JsonReader reader, IJsonFormatterResolver if (i < to && array[i] == '-' || array[i] == '+') { - if (!(i + 5 < to)) goto ERROR; + if (len != 30 && len != 32 && len != 33) goto ERROR; var minus = array[i++] == '-'; var h = (array[i++] - (byte)'0') * 10 + (array[i++] - (byte)'0'); - i++; - var m = (array[i++] - (byte)'0') * 10 + (array[i++] - (byte)'0'); + var m = 0; + if (i < to) + { + if (len == 33) + { + if (array[i] != ':') goto ERROR; + i++; + } + + m = (array[i++] - (byte)'0') * 10 + (array[i++] - (byte)'0'); + } var offset = new TimeSpan(h, m, 0); if (minus) offset = offset.Negate(); diff --git a/tests/Tests.Reproduce/GitHubIssue4876.cs b/tests/Tests.Reproduce/GitHubIssue4876.cs new file mode 100644 index 00000000000..c39980e6d25 --- /dev/null +++ b/tests/Tests.Reproduce/GitHubIssue4876.cs @@ -0,0 +1,86 @@ +// Licensed to Elasticsearch B.V under one or more agreements. +// Elasticsearch B.V licenses this file to you under the Apache 2.0 License. +// See the LICENSE file in the project root for more information + +using System; +using System.IO; +using System.Text; +using Elastic.Elasticsearch.Xunit.XunitPlumbing; +using FluentAssertions; +using Nest; + +namespace Tests.Reproduce +{ + public class GitHubIssue4876 + { + private readonly ElasticClient _client; + + public GitHubIssue4876() => _client = new ElasticClient(); + + [U] + public void CanDeserializeExtendedFormatOffsetIso8601DateTime() => + AssertDateTime("{\"timestamp\":\"2020-07-31T12:29:29.4425068+10:00\"}"); + + [U] + public void CanDeserializeBasicFormatOffsetWithMinutesIso8601DateTime() => + AssertDateTime("{\"timestamp\":\"2020-07-31T12:29:29.4425068+1000\"}"); + + [U] + public void CanDeserializeBasicFormatOffsetIso8601DateTime() => + AssertDateTime("{\"timestamp\":\"2020-07-31T12:29:29.4425068+10\"}"); + + [U] + public void ThrowExceptionWhenInvalidBasicFormatOffset() + { + Action action = () => AssertDateTime("{\"timestamp\":\"2020-07-31T12:29:29.4425068+100\"}"); + action.Should().Throw(); + } + + [U] + public void ThrowExceptionWhenInvalidBasicFormatOffset2() + { + Action action = () => AssertDateTime("{\"timestamp\":\"2020-07-31T12:29:29.4425068-10000\"}"); + action.Should().Throw(); + } + + [U] + public void CanDeserializeExtendedFormatOffsetIso8601DateTimeOffset() => + AssertDateTimeOffset("{\"timestamp\":\"2020-07-31T12:29:29.4425068+10:00\"}"); + + [U] + public void CanDeserializeBasicFormatOffsetWithMinutesIso8601DateTimeOffset() => + AssertDateTimeOffset("{\"timestamp\":\"2020-07-31T12:29:29.4425068+1000\"}"); + + [U] + public void CanDeserializeBasicFormatOffsetIso8601DateTimeOffset() => + AssertDateTimeOffset("{\"timestamp\":\"2020-07-31T12:29:29.4425068+10\"}"); + + private void AssertDateTime(string json) + { + var stream = new MemoryStream(Encoding.UTF8.GetBytes(json)); + var document = _client.SourceSerializer.Deserialize(stream); + + document.Timestamp.Should().Be( + new DateTime(637317593694425068, DateTimeKind.Utc).ToLocalTime()); + } + + private void AssertDateTimeOffset(string json) + { + var stream = new MemoryStream(Encoding.UTF8.GetBytes(json)); + var document = _client.SourceSerializer.Deserialize(stream); + + document.Timestamp.Should().Be( + new DateTimeOffset(637317593694425068, TimeSpan.Zero)); + } + + public class Document + { + public DateTime Timestamp { get; set; } + } + + public class Document2 + { + public DateTimeOffset Timestamp { get; set; } + } + } +}