Add support to pre-epoch Unix time (#2614)
Co-authored-by: Max A. Kiselev <kiselevma@gmail.com> Co-authored-by: James Newton-King <james@newtonking.com>
This commit is contained in:
co-authored by
Max A. Kiselev
James Newton-King
parent
13717cfc45
commit
7f78562d44
@@ -25,6 +25,7 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
#if DNXCORE50
|
||||
using Xunit;
|
||||
using Test = Xunit.FactAttribute;
|
||||
@@ -71,6 +72,17 @@ namespace Newtonsoft.Json.Tests.Converters
|
||||
);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void SerializeDateBeforeEpoch()
|
||||
{
|
||||
DateTime date = new DateTime(1964, 2, 7);
|
||||
long dateSeconds = (long)(date.ToUniversalTime() - new DateTime(1970, 1, 1)).TotalSeconds;
|
||||
|
||||
string result = JsonConvert.SerializeObject(date, new UnixDateTimeConverter { AllowPreEpoch = true });
|
||||
|
||||
Assert.AreEqual(dateSeconds.ToString(CultureInfo.InvariantCulture), result);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void WriteJsonInvalidType()
|
||||
{
|
||||
@@ -159,6 +171,19 @@ namespace Newtonsoft.Json.Tests.Converters
|
||||
"Cannot convert invalid value to System.DateTimeOffset. Path '', line 1, position 5."
|
||||
);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void DeserializeDateTimeOffsetBeforeEpoch()
|
||||
{
|
||||
UnixDateTimeConverter converter = new UnixDateTimeConverter(true);
|
||||
DateTimeOffset d = new DateTimeOffset(1969, 2, 1, 20, 6, 18, TimeSpan.Zero);
|
||||
|
||||
string json = JsonConvert.SerializeObject(d, converter);
|
||||
|
||||
DateTimeOffset result = JsonConvert.DeserializeObject<DateTimeOffset>(json, converter);
|
||||
|
||||
Assert.AreEqual(new DateTimeOffset(1969, 2, 1, 20, 6, 18, TimeSpan.Zero), result);
|
||||
}
|
||||
#endif
|
||||
|
||||
[Test]
|
||||
@@ -186,6 +211,14 @@ namespace Newtonsoft.Json.Tests.Converters
|
||||
);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void DeserializeNegativeIntegerToDateTimeBeforeEpoch()
|
||||
{
|
||||
DateTime result = JsonConvert.DeserializeObject<DateTime>("-1514840476", new UnixDateTimeConverter(true));
|
||||
|
||||
Assert.AreEqual(new DateTime(1921, 12, 31, 02, 58, 44, DateTimeKind.Utc), result);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void DeserializeInvalidValueType()
|
||||
{
|
||||
@@ -263,6 +296,49 @@ namespace Newtonsoft.Json.Tests.Converters
|
||||
Assert.IsNull(obj2.Object2);
|
||||
Assert.AreEqual(new DateTime(2018, 1, 1, 21, 1, 16, DateTimeKind.Utc), obj2.ObjectNotHandled);
|
||||
}
|
||||
|
||||
#if !NET20
|
||||
[Test]
|
||||
public void ConverterObjectWithDatesBeforeEpoch()
|
||||
{
|
||||
PreEpochUnixConverterObject obj1 = new PreEpochUnixConverterObject
|
||||
{
|
||||
Date1 = new DateTime(1969, 1, 1, 0, 0, 3, DateTimeKind.Utc),
|
||||
Date2 = new DateTimeOffset(1969, 1, 1, 0, 0, 3, TimeSpan.Zero)
|
||||
};
|
||||
|
||||
string json = JsonConvert.SerializeObject(obj1, Formatting.Indented);
|
||||
StringAssert.AreEqual(@"{
|
||||
""Date1"": -31535997,
|
||||
""Date2"": -31535997
|
||||
}", json);
|
||||
|
||||
PreEpochUnixConverterObject obj2 = JsonConvert.DeserializeObject<PreEpochUnixConverterObject>(json);
|
||||
Assert.IsNotNull(obj2);
|
||||
|
||||
Assert.AreEqual(new DateTime(1969, 1, 1, 0, 0, 3, DateTimeKind.Utc), obj2.Date1);
|
||||
Assert.AreEqual(new DateTimeOffset(1969, 1, 1, 0, 0, 3, TimeSpan.Zero), obj2.Date2);
|
||||
}
|
||||
#else
|
||||
[Test]
|
||||
public void ConverterObjectWithDatesBeforeEpoch()
|
||||
{
|
||||
PreEpochUnixConverterObject obj1 = new PreEpochUnixConverterObject
|
||||
{
|
||||
Date1 = new DateTime(1969, 1, 1, 0, 0, 3, DateTimeKind.Utc)
|
||||
};
|
||||
|
||||
string json = JsonConvert.SerializeObject(obj1, Formatting.Indented);
|
||||
StringAssert.AreEqual(@"{
|
||||
""Date1"": -31535997
|
||||
}", json);
|
||||
|
||||
PreEpochUnixConverterObject obj2 = JsonConvert.DeserializeObject<PreEpochUnixConverterObject>(json);
|
||||
Assert.IsNotNull(obj2);
|
||||
|
||||
Assert.AreEqual(new DateTime(1969, 1, 1, 0, 0, 3, DateTimeKind.Utc), obj2.Date1);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
[JsonArray(ItemConverterType = typeof(UnixDateTimeConverter))]
|
||||
@@ -281,4 +357,15 @@ namespace Newtonsoft.Json.Tests.Converters
|
||||
[JsonConverter(typeof(UnixDateTimeConverter))]
|
||||
public object ObjectNotHandled { get; set; }
|
||||
}
|
||||
|
||||
public class PreEpochUnixConverterObject
|
||||
{
|
||||
[JsonConverter(typeof(UnixDateTimeConverter), true)]
|
||||
public DateTime Date1 { get; set; }
|
||||
|
||||
#if !NET20
|
||||
[JsonConverter (typeof(UnixDateTimeConverter), true)]
|
||||
public DateTimeOffset Date2 { get; set; }
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@@ -36,6 +36,37 @@ namespace Newtonsoft.Json.Converters
|
||||
{
|
||||
internal static readonly DateTime UnixEpoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the dates before Unix epoch
|
||||
/// should converted to and from JSON.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// <c>true</c> to allow converting dates before Unix epoch to and from JSON;
|
||||
/// <c>false</c> to throw an exception when a date being converted to or from JSON
|
||||
/// occurred before Unix epoch. The default value is <c>false</c>.
|
||||
/// </value>
|
||||
public bool AllowPreEpoch { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="UnixDateTimeConverter"/> class.
|
||||
/// </summary>
|
||||
public UnixDateTimeConverter() : this(false)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="UnixDateTimeConverter"/> class.
|
||||
/// </summary>
|
||||
/// <param name="allowPreEpoch">
|
||||
/// <c>true</c> to allow converting dates before Unix epoch to and from JSON;
|
||||
/// <c>false</c> to throw an exception when a date being converted to or from JSON
|
||||
/// occurred before Unix epoch. The default value is <c>false</c>.
|
||||
/// </param>
|
||||
public UnixDateTimeConverter(bool allowPreEpoch)
|
||||
{
|
||||
AllowPreEpoch = allowPreEpoch;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes the JSON representation of the object.
|
||||
/// </summary>
|
||||
@@ -61,7 +92,7 @@ namespace Newtonsoft.Json.Converters
|
||||
throw new JsonSerializationException("Expected date object value.");
|
||||
}
|
||||
|
||||
if (seconds < 0)
|
||||
if (!AllowPreEpoch && seconds < 0)
|
||||
{
|
||||
throw new JsonSerializationException("Cannot convert date value that is before Unix epoch of 00:00:00 UTC on 1 January 1970.");
|
||||
}
|
||||
@@ -108,7 +139,7 @@ namespace Newtonsoft.Json.Converters
|
||||
throw JsonSerializationException.Create(reader, "Unexpected token parsing date. Expected Integer or String, got {0}.".FormatWith(CultureInfo.InvariantCulture, reader.TokenType));
|
||||
}
|
||||
|
||||
if (seconds >= 0)
|
||||
if (AllowPreEpoch || seconds >= 0)
|
||||
{
|
||||
DateTime d = UnixEpoch.AddSeconds(seconds);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user