Skip to content
Open
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
14 changes: 11 additions & 3 deletions src/NServiceKit.Text/Common/DateTimeSerializer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -73,18 +73,26 @@ public static class DateTimeSerializer
/// <summary>The XSD UTC suffix.</summary>
private const string XsdUtcSuffix = "Z";

/// <summary>If AlwaysUseUtc is set to true then convert all DateTime to UTC.</summary>
/// <summary>
/// If AssumeUtc, assume DateTimeKind.Unspecified dates are really UTC.
/// If AlwaysUseUtc is set to true then convert all DateTime to UTC.
/// </summary>
/// <param name="dateTime"> Date Time to be "Prepared".</param>
/// <param name="parsedAsUtc">true to parsed as UTC.</param>
/// <returns>The Prepared DateTime.</returns>
private static DateTime Prepare(this DateTime dateTime, bool parsedAsUtc = false)
{
if (dateTime.Kind == DateTimeKind.Unspecified && JsConfig.DateHandler != JsonDateHandler.TimestampOffset)
{
dateTime = DateTime.SpecifyKind(dateTime, JsConfig.AssumeUtc ? DateTimeKind.Utc : DateTimeKind.Local);
}

if (JsConfig.AlwaysUseUtc)
{
return dateTime.Kind != DateTimeKind.Utc ? dateTime.ToStableUniversalTime() : dateTime;
}

return parsedAsUtc ? dateTime.ToLocalTime() : dateTime;
return parsedAsUtc && !JsConfig.AssumeUtc ? dateTime.ToLocalTime() : dateTime;
}

/// <summary>Parses a date time string and returns a nullable datetime.</summary>
Expand Down Expand Up @@ -155,7 +163,7 @@ public static DateTime ParseShortestXsdDateTime(string dateTimeStr)

try
{
return DateTime.Parse(dateTimeStr, null, DateTimeStyles.AssumeLocal).Prepare();
return DateTime.Parse(dateTimeStr).Prepare();
}
catch (FormatException)
{
Expand Down
6 changes: 3 additions & 3 deletions tests/NServiceKit.Text.Tests/JsonTests/JsonDateTimeTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -383,16 +383,16 @@ public void Can_deserialize_json_date_iso8601_withZOffset_asUtc()
JsConfig.Reset();
}

/// <summary>Can deserialize JSON date ISO 8601 without offset as unspecified.</summary>
/// <summary>Can deserialize JSON date ISO 8601 without offset as Local (per the ISO 8601 spec).</summary>
[Test]
public void Can_deserialize_json_date_iso8601_withoutOffset_asUnspecified()
public void Can_deserialize_json_date_iso8601_withoutOffset_asLocal()
{
JsConfig.DateHandler = JsonDateHandler.ISO8601;

const string json = @"""1994-11-24T12:34:56""";
var fromJson = JsonSerializer.DeserializeFromString<DateTime>(json);

var dateTime = new DateTime(1994, 11, 24, 12, 34, 56, DateTimeKind.Unspecified);
var dateTime = new DateTime(1994, 11, 24, 12, 34, 56, DateTimeKind.Local);
Assert.That(fromJson, Is.EqualTo(dateTime));
Assert.That(fromJson.Kind, Is.EqualTo(dateTime.Kind));
JsConfig.Reset();
Expand Down
22 changes: 6 additions & 16 deletions tests/NServiceKit.Text.Tests/NServiceKit.Text.Tests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -122,33 +122,17 @@
<Reference Include="NServiceKit.ServiceInterface">
<HintPath>..\..\lib\tests\NServiceKit.ServiceInterface.dll</HintPath>
</Reference>
<Reference Include="NServiceKit.Text">
<HintPath>..\..\src\NServiceKit.Text\bin\Release\NServiceKit.Text.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.ComponentModel.DataAnnotations" />
<Reference Include="System.Core" />
<Reference Include="System.Drawing" />
<Reference Include="System.Runtime.Serialization" />
<Reference Include="System.ServiceModel.Web" />
<Reference Include="System.Web" />
<Reference Include="System.Web.Extensions" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="System.Data" />
<Reference Include="System.Xml" />
<Reference Include="nunit.framework">
<HintPath>..\..\lib\tests\nunit.framework.dll</HintPath>
</Reference>
<Reference Include="Platform">
<HintPath>..\..\lib\tests\Platform.dll</HintPath>
</Reference>
<Reference Include="protobuf-net">
<HintPath>..\..\lib\tests\protobuf-net.dll</HintPath>
</Reference>
<Reference Include="protobuf-net.Extensions">
<HintPath>..\..\lib\tests\protobuf-net.Extensions.dll</HintPath>
</Reference>
</ItemGroup>
<ItemGroup>
<Compile Include="AdhocModelTests.cs" />
Expand Down Expand Up @@ -265,6 +249,12 @@
<ItemGroup>
<Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\NServiceKit.Text\NServiceKit.Text.csproj">
<Project>{579b3fdb-cdad-44e1-8417-885c38e49a0e}</Project>
<Name>NServiceKit.Text</Name>
</ProjectReference>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
Expand Down
88 changes: 84 additions & 4 deletions tests/NServiceKit.Text.Tests/Utils/DateTimeSerializerTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -150,8 +150,8 @@ public void DateTimeWithoutMilliseconds_should_always_be_deserialized_correctly_
Assert.AreEqual(dateWithoutMillisecondsUnspecified, deserialized);
}

/// <summary>UTC date time is deserialized as kind UTC.</summary>
[Test, Ignore("Don't pre-serialize into Utc")]
/// <summary>UTC date time is deserialized as kind UTC.</summary>
[Test]
public void UtcDateTime_Is_Deserialized_As_Kind_Utc()
{
//Serializing UTC
Expand All @@ -160,7 +160,7 @@ public void UtcDateTime_Is_Deserialized_As_Kind_Utc()
var serialized = JsonSerializer.SerializeToString(utcNow);
//Deserializing UTC?
var deserialized = JsonSerializer.DeserializeFromString<DateTime>(serialized);
Assert.That(deserialized.Kind, Is.EqualTo(DateTimeKind.Utc)); //fails -> is DateTimeKind.Local
Assert.That(deserialized.Kind, Is.EqualTo(DateTimeKind.Utc));
Copy link
Author

Choose a reason for hiding this comment

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

The comment said this test was failing but I checked it again and it passed fine, so I removed the Ignore tag.

}

/// <summary>
Expand Down Expand Up @@ -371,6 +371,86 @@ public void DateTime_Is_Serialized_As_Utc_and_Deserialized_as_local()
{
Assert.AreEqual(DateTimeKind.Utc, TypeSerializer.DeserializeFromString<TestObject>(TypeSerializer.SerializeToString<TestObject>(testObject)).Date.Kind);
}
}
}

/// <summary>
/// DateTime with DateTimeKind.Unspecified should be treated as UTC (rather than Local) when AssumeUtc is true
/// </summary>
[Test]
public void DateTime_Unspecified_Is_Serialized_And_Deserialized_As_Utc_When_AssumeUtc_Flag_Is_True()
{
var dateTimeUnspecified = new DateTime(2013, 1, 1, 0, 0, 1, DateTimeKind.Unspecified);

// Using JsonSerializer becayse TypeSerializer doesn't call DateTimeSerializer.WriteWcfJsonDate
var deserialized = JsonSerializer.DeserializeFromString<DateTime>(JsonSerializer.SerializeToString<DateTime>(dateTimeUnspecified));
Copy link
Author

Choose a reason for hiding this comment

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

Had to use JsonSerializer here because it seems TypeSerializer is only calling the DateTimeSerializer methods on deserialize... not on serialize. JsonSerializer used it for both.

Assert.AreEqual(DateTimeKind.Local, deserialized.Date.Kind);
Assert.AreEqual(dateTimeUnspecified.Hour, deserialized.Hour);


// Change the behavior with config
using (JsConfig.With(assumeUtc: true))
{
// Using JsonSerializer becayse TypeSerializer doesn't call DateTimeSerializer.WriteWcfJsonDate
var serializedUtc = JsonSerializer.SerializeToString<DateTime>(dateTimeUnspecified);
var deserializedUtc = JsonSerializer.DeserializeFromString<DateTime>(serializedUtc);
Copy link
Author

Choose a reason for hiding this comment

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

Same as note above at line 385.

Assert.AreEqual(DateTimeKind.Utc, deserializedUtc.Date.Kind);
Assert.AreEqual(dateTimeUnspecified.Hour, deserializedUtc.Hour);
}
}

/// <summary>
/// Dates strings with no time component should deserialize to Midnight (00:00:00) Local by default, or Midnight Utc when AssumeUtc is set to true
/// </summary>
[Test]
public void DateTime_Should_Deserialize_Strings_With_No_Time_As_Midnight()
{
const string dateTimeStr = "2015-03-23";

var deserialized = (TypeSerializer.DeserializeFromString<DateTime>(dateTimeStr));
Assert.AreEqual(DateTimeKind.Local, deserialized.Date.Kind);
Assert.AreEqual(0, deserialized.Hour);

// Change the behavior with config
using (JsConfig.With(assumeUtc: true))
{
var deserializedUtc = (TypeSerializer.DeserializeFromString<DateTime>(dateTimeStr));
Assert.AreEqual(DateTimeKind.Utc, deserializedUtc.Date.Kind);
Assert.AreEqual(0, deserializedUtc.Hour);
}
}

[Test]
public void DateTime_Should_Deserialize_Correctly_If_It_Doesnt_Recognize_The_Offset_and_falls_back_to_DateTimeParse()
{
const string dateTimeStr = "2015-03-31T16:02:42-04:00";
var currentTimeZone = TimeZone.CurrentTimeZone;

// TEST 1: No JsConfig options set should deserialize as local
var deserialized = (TypeSerializer.DeserializeFromString<DateTime>(dateTimeStr));
Assert.AreEqual(DateTimeKind.Local, deserialized.Date.Kind);

var deserializedJson = (JsonSerializer.DeserializeFromString<DateTime>(dateTimeStr));
Assert.AreEqual(DateTimeKind.Local, deserializedJson.Date.Kind);

// TEST 2: AssumeUtc shouldn't change anything because the time zone is specified
using (JsConfig.With(assumeUtc: true))
{
deserialized = (TypeSerializer.DeserializeFromString<DateTime>(dateTimeStr));
Assert.AreEqual(DateTimeKind.Local, deserialized.Date.Kind);

deserializedJson = (JsonSerializer.DeserializeFromString<DateTime>(dateTimeStr));
Assert.AreEqual(DateTimeKind.Local, deserializedJson.Date.Kind);
}

// TEST 3: AlwaysUseUtc should convert the time to UTC
using (JsConfig.With(alwaysUseUtc: true))
{
deserialized = (TypeSerializer.DeserializeFromString<DateTime>(dateTimeStr));
Assert.AreEqual(DateTimeKind.Utc, deserialized.Date.Kind);

deserializedJson = (JsonSerializer.DeserializeFromString<DateTime>(dateTimeStr));
Assert.AreEqual(DateTimeKind.Utc, deserializedJson.Date.Kind);
}
}
}
}