using System.Text.Json; using System.Text.Json.Serialization; using System.Text.RegularExpressions; namespace GB5Shared.UnixEpochDate { public sealed class MicrosoftEpochDateTimeConverter : JsonConverter { private static readonly Regex _regex = new(@"^/Date\((\-?\d+)\)/$", RegexOptions.Compiled); public override DateTime Read( ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { if (reader.TokenType == JsonTokenType.Null) return default; if (reader.TokenType != JsonTokenType.String) return default; var value = reader.GetString(); if (string.IsNullOrWhiteSpace(value)) return default; // Try Microsoft format: /Date(123456789)/ var match = _regex.Match(value); if (match.Success) { var ms = long.Parse(match.Groups[1].Value); return DateTimeOffset .FromUnixTimeMilliseconds(ms) .UtcDateTime; } // Try ISO 8601 (2025-03-01T00:00:00 / with Z) if (DateTime.TryParse(value, out var parsed)) return parsed; // /Date(NaN)/ or any other unrecognized format — return default instead of throwing return default; } public override void Write( Utf8JsonWriter writer, DateTime value, JsonSerializerOptions options) { var epochMs = new DateTimeOffset( value.ToUniversalTime()) .ToUnixTimeMilliseconds(); writer.WriteStringValue($"/Date({epochMs})/"); } } public sealed class MicrosoftEpochNullableDateTimeConverter : JsonConverter { private static readonly MicrosoftEpochDateTimeConverter _inner = new(); public override DateTime? Read( ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { if (reader.TokenType == JsonTokenType.Null) return null; return _inner.Read(ref reader, typeof(DateTime), options); } public override void Write( Utf8JsonWriter writer, DateTime? value, JsonSerializerOptions options) { if (!value.HasValue) { writer.WriteNullValue(); return; } _inner.Write(writer, value.Value, options); } } }