Skip to content

Commit f816b46

Browse files
committed
IsoDateTimeConverterTests (#2796)
forwarded port from 2.x (cherry picked from commit 2b8f7eb)
1 parent 3be2f67 commit f816b46

File tree

1 file changed

+288
-0
lines changed

1 file changed

+288
-0
lines changed
Lines changed: 288 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,288 @@
1+
using System;
2+
using System.IO;
3+
using System.Text;
4+
using Elasticsearch.Net;
5+
using FluentAssertions;
6+
using Nest;
7+
using Newtonsoft.Json;
8+
using Newtonsoft.Json.Linq;
9+
using Tests.Framework;
10+
11+
namespace Tests.ClientConcepts.Serializer
12+
{
13+
/// <summary>
14+
/// Tests for default DateTime zone serialization within NEST
15+
/// </summary>
16+
public class IsoDateTimeConverterHandlingTests
17+
{
18+
private readonly Flight _flight;
19+
private readonly string _offset;
20+
private readonly TimeSpan _timeSpanOffset;
21+
22+
public IsoDateTimeConverterHandlingTests()
23+
{
24+
var departureDateLocal = new DateTime(2013, 1, 21, 0, 0, 0, DateTimeKind.Local);
25+
_timeSpanOffset = TimeZoneInfo.Local.GetUtcOffset(departureDateLocal);
26+
27+
_flight = new Flight
28+
{
29+
DepartureDate = new DateTime(2013, 1, 21, 0, 0, 0, DateTimeKind.Unspecified),
30+
DepartureDateUtc = new DateTime(2013, 1, 21, 0, 0, 0, DateTimeKind.Utc),
31+
DepartureDateLocal = departureDateLocal,
32+
DepartureDateOffset = new DateTimeOffset(2013, 1, 21, 0, 0, 0, _timeSpanOffset),
33+
DepartureDateOffsetZero = new DateTimeOffset(2013, 1, 21, 0, 0, 0, TimeSpan.Zero),
34+
DepartureDateOffsetNonLocal = new DateTimeOffset(2013, 1, 21, 0, 0, 0, TimeSpan.FromHours(-6.25)),
35+
};
36+
37+
_offset = $"{_timeSpanOffset.Hours.ToString("+00;-00;")}:{_timeSpanOffset.Minutes.ToString("00")}";
38+
}
39+
40+
/// <remarks>
41+
/// Timezone offset serialized is based on DateTimeKind
42+
/// Unspecified = None
43+
/// Utc = UTC Timezone identifier
44+
/// Local = Local Timezone offset
45+
/// Offset = Timezone offset specified
46+
/// </remarks>
47+
[U]
48+
public void RoundTripKind()
49+
{
50+
var dateTimeZoneHandling = DateTimeZoneHandling.RoundtripKind;
51+
52+
var jsonWithRoundtripTimeZone = this.SerializeUsing(dateTimeZoneHandling);
53+
var expected = @" {
54+
""DepartureDate"": ""2013-01-21T00:00:00"",
55+
""DepartureDateUtc"": ""2013-01-21T00:00:00Z"",
56+
""DepartureDateLocal"": ""2013-01-21T00:00:00" + _offset + @""",
57+
""DepartureDateOffset"": ""2013-01-21T00:00:00" + _offset + @""",
58+
""DepartureDateOffsetZero"": ""2013-01-21T00:00:00+00:00"",
59+
""DepartureDateOffsetNonLocal"": ""2013-01-21T00:00:00-06:15""
60+
}";
61+
62+
jsonWithRoundtripTimeZone.JsonEquals(expected).Should().BeTrue("{0}", jsonWithRoundtripTimeZone);
63+
64+
var flight = this.DeserializeUsing(jsonWithRoundtripTimeZone, dateTimeZoneHandling);
65+
66+
flight.Should().Be(_flight);
67+
flight.DepartureDate.Kind.Should().Be(_flight.DepartureDate.Kind);
68+
flight.DepartureDateLocal.Kind.Should().Be(_flight.DepartureDateLocal.Kind);
69+
flight.DepartureDateUtc.Kind.Should().Be(_flight.DepartureDateUtc.Kind);
70+
flight.DepartureDateOffset.Offset.Should().Be(_flight.DepartureDateOffset.Offset);
71+
flight.DepartureDateOffsetZero.Offset.Should().Be(_flight.DepartureDateOffsetZero.Offset);
72+
flight.DepartureDateOffsetNonLocal.Offset.Should().Be(_flight.DepartureDateOffsetNonLocal.Offset);
73+
}
74+
75+
/// <remarks>
76+
/// Unspecified = Serialized as is with UTC offset
77+
/// UTC = Serialized as is with UTC Offset
78+
/// Local = Serialied as is with the local offset
79+
/// Offset = Serialized as is with specified offset
80+
/// </remarks>
81+
[U]
82+
public void Utc()
83+
{
84+
var dateTimeZoneHandling = DateTimeZoneHandling.Utc;
85+
var dateTimeKind = DateTimeKind.Utc;
86+
87+
var departureDateLocalInUtc = TimeZoneInfo.ConvertTime(_flight.DepartureDateLocal, TimeZoneInfo.Local, TimeZoneInfo.Utc);
88+
89+
var jsonWithUtcTimeZone = this.SerializeUsing(dateTimeZoneHandling);
90+
var expected = @" {
91+
""DepartureDate"": ""2013-01-21T00:00:00Z"",
92+
""DepartureDateUtc"": ""2013-01-21T00:00:00Z"",
93+
""DepartureDateLocal"": ""2013-01-21T00:00:00" + _offset + @""",
94+
""DepartureDateOffset"": ""2013-01-21T00:00:00" + _offset + @""",
95+
""DepartureDateOffsetZero"": ""2013-01-21T00:00:00+00:00"",
96+
""DepartureDateOffsetNonLocal"": ""2013-01-21T00:00:00-06:15""
97+
}";
98+
99+
jsonWithUtcTimeZone.JsonEquals(expected).Should().BeTrue("{0}", jsonWithUtcTimeZone);
100+
101+
var flight = this.DeserializeUsing(jsonWithUtcTimeZone, dateTimeZoneHandling);
102+
103+
flight.DepartureDate.Should().Be(_flight.DepartureDate);
104+
flight.DepartureDate.Kind.Should().Be(dateTimeKind);
105+
106+
// The deserialized local will be the UTC DateTime + the local timezone offset,
107+
// and with a DateTimeKind of UTC when deserialized.
108+
//
109+
// Calling .ToLocalTime() will return DepartureDateLocal with correct
110+
// local datetime and DateTimeKind.Local
111+
flight.DepartureDateLocal.Should().Be(departureDateLocalInUtc);
112+
flight.DepartureDateLocal.Kind.Should().Be(dateTimeKind);
113+
114+
flight.DepartureDateUtc.Should().Be(_flight.DepartureDateUtc);
115+
flight.DepartureDateUtc.Kind.Should().Be(dateTimeKind);
116+
117+
flight.DepartureDateOffset.Should().Be(_flight.DepartureDateOffset);
118+
flight.DepartureDateOffset.Offset.Should().Be(_flight.DepartureDateOffset.Offset);
119+
120+
flight.DepartureDateOffsetZero.Should().Be(_flight.DepartureDateOffsetZero);
121+
flight.DepartureDateOffsetZero.Offset.Should().Be(_flight.DepartureDateOffsetZero.Offset);
122+
123+
flight.DepartureDateOffsetNonLocal.Should().Be(_flight.DepartureDateOffsetNonLocal);
124+
flight.DepartureDateOffsetNonLocal.Offset.Should().Be(_flight.DepartureDateOffsetNonLocal.Offset);
125+
}
126+
127+
[U]
128+
public void Unspecified()
129+
{
130+
var dateTimeZoneHandling = DateTimeZoneHandling.Unspecified;
131+
var dateTimeKind = DateTimeKind.Unspecified;
132+
133+
var jsonWithUnspecifiedTimeZone = this.SerializeUsing(dateTimeZoneHandling);
134+
var expected = @" {
135+
""DepartureDate"": ""2013-01-21T00:00:00"",
136+
""DepartureDateUtc"": ""2013-01-21T00:00:00"",
137+
""DepartureDateLocal"": ""2013-01-21T00:00:00"",
138+
""DepartureDateOffset"": ""2013-01-21T00:00:00" + _offset + @""",
139+
""DepartureDateOffsetZero"": ""2013-01-21T00:00:00+00:00"",
140+
""DepartureDateOffsetNonLocal"": ""2013-01-21T00:00:00-06:15""
141+
}";
142+
143+
jsonWithUnspecifiedTimeZone.JsonEquals(expected).Should().BeTrue("{0}", jsonWithUnspecifiedTimeZone);
144+
145+
var flight = this.DeserializeUsing(jsonWithUnspecifiedTimeZone, dateTimeZoneHandling);
146+
147+
flight.Should().Be(_flight);
148+
flight.DepartureDate.Kind.Should().Be(dateTimeKind);
149+
flight.DepartureDateLocal.Kind.Should().Be(dateTimeKind);
150+
flight.DepartureDateUtc.Kind.Should().Be(dateTimeKind);
151+
flight.DepartureDateOffset.Offset.Should().Be(_flight.DepartureDateOffset.Offset);
152+
flight.DepartureDateOffsetZero.Offset.Should().Be(_flight.DepartureDateOffsetZero.Offset);
153+
flight.DepartureDateOffsetNonLocal.Offset.Should().Be(_flight.DepartureDateOffsetNonLocal.Offset);
154+
}
155+
156+
[U]
157+
public void Local()
158+
{
159+
var dateTimeZoneHandling = DateTimeZoneHandling.Local;
160+
var dateTimeKind = DateTimeKind.Local;
161+
162+
var jsonWithLocalTimeZone = this.SerializeUsing(dateTimeZoneHandling);
163+
var departureDateUtcInLocal = TimeZoneInfo.ConvertTime(_flight.DepartureDateUtc, TimeZoneInfo.Utc, TimeZoneInfo.Local);
164+
165+
var expected = @"
166+
{
167+
""DepartureDate"": ""2013-01-21T00:00:00"",
168+
""DepartureDateUtc"": ""2013-01-21T00:00:00Z"",
169+
""DepartureDateLocal"": ""2013-01-21T00:00:00" + _offset + @""",
170+
""DepartureDateOffset"": ""2013-01-21T00:00:00" + _offset + @""",
171+
""DepartureDateOffsetZero"": ""2013-01-21T00:00:00+00:00"",
172+
""DepartureDateOffsetNonLocal"": ""2013-01-21T00:00:00-06:15""
173+
}";
174+
175+
jsonWithLocalTimeZone.JsonEquals(expected).Should().BeTrue("{0}", jsonWithLocalTimeZone);
176+
177+
var flight = this.DeserializeUsing(jsonWithLocalTimeZone, dateTimeZoneHandling);
178+
179+
flight.DepartureDate.Should().Be(_flight.DepartureDate);
180+
flight.DepartureDate.Kind.Should().Be(dateTimeKind);
181+
182+
183+
flight.DepartureDateLocal.Should().Be(_flight.DepartureDateLocal);
184+
flight.DepartureDateLocal.Kind.Should().Be(dateTimeKind);
185+
186+
// The deserialized UTC will be the UTC DateTime + the local timezone offset
187+
// and a DateTimeKind of LOCAL when deserialized.
188+
//
189+
// Calling .ToUniversalTime() will return DepartureDateUtc with correct
190+
// UTC datetime and DateTimeKind.Utc
191+
flight.DepartureDateUtc.Should().Be(departureDateUtcInLocal);
192+
flight.DepartureDateUtc.Kind.Should().Be(dateTimeKind);
193+
194+
flight.DepartureDateOffset.Should().Be(_flight.DepartureDateOffset);
195+
flight.DepartureDateOffset.Offset.Should().Be(_flight.DepartureDateOffset.Offset);
196+
197+
flight.DepartureDateOffsetZero.Should().Be(_flight.DepartureDateOffsetZero);
198+
flight.DepartureDateOffsetZero.Offset.Should().Be(_flight.DepartureDateOffsetZero.Offset);
199+
200+
flight.DepartureDateOffsetNonLocal.Should().Be(_flight.DepartureDateOffsetNonLocal);
201+
flight.DepartureDateOffsetNonLocal.Offset.Should().Be(_flight.DepartureDateOffsetNonLocal.Offset);
202+
}
203+
204+
private string SerializeUsing(DateTimeZoneHandling handling)
205+
{
206+
var pool = new SingleNodeConnectionPool(new Uri("http://localhost:9200"));
207+
var settings = new ConnectionSettings(pool, new InMemoryConnection(), new SerializerFactory(
208+
(serializerSettings, connectionSettings) =>
209+
{
210+
serializerSettings.DateTimeZoneHandling = handling;
211+
serializerSettings.Formatting = Formatting.Indented;
212+
}))
213+
.DefaultFieldNameInferrer(p => p);
214+
215+
var client = new ElasticClient(settings);
216+
return client.Serializer.SerializeToString(_flight);
217+
}
218+
219+
private Flight DeserializeUsing(string json, DateTimeZoneHandling handling)
220+
{
221+
var pool = new SingleNodeConnectionPool(new Uri("http://localhost:9200"));
222+
var settings = new ConnectionSettings(pool, new InMemoryConnection(), new SerializerFactory(
223+
(serializerSettings, connectionSettings) =>
224+
{
225+
serializerSettings.DateTimeZoneHandling = handling;
226+
}))
227+
.DefaultFieldNameInferrer(p => p);
228+
229+
var client = new ElasticClient(settings);
230+
using (var stream = new MemoryStream(Encoding.UTF8.GetBytes(json)))
231+
{
232+
return client.Serializer.Deserialize<Flight>(stream);
233+
}
234+
}
235+
}
236+
237+
internal class Flight
238+
{
239+
public DateTime DepartureDate { get; set; }
240+
public DateTime DepartureDateUtc { get; set; }
241+
public DateTime DepartureDateLocal { get; set; }
242+
public DateTimeOffset DepartureDateOffset { get; set; }
243+
public DateTimeOffset DepartureDateOffsetZero { get; set; }
244+
public DateTimeOffset DepartureDateOffsetNonLocal { get; set; }
245+
246+
protected bool Equals(Flight other)
247+
{
248+
return DepartureDate.Equals(other.DepartureDate) &&
249+
DepartureDateUtc.Equals(other.DepartureDateUtc) &&
250+
DepartureDateLocal.Equals(other.DepartureDateLocal) &&
251+
DepartureDateOffset.Equals(other.DepartureDateOffset) &&
252+
DepartureDateOffsetZero.Equals(other.DepartureDateOffsetZero) &&
253+
DepartureDateOffsetNonLocal.Equals(other.DepartureDateOffsetNonLocal);
254+
}
255+
256+
public override bool Equals(object obj)
257+
{
258+
if (ReferenceEquals(null, obj)) return false;
259+
if (ReferenceEquals(this, obj)) return true;
260+
if (obj.GetType() != this.GetType()) return false;
261+
return Equals((Flight)obj);
262+
}
263+
264+
public override int GetHashCode()
265+
{
266+
unchecked
267+
{
268+
var hashCode = DepartureDate.GetHashCode();
269+
hashCode = (hashCode * 397) ^ DepartureDateUtc.GetHashCode();
270+
hashCode = (hashCode * 397) ^ DepartureDateLocal.GetHashCode();
271+
hashCode = (hashCode * 397) ^ DepartureDateOffset.GetHashCode();
272+
hashCode = (hashCode * 397) ^ DepartureDateOffsetZero.GetHashCode();
273+
hashCode = (hashCode * 397) ^ DepartureDateOffsetNonLocal.GetHashCode();
274+
return hashCode;
275+
}
276+
}
277+
}
278+
279+
internal static class JsonExtensions
280+
{
281+
internal static bool JsonEquals(this string value, string other)
282+
{
283+
var valueToken = JObject.Parse(value);
284+
var otherToken = JObject.Parse(other);
285+
return JToken.DeepEquals(valueToken, otherToken);
286+
}
287+
}
288+
}

0 commit comments

Comments
 (0)