diff --git a/src/Tests/ClientConcepts/Serializer/IsoDateTimeConverterTests.cs b/src/Tests/ClientConcepts/Serializer/IsoDateTimeConverterTests.cs
new file mode 100644
index 00000000000..8e1aaf1ac60
--- /dev/null
+++ b/src/Tests/ClientConcepts/Serializer/IsoDateTimeConverterTests.cs
@@ -0,0 +1,288 @@
+using System;
+using System.IO;
+using System.Text;
+using Elasticsearch.Net;
+using FluentAssertions;
+using Nest;
+using Newtonsoft.Json;
+using Newtonsoft.Json.Linq;
+using Tests.Framework;
+
+namespace Tests.ClientConcepts.Serializer
+{
+ ///
+ /// Tests for default DateTime zone serialization within NEST
+ ///
+ public class IsoDateTimeConverterHandlingTests
+ {
+ private readonly Flight _flight;
+ private readonly string _offset;
+ private readonly TimeSpan _timeSpanOffset;
+
+ public IsoDateTimeConverterHandlingTests()
+ {
+ var departureDateLocal = new DateTime(2013, 1, 21, 0, 0, 0, DateTimeKind.Local);
+ _timeSpanOffset = TimeZoneInfo.Local.GetUtcOffset(departureDateLocal);
+
+ _flight = new Flight
+ {
+ DepartureDate = new DateTime(2013, 1, 21, 0, 0, 0, DateTimeKind.Unspecified),
+ DepartureDateUtc = new DateTime(2013, 1, 21, 0, 0, 0, DateTimeKind.Utc),
+ DepartureDateLocal = departureDateLocal,
+ DepartureDateOffset = new DateTimeOffset(2013, 1, 21, 0, 0, 0, _timeSpanOffset),
+ DepartureDateOffsetZero = new DateTimeOffset(2013, 1, 21, 0, 0, 0, TimeSpan.Zero),
+ DepartureDateOffsetNonLocal = new DateTimeOffset(2013, 1, 21, 0, 0, 0, TimeSpan.FromHours(-6.25)),
+ };
+
+ _offset = $"{_timeSpanOffset.Hours.ToString("+00;-00;")}:{_timeSpanOffset.Minutes.ToString("00")}";
+ }
+
+ ///
+ /// Timezone offset serialized is based on DateTimeKind
+ /// Unspecified = None
+ /// Utc = UTC Timezone identifier
+ /// Local = Local Timezone offset
+ /// Offset = Timezone offset specified
+ ///
+ [U]
+ public void RoundTripKind()
+ {
+ var dateTimeZoneHandling = DateTimeZoneHandling.RoundtripKind;
+
+ var jsonWithRoundtripTimeZone = this.SerializeUsing(dateTimeZoneHandling);
+ var expected = @" {
+ ""DepartureDate"": ""2013-01-21T00:00:00"",
+ ""DepartureDateUtc"": ""2013-01-21T00:00:00Z"",
+ ""DepartureDateLocal"": ""2013-01-21T00:00:00" + _offset + @""",
+ ""DepartureDateOffset"": ""2013-01-21T00:00:00" + _offset + @""",
+ ""DepartureDateOffsetZero"": ""2013-01-21T00:00:00+00:00"",
+ ""DepartureDateOffsetNonLocal"": ""2013-01-21T00:00:00-06:15""
+ }";
+
+ jsonWithRoundtripTimeZone.JsonEquals(expected).Should().BeTrue("{0}", jsonWithRoundtripTimeZone);
+
+ var flight = this.DeserializeUsing(jsonWithRoundtripTimeZone, dateTimeZoneHandling);
+
+ flight.Should().Be(_flight);
+ flight.DepartureDate.Kind.Should().Be(_flight.DepartureDate.Kind);
+ flight.DepartureDateLocal.Kind.Should().Be(_flight.DepartureDateLocal.Kind);
+ flight.DepartureDateUtc.Kind.Should().Be(_flight.DepartureDateUtc.Kind);
+ flight.DepartureDateOffset.Offset.Should().Be(_flight.DepartureDateOffset.Offset);
+ flight.DepartureDateOffsetZero.Offset.Should().Be(_flight.DepartureDateOffsetZero.Offset);
+ flight.DepartureDateOffsetNonLocal.Offset.Should().Be(_flight.DepartureDateOffsetNonLocal.Offset);
+ }
+
+ ///
+ /// Unspecified = Serialized as is with UTC offset
+ /// UTC = Serialized as is with UTC Offset
+ /// Local = Serialied as is with the local offset
+ /// Offset = Serialized as is with specified offset
+ ///
+ [U]
+ public void Utc()
+ {
+ var dateTimeZoneHandling = DateTimeZoneHandling.Utc;
+ var dateTimeKind = DateTimeKind.Utc;
+
+ var departureDateLocalInUtc = TimeZoneInfo.ConvertTime(_flight.DepartureDateLocal, TimeZoneInfo.Local, TimeZoneInfo.Utc);
+
+ var jsonWithUtcTimeZone = this.SerializeUsing(dateTimeZoneHandling);
+ var expected = @" {
+ ""DepartureDate"": ""2013-01-21T00:00:00Z"",
+ ""DepartureDateUtc"": ""2013-01-21T00:00:00Z"",
+ ""DepartureDateLocal"": ""2013-01-21T00:00:00" + _offset + @""",
+ ""DepartureDateOffset"": ""2013-01-21T00:00:00" + _offset + @""",
+ ""DepartureDateOffsetZero"": ""2013-01-21T00:00:00+00:00"",
+ ""DepartureDateOffsetNonLocal"": ""2013-01-21T00:00:00-06:15""
+ }";
+
+ jsonWithUtcTimeZone.JsonEquals(expected).Should().BeTrue("{0}", jsonWithUtcTimeZone);
+
+ var flight = this.DeserializeUsing(jsonWithUtcTimeZone, dateTimeZoneHandling);
+
+ flight.DepartureDate.Should().Be(_flight.DepartureDate);
+ flight.DepartureDate.Kind.Should().Be(dateTimeKind);
+
+ // The deserialized local will be the UTC DateTime + the local timezone offset,
+ // and with a DateTimeKind of UTC when deserialized.
+ //
+ // Calling .ToLocalTime() will return DepartureDateLocal with correct
+ // local datetime and DateTimeKind.Local
+ flight.DepartureDateLocal.Should().Be(departureDateLocalInUtc);
+ flight.DepartureDateLocal.Kind.Should().Be(dateTimeKind);
+
+ flight.DepartureDateUtc.Should().Be(_flight.DepartureDateUtc);
+ flight.DepartureDateUtc.Kind.Should().Be(dateTimeKind);
+
+ flight.DepartureDateOffset.Should().Be(_flight.DepartureDateOffset);
+ flight.DepartureDateOffset.Offset.Should().Be(_flight.DepartureDateOffset.Offset);
+
+ flight.DepartureDateOffsetZero.Should().Be(_flight.DepartureDateOffsetZero);
+ flight.DepartureDateOffsetZero.Offset.Should().Be(_flight.DepartureDateOffsetZero.Offset);
+
+ flight.DepartureDateOffsetNonLocal.Should().Be(_flight.DepartureDateOffsetNonLocal);
+ flight.DepartureDateOffsetNonLocal.Offset.Should().Be(_flight.DepartureDateOffsetNonLocal.Offset);
+ }
+
+ [U]
+ public void Unspecified()
+ {
+ var dateTimeZoneHandling = DateTimeZoneHandling.Unspecified;
+ var dateTimeKind = DateTimeKind.Unspecified;
+
+ var jsonWithUnspecifiedTimeZone = this.SerializeUsing(dateTimeZoneHandling);
+ var expected = @" {
+ ""DepartureDate"": ""2013-01-21T00:00:00"",
+ ""DepartureDateUtc"": ""2013-01-21T00:00:00"",
+ ""DepartureDateLocal"": ""2013-01-21T00:00:00"",
+ ""DepartureDateOffset"": ""2013-01-21T00:00:00" + _offset + @""",
+ ""DepartureDateOffsetZero"": ""2013-01-21T00:00:00+00:00"",
+ ""DepartureDateOffsetNonLocal"": ""2013-01-21T00:00:00-06:15""
+ }";
+
+ jsonWithUnspecifiedTimeZone.JsonEquals(expected).Should().BeTrue("{0}", jsonWithUnspecifiedTimeZone);
+
+ var flight = this.DeserializeUsing(jsonWithUnspecifiedTimeZone, dateTimeZoneHandling);
+
+ flight.Should().Be(_flight);
+ flight.DepartureDate.Kind.Should().Be(dateTimeKind);
+ flight.DepartureDateLocal.Kind.Should().Be(dateTimeKind);
+ flight.DepartureDateUtc.Kind.Should().Be(dateTimeKind);
+ flight.DepartureDateOffset.Offset.Should().Be(_flight.DepartureDateOffset.Offset);
+ flight.DepartureDateOffsetZero.Offset.Should().Be(_flight.DepartureDateOffsetZero.Offset);
+ flight.DepartureDateOffsetNonLocal.Offset.Should().Be(_flight.DepartureDateOffsetNonLocal.Offset);
+ }
+
+ [U]
+ public void Local()
+ {
+ var dateTimeZoneHandling = DateTimeZoneHandling.Local;
+ var dateTimeKind = DateTimeKind.Local;
+
+ var jsonWithLocalTimeZone = this.SerializeUsing(dateTimeZoneHandling);
+ var departureDateUtcInLocal = TimeZoneInfo.ConvertTime(_flight.DepartureDateUtc, TimeZoneInfo.Utc, TimeZoneInfo.Local);
+
+ var expected = @"
+ {
+ ""DepartureDate"": ""2013-01-21T00:00:00"",
+ ""DepartureDateUtc"": ""2013-01-21T00:00:00Z"",
+ ""DepartureDateLocal"": ""2013-01-21T00:00:00" + _offset + @""",
+ ""DepartureDateOffset"": ""2013-01-21T00:00:00" + _offset + @""",
+ ""DepartureDateOffsetZero"": ""2013-01-21T00:00:00+00:00"",
+ ""DepartureDateOffsetNonLocal"": ""2013-01-21T00:00:00-06:15""
+ }";
+
+ jsonWithLocalTimeZone.JsonEquals(expected).Should().BeTrue("{0}", jsonWithLocalTimeZone);
+
+ var flight = this.DeserializeUsing(jsonWithLocalTimeZone, dateTimeZoneHandling);
+
+ flight.DepartureDate.Should().Be(_flight.DepartureDate);
+ flight.DepartureDate.Kind.Should().Be(dateTimeKind);
+
+
+ flight.DepartureDateLocal.Should().Be(_flight.DepartureDateLocal);
+ flight.DepartureDateLocal.Kind.Should().Be(dateTimeKind);
+
+ // The deserialized UTC will be the UTC DateTime + the local timezone offset
+ // and a DateTimeKind of LOCAL when deserialized.
+ //
+ // Calling .ToUniversalTime() will return DepartureDateUtc with correct
+ // UTC datetime and DateTimeKind.Utc
+ flight.DepartureDateUtc.Should().Be(departureDateUtcInLocal);
+ flight.DepartureDateUtc.Kind.Should().Be(dateTimeKind);
+
+ flight.DepartureDateOffset.Should().Be(_flight.DepartureDateOffset);
+ flight.DepartureDateOffset.Offset.Should().Be(_flight.DepartureDateOffset.Offset);
+
+ flight.DepartureDateOffsetZero.Should().Be(_flight.DepartureDateOffsetZero);
+ flight.DepartureDateOffsetZero.Offset.Should().Be(_flight.DepartureDateOffsetZero.Offset);
+
+ flight.DepartureDateOffsetNonLocal.Should().Be(_flight.DepartureDateOffsetNonLocal);
+ flight.DepartureDateOffsetNonLocal.Offset.Should().Be(_flight.DepartureDateOffsetNonLocal.Offset);
+ }
+
+ private string SerializeUsing(DateTimeZoneHandling handling)
+ {
+ var pool = new SingleNodeConnectionPool(new Uri("http://localhost:9200"));
+ var settings = new ConnectionSettings(pool, new InMemoryConnection(), new SerializerFactory(
+ (serializerSettings, connectionSettings) =>
+ {
+ serializerSettings.DateTimeZoneHandling = handling;
+ serializerSettings.Formatting = Formatting.Indented;
+ }))
+ .DefaultFieldNameInferrer(p => p);
+
+ var client = new ElasticClient(settings);
+ return client.Serializer.SerializeToString(_flight);
+ }
+
+ private Flight DeserializeUsing(string json, DateTimeZoneHandling handling)
+ {
+ var pool = new SingleNodeConnectionPool(new Uri("http://localhost:9200"));
+ var settings = new ConnectionSettings(pool, new InMemoryConnection(), new SerializerFactory(
+ (serializerSettings, connectionSettings) =>
+ {
+ serializerSettings.DateTimeZoneHandling = handling;
+ }))
+ .DefaultFieldNameInferrer(p => p);
+
+ var client = new ElasticClient(settings);
+ using (var stream = new MemoryStream(Encoding.UTF8.GetBytes(json)))
+ {
+ return client.Serializer.Deserialize(stream);
+ }
+ }
+ }
+
+ internal class Flight
+ {
+ public DateTime DepartureDate { get; set; }
+ public DateTime DepartureDateUtc { get; set; }
+ public DateTime DepartureDateLocal { get; set; }
+ public DateTimeOffset DepartureDateOffset { get; set; }
+ public DateTimeOffset DepartureDateOffsetZero { get; set; }
+ public DateTimeOffset DepartureDateOffsetNonLocal { get; set; }
+
+ protected bool Equals(Flight other)
+ {
+ return DepartureDate.Equals(other.DepartureDate) &&
+ DepartureDateUtc.Equals(other.DepartureDateUtc) &&
+ DepartureDateLocal.Equals(other.DepartureDateLocal) &&
+ DepartureDateOffset.Equals(other.DepartureDateOffset) &&
+ DepartureDateOffsetZero.Equals(other.DepartureDateOffsetZero) &&
+ DepartureDateOffsetNonLocal.Equals(other.DepartureDateOffsetNonLocal);
+ }
+
+ public override bool Equals(object obj)
+ {
+ if (ReferenceEquals(null, obj)) return false;
+ if (ReferenceEquals(this, obj)) return true;
+ if (obj.GetType() != this.GetType()) return false;
+ return Equals((Flight)obj);
+ }
+
+ public override int GetHashCode()
+ {
+ unchecked
+ {
+ var hashCode = DepartureDate.GetHashCode();
+ hashCode = (hashCode * 397) ^ DepartureDateUtc.GetHashCode();
+ hashCode = (hashCode * 397) ^ DepartureDateLocal.GetHashCode();
+ hashCode = (hashCode * 397) ^ DepartureDateOffset.GetHashCode();
+ hashCode = (hashCode * 397) ^ DepartureDateOffsetZero.GetHashCode();
+ hashCode = (hashCode * 397) ^ DepartureDateOffsetNonLocal.GetHashCode();
+ return hashCode;
+ }
+ }
+ }
+
+ internal static class JsonExtensions
+ {
+ internal static bool JsonEquals(this string value, string other)
+ {
+ var valueToken = JObject.Parse(value);
+ var otherToken = JObject.Parse(other);
+ return JToken.DeepEquals(valueToken, otherToken);
+ }
+ }
+}
\ No newline at end of file