using System;
using System.Text.Json;
using System.Text.Json.Serialization;
using System.Text.RegularExpressions;
namespace GB5Shared.DateConverter
{
///
/// Handles absolute instants in time (CreatedOn, ModifiedOn, audit timestamps).
/// READ : /Date(N)/ (legacy Angular) OR ISO 8601 string (with or without offset)
/// WRITE: "yyyy-MM-ddTHH:mm:ss.fffZ" (UTC, ISO 8601)
///
public sealed class Gb5DateTimeOffsetJsonConverter : JsonConverter
{
private static readonly Regex _msRegex =
new(@"^/Date\((-?\d+)([-+]\d{4})?\)/$", RegexOptions.Compiled);
public override DateTimeOffset Read(
ref Utf8JsonReader reader,
Type typeToConvert,
JsonSerializerOptions options)
{
if (reader.TokenType == JsonTokenType.Null)
return DateTimeOffset.MinValue;
if (reader.TokenType == JsonTokenType.Number &&
reader.TryGetInt64(out long epoch))
return DateTimeOffset.FromUnixTimeMilliseconds(epoch);
if (reader.TokenType == JsonTokenType.String)
{
var raw = reader.GetString();
if (string.IsNullOrWhiteSpace(raw))
return DateTimeOffset.MinValue;
if (raw.StartsWith("/Date(NaN", StringComparison.Ordinal))
return DateTimeOffset.UtcNow;
var match = _msRegex.Match(raw);
if (match.Success && long.TryParse(match.Groups[1].Value, out long ms))
return DateTimeOffset.FromUnixTimeMilliseconds(ms);
if (DateTimeOffset.TryParse(raw, out var dto))
return dto;
if (DateTime.TryParse(raw, out var dt))
return new DateTimeOffset(dt.Kind == DateTimeKind.Unspecified
? DateTime.SpecifyKind(dt, DateTimeKind.Utc)
: dt);
}
return DateTimeOffset.MinValue;
}
public override void Write(
Utf8JsonWriter writer,
DateTimeOffset value,
JsonSerializerOptions options)
{
if (value == DateTimeOffset.MinValue)
{
writer.WriteNullValue();
return;
}
writer.WriteStringValue(value.ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ss.fffZ"));
}
}
public sealed class NullableGb5DateTimeOffsetJsonConverter : JsonConverter
{
private static readonly Gb5DateTimeOffsetJsonConverter _inner = new();
public override DateTimeOffset? Read(
ref Utf8JsonReader reader,
Type typeToConvert,
JsonSerializerOptions options)
{
if (reader.TokenType == JsonTokenType.Null)
return null;
var val = _inner.Read(ref reader, typeof(DateTimeOffset), options);
return val == DateTimeOffset.MinValue ? null : val;
}
public override void Write(
Utf8JsonWriter writer,
DateTimeOffset? value,
JsonSerializerOptions options)
{
if (!value.HasValue)
{
writer.WriteNullValue();
return;
}
_inner.Write(writer, value.Value, options);
}
}
}