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); } } }