Skip to content

Commit

Permalink
Supports DateTimeOffset.
Browse files Browse the repository at this point in the history
  • Loading branch information
cmstar committed May 9, 2022
1 parent ce0b0a5 commit 1511b04
Show file tree
Hide file tree
Showing 12 changed files with 299 additions and 160 deletions.
23 changes: 16 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,7 @@ The table below gives out the default contracts, which will convert the CLR type
|Single|NumberContarct|Number|
|Double|NumberContarct|Number|
|Decimal|NumberContarct|Number|
|DateTimeOffset|DateTimeOffsetContarct|String|
|DateTime|DateTimeContarct|String|
|Guid|GuidContarct|String|
|Nullable<T>|NullableTypeContract|Depends on typeof(T)|
Expand Down Expand Up @@ -229,28 +230,36 @@ Note: You can't register custom `JsonContract`s to `JsonSerializer.Default` at p

### Serializing Dates

The default contract for `DateTime` is the `DateTimeContract`, which will serialize dates
in the ISO-8601 format `yyyy-MM-ddTHH:mm:ss.ffffffZ`, such as `2022-01-31T13:15:05.2151663-02:00`.
The default contract for `DateTime`/`DateTimeOffset` is the `DateTimeContract`/`DateTimeOffsetContract`,
which will serialize dates in the ISO-8601 format `yyyy-MM-ddTHH:mm:ss.ffffffZ`,
such as `2022-01-31T13:15:05.2151663-02:00`, or `2022-01-31T13:15:05.2151663Z` (UTC).

You can register `CustomFormatDateTimeContract` to customize the format, with a property `Format`,
You can register `CustomFormatDateTimeOffsetContract` to customize the format, with a property `Format`,
the code below shows how to serialize dates in the format `yyyy~MM~dd HH:mm:ss`:

`DateTimeContract` shares the format of `DateTimeOffsetContract`,
Change the format for `DateTimeOffset` will also change the format for `DateTime`.

```csharp
var dateTimeContract = new CustomFormatDateTimeContract();
var dateTimeContract = new CustomFormatDateTimeOffsetContract();
dateTimeContract.Format = "yyyy~MM~dd HH@mm@ss";

var customContracts = new Dictionary<Type, JsonContract>();
customContracts.Add(typeof(DateTime), dateTimeContract);
customContracts.Add(typeof(DateTimeOffset), dateTimeContract);

var contractResolver = new JsonContractResolver(customContracts);
var serializer = new JsonSerializer(contractResolver);

serializer.Serialize(DateTimeOffset.Now);
//-> "2013~07~15 14@41@03"
// When serializing DateTime, it shares the format.
serializer.Serialize(DateTime.Now);
//-> "2013~07~15 14@41@03"
```

Another contract provided is `JavascriptDateTimeContract`, that formats time
in the Javascript `Date` function expression, like `/Date(1620142251000+0300)/`.
Another contract provided is `MicrosoftJsonDateContract`, which formats time
in the Miscrosoft JSON format, such as `/Date(1620142251000+0300)/`.

### Serializing Enums

Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
using System;
using NUnit.Framework;

namespace cmstar.Serialization.Json.Contracts
{
[TestFixture]
public class CustomFormatDateTimeOffsetContractTests : DateTimeOffsetContractTests
{
protected override JsonContract GetContract()
{
var contract = new CustomFormatDateTimeOffsetContract();
contract.Format = "yyyy@M@dd HH~~mm~~ss ffffff";
return contract;
}

public override void Write()
{
var date = new DateTime(2013, 1, 25, 12, 26, 33, 123);
var json = DoWrite(date);
Assert.AreEqual("\"2013@1@25 12~~26~~33 123000\"", json);

date = new DateTime(2012, 3, 15, 6, 25, 35, 152);
json = DoWrite(date);
Assert.AreEqual("\"2012@3@15 06~~25~~35 152000\"", json);

date = new DateTime(1976, 12, 2, 23, 42, 25, 0);
json = DoWrite(date);
Assert.AreEqual("\"1976@12@02 23~~42~~28 000000\"", json);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
using System;
using NUnit.Framework;

namespace cmstar.Serialization.Json.Contracts
{
[TestFixture]
public class DateTimeOffsetContractTests : ContractTestBase
{
protected override Type UnderlyingType => typeof(DateTimeOffset);

protected override bool SupportsNullValue => false;

[Test]
public virtual void Write()
{
var date = new DateTimeOffset(2013, 1, 25, 12, 26, 33, 123, TimeSpan.FromHours(0));
var json = DoWrite(date);
Assert.AreEqual("\"2013-01-25T12:26:33.1230000Z\"", json);

date = new DateTimeOffset(2012, 3, 15, 6, 25, 35, 152, TimeSpan.FromHours(4));
json = DoWrite(date);
Assert.AreEqual("\"2012-03-15T06:25:35.1520000+04:00\"", json);

date = new DateTimeOffset(1976, 12, 2, 23, 42, 25, 765, TimeSpan.FromHours(-6));
json = DoWrite(date);
Assert.AreEqual("\"1976-12-02T23:42:25.7650000-06:00\"", json);
}

// The contract can read value in Microsoft JSON datetime format.
[Test]
public void ReadMicrosoftJsonDate()
{
var result = DoRead("\"\\/Date(1359116793123)\\/\"");
Assert.IsInstanceOf<DateTimeOffset>(result);
var expected = new DateTimeOffset(2013, 1, 25, 12, 26, 33, 123, TimeSpan.FromHours(0));
Assert.AreEqual(expected, (DateTimeOffset)result);

result = DoRead("\"/Date(218418145765+0400)/\"");
Assert.IsInstanceOf<DateTimeOffset>(result);
expected = new DateTimeOffset(1976, 12, 3, 3, 42, 25, 765, TimeSpan.FromHours(4));
Assert.AreEqual(expected, (DateTimeOffset)result);

result = DoRead("\"/Date(218418145765-0800)/\"");
Assert.IsInstanceOf<DateTimeOffset>(result);
expected = new DateTimeOffset(1976, 12, 2, 15, 42, 25, 765, TimeSpan.FromHours(-8));
Assert.AreEqual(expected, (DateTimeOffset)result);
}

[Test]
public void ReadString()
{
var result = DoRead("\"2013-11-25 18:28:36.213\"");
Assert.IsInstanceOf<DateTimeOffset>(result);
var localTime = DateTime.Parse("2013-11-25 18:28:36.213");
var expected = new DateTimeOffset(localTime);
Assert.AreEqual(expected, result);

result = DoRead("\"2013-11-25T18:28:36.213+0000\"");
Assert.IsInstanceOf<DateTimeOffset>(result);
expected = new DateTimeOffset(2013, 11, 25, 18, 28, 36, 213, TimeSpan.FromHours(0));
Assert.AreEqual(expected, result);

result = DoRead("\"2013-11-25T18:28:36.213+0300\"");
Assert.IsInstanceOf<DateTimeOffset>(result);
expected = new DateTimeOffset(2013, 11, 25, 18, 28, 36, 213, TimeSpan.FromHours(3));
Assert.AreEqual(expected, result);

result = DoRead("\"2013-11-25T18:28:36.213-0600\"");
Assert.IsInstanceOf<DateTimeOffset>(result);
expected = new DateTimeOffset(2013, 11, 25, 18, 28, 36, 213, TimeSpan.FromHours(-6));
Assert.AreEqual(expected, result);
}

[Test]
public void ReadIllegal()
{
Assert.Throws<JsonContractException>(() => DoRead("\"some value that is not a date\""));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
namespace cmstar.Serialization.Json.Contracts
{
[TestFixture]
public class MicrosoftJsonDateContractTests : DateTimeContractTests
public class MicrosoftJsonDateContractTests : DateTimeOffsetContractTests
{
protected override JsonContract GetContract()
{
Expand All @@ -15,17 +15,17 @@ protected override JsonContract GetContract()
[Test]
public override void Write()
{
var date = new DateTime(2013, 1, 25, 12, 26, 33, 123, DateTimeKind.Utc);
var date = new DateTimeOffset(2013, 1, 25, 12, 26, 33, 123, TimeSpan.FromHours(0));
var json = DoWrite(date);
Assert.AreEqual("\"\\/Date(1359116793123)\\/\"", json);

date = new DateTime(2012, 3, 15, 6, 25, 35, 152, DateTimeKind.Utc);
date = new DateTimeOffset(2012, 3, 15, 14, 25, 35, 152, TimeSpan.FromHours(8));
json = DoWrite(date);
Assert.AreEqual("\"\\/Date(1331792735152)\\/\"", json);
Assert.AreEqual("\"\\/Date(1331792735152+0800)\\/\"", json);

date = new DateTime(1976, 12, 2, 23, 42, 25, 765, DateTimeKind.Utc);
date = new DateTimeOffset(1976, 12, 2, 17, 42, 25, 765, TimeSpan.FromHours(-6));
json = DoWrite(date);
Assert.AreEqual("\"\\/Date(218418145765)\\/\"", json);
Assert.AreEqual("\"\\/Date(218418145765-0600)\\/\"", json);
}
}
}
13 changes: 10 additions & 3 deletions src/cmstar.Tests/Serialization/Json/JsonContractResolverTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ public void ResolveScalarContract()
AssertTypeContract<StringContract>(resolver, typeof(char));
AssertTypeContract<StringContract>(resolver, typeof(string));
AssertTypeContract<BooleanContract>(resolver, typeof(bool));
AssertTypeContract<DateTimeOffsetContract>(resolver, typeof(DateTimeOffset));
AssertTypeContract<DateTimeContract>(resolver, typeof(DateTime));
AssertTypeContract<EnumContract>(resolver, typeof(SaleOrderType));
AssertTypeContract<GuidContract>(resolver, typeof(Guid));
Expand Down Expand Up @@ -393,9 +394,9 @@ private TContract AssertTypeContract<TContract>(JsonContractResolver resolver, T

private class TypeAndNameAndIsProperty
{
public Type Type { get; private set; }
public string Name { get; private set; }
public bool IsProperty { get; private set; }
public Type Type { get; }
public string Name { get; }
public bool IsProperty { get; }

public TypeAndNameAndIsProperty(Type type, string name, bool isProperty)
{
Expand All @@ -409,6 +410,9 @@ private class A : List<B>
{
}

// ReSharper disable UnusedMember.Global
// ReSharper disable ValueParameterNotUsed
// ReSharper disable ArrangeAccessorOwnerBody
private class B
{
public A RefA { get; set; }
Expand Down Expand Up @@ -486,5 +490,8 @@ public int NoGetter { set { } }
[JsonProperty("internal_getter")]
public int InternalGetter { internal get; set; }
}
// ReSharper restore ArrangeAccessorOwnerBody
// ReSharper restore ValueParameterNotUsed
// ReSharper restore UnusedMember.Global
}
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
using System;

namespace cmstar.Serialization.Json.Contracts
{
/// <summary>
/// An extension of <see cref="DateTimeOffsetContract"/> that allows to specify
/// the format for serializing the date.
/// </summary>
public class CustomFormatDateTimeOffsetContract : DateTimeOffsetContract
{
private string _format;

/// <summary>
/// Gets or sets a value which is used to format the date and time.
/// The format string will be passed to the <see cref="DateTime.ToString()"/>
/// method during the serializing.
/// </summary>
public string Format
{
get
{
return _format;
}
set
{
ArgAssert.NotNull(value, "Format");
_format = value;
}
}

protected override string ToStringValue(DateTimeOffset value)
{
//if _format is null the default format would be used
return value.ToString(_format);
}
}
}
Loading

0 comments on commit 1511b04

Please sign in to comment.