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
37 changes: 32 additions & 5 deletions src/Marten.NodaTime.Testing/Acceptance/noda_time_acceptance.cs
Original file line number Diff line number Diff line change
Expand Up @@ -227,9 +227,22 @@ public async Task can_append_and_query_events(SerializerType serializerType)
[InlineData(SerializerType.Newtonsoft)]
public async Task bug_1276_can_select_instant(SerializerType serializerType)
{
return; // TODO -- FIX THIS
StoreOptions(opts =>
{
switch (serializerType)
{
case SerializerType.Newtonsoft:
opts.UseNewtonsoftForSerialization();
break;
case SerializerType.SystemTextJson:
opts.UseSystemTextJsonForSerialization();
break;
default:
throw new ArgumentOutOfRangeException(nameof(serializerType), serializerType, null);
}

StoreOptions(_ => _.UseNodaTime());
opts.UseNodaTime();
});

var dateTime = DateTime.UtcNow;
var instantUTC = Instant.FromDateTimeUtc(dateTime.ToUniversalTime());
Expand All @@ -243,18 +256,32 @@ public async Task bug_1276_can_select_instant(SerializerType serializerType)

using (var query = theStore.QuerySession())
{
var resulta = query.Query<TargetWithDates>()
var result = query.Query<TargetWithDates>()
.Where(c => c.Id == testDoc.Id)
.Single();

var result = query.Query<TargetWithDates>()
var resultWithSelect = query.Query<TargetWithDates>()
.Where(c => c.Id == testDoc.Id)
.Select(c => new { c.Id, c.InstantUTC })
.Select(c => new { c.Id, c.InstantUTC, c.NullableInstantUTC, c.NullInstantUTC })
.Single();

result.ShouldNotBeNull();
result.Id.ShouldBe(testDoc.Id);
result.InstantUTC.ShouldBe(instantUTC);
result.NullableInstantUTC.ShouldBe(instantUTC);
result.NullInstantUTC.ShouldBeNull();

ShouldBeEqualWithDbPrecision(result.InstantUTC, instantUTC);
ShouldBeEqualWithDbPrecision(result.NullableInstantUTC.Value, instantUTC);

resultWithSelect.ShouldNotBeNull();
resultWithSelect.Id.ShouldBe(testDoc.Id);
resultWithSelect.InstantUTC.ShouldBe(instantUTC);
resultWithSelect.NullableInstantUTC.ShouldBe(instantUTC);
resultWithSelect.NullInstantUTC.ShouldBeNull();

ShouldBeEqualWithDbPrecision(resultWithSelect.InstantUTC, instantUTC);
ShouldBeEqualWithDbPrecision(resultWithSelect.NullableInstantUTC.Value, instantUTC);
}
}

Expand Down
4 changes: 3 additions & 1 deletion src/Marten.NodaTime.Testing/TestData/TargetWithDates.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ public class TargetWithDates: IEquatable<TargetWithDates>
public LocalDateTime? NullableLocalDateTime { get; set; }
public Instant InstantUTC { get; set; }
public Instant? NullableInstantUTC { get; set; }
public Instant? NullInstantUTC { get; set; }

internal static TargetWithDates Generate(DateTime? defaultDateTime = null)
{
Expand All @@ -36,7 +37,8 @@ internal static TargetWithDates Generate(DateTime? defaultDateTime = null)
LocalDateTime = localDateTime,
NullableLocalDateTime = localDateTime,
InstantUTC = instant,
NullableInstantUTC = instant
NullableInstantUTC = instant,
NullInstantUTC = null
};
}

Expand Down
72 changes: 72 additions & 0 deletions src/Marten.NodaTime/InstantJsonConverter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
using System;
using System.Text.Json;
using Newtonsoft.Json;
using NodaTime;
using NodaTime.Serialization.SystemTextJson;
using NodaTime.Text;
using NodaTime.Utility;
using JsonSerializer = Newtonsoft.Json.JsonSerializer;

namespace Marten.NodaTimePlugin;

public class InstantJsonConverter
{
public static readonly StjConverter Stj = new();
public static readonly NewtonsoftConverter Newtonsoft = new();

private static readonly InstantPattern InstantIsoPattern = InstantPattern.ExtendedIso;

private static readonly OffsetDateTimePattern InstantOffsetPattern =
OffsetDateTimePattern.CreateWithInvariantCulture("uuuu'-'MM'-'dd'T'HH':'mm':'ss.FFFFFFo<G>");

public class StjConverter: NodaConverterBase<Instant>
{
protected override Instant ReadJsonImpl(ref Utf8JsonReader reader, JsonSerializerOptions options)
{
var text = reader.GetString()!;
return ParseInstant(text);
}

protected override void WriteJsonImpl(Utf8JsonWriter writer, Instant value, JsonSerializerOptions options)
{
writer.WriteStringValue(InstantIsoPattern.Format(value));
}
}

public class NewtonsoftConverter: NodaTime.Serialization.JsonNet.NodaConverterBase<Instant>
{
protected override Instant ReadJsonImpl(JsonReader reader, JsonSerializer serializer)
{
if (reader.TokenType != JsonToken.String)
{
throw new InvalidNodaDataException(
$"Unexpected token parsing {nameof(Instant)}. Expected String, got {reader.TokenType}.");
}

var text = reader.Value!.ToString();
return ParseInstant(text!);
}

protected override void WriteJsonImpl(JsonWriter writer, Instant value, JsonSerializer serializer)
{
writer.WriteValue(InstantIsoPattern.Format(value));
}
}

private static Instant ParseInstant(string text)
{
var isoParseResult = InstantIsoPattern.Parse(text);
if (isoParseResult.Success)
{
return isoParseResult.Value;
}

var offsetParseResult = InstantOffsetPattern.Parse(text);
if (offsetParseResult.Success)
{
return offsetParseResult.Value.ToInstant();
}

throw new AggregateException(isoParseResult.Exception, offsetParseResult.Exception);
}
}
11 changes: 9 additions & 2 deletions src/Marten.NodaTime/NodaTimeExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
using Npgsql;
using NpgsqlTypes;
using Weasel.Postgresql;
using NodaJsonSettings = NodaTime.Serialization.JsonNet.NodaJsonSettings;

namespace Marten.NodaTimePlugin;

Expand All @@ -34,13 +35,19 @@ public static void UseNodaTime(this StoreOptions storeOptions, bool shouldConfig
case JsonNetSerializer jsonNetSerializer:
jsonNetSerializer.Configure(s =>
{
s.ConfigureForNodaTime(DateTimeZoneProviders.Tzdb);
s.ConfigureForNodaTime(new NodaJsonSettings(DateTimeZoneProviders.Tzdb)
{
InstantConverter = InstantJsonConverter.Newtonsoft
});
});
break;
case SystemTextJsonSerializer systemTextJsonSerializer:
systemTextJsonSerializer.Configure(s =>
{
s.ConfigureForNodaTime(DateTimeZoneProviders.Tzdb);
s.ConfigureForNodaTime(new NodaTime.Serialization.SystemTextJson.NodaJsonSettings(DateTimeZoneProviders.Tzdb)
{
InstantConverter = InstantJsonConverter.Stj
});
});
break;
default:
Expand Down
Loading