using System; using System.Text.Json; using System.Text.Json.Serialization; using System.Text.RegularExpressions; namespace GB5Shared.UnixEpochDate { internal static class EpochDateTimeHelper { private static readonly Regex _msRegex = new(@"^/Date\((\-?\d+)\)/$", RegexOptions.Compiled); internal static DateTime NormalizeToUtc(DateTime value) { if (value == DateTime.MinValue || value == default) return DateTime.MinValue; return value.Kind switch { DateTimeKind.Utc => value, DateTimeKind.Local => value.ToUniversalTime(), DateTimeKind.Unspecified => DateTime.SpecifyKind(value, DateTimeKind.Utc), _ => value }; } internal static DateTime Parse(string? input) { if (string.IsNullOrWhiteSpace(input)) return DateTime.MinValue; // Strict /Date(123456)/ format var match = _msRegex.Match(input); if (match.Success && long.TryParse(match.Groups[1].Value, out long epoch)) { return DateTimeOffset .FromUnixTimeMilliseconds(epoch) .UtcDateTime; } // ISO fallback if (DateTime.TryParse(input, out DateTime parsed)) return NormalizeToUtc(parsed); return DateTime.MinValue; } } public sealed class EpochDateTimeConverter : JsonConverter { public override DateTime Read( ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { if (reader.TokenType == JsonTokenType.Null) return DateTime.MinValue; if (reader.TokenType == JsonTokenType.Number) { if (reader.TryGetInt64(out long epoch)) { return DateTimeOffset .FromUnixTimeMilliseconds(epoch) .UtcDateTime; } return DateTime.MinValue; } if (reader.TokenType == JsonTokenType.String) return EpochDateTimeHelper.Parse(reader.GetString()); return DateTime.MinValue; } public override void Write( Utf8JsonWriter writer, DateTime value, JsonSerializerOptions options) { if (value == DateTime.MinValue || value == default) { writer.WriteStringValue("/Date(0)/"); return; } var utc = EpochDateTimeHelper.NormalizeToUtc(value); long epoch = new DateTimeOffset(utc) .ToUnixTimeMilliseconds(); writer.WriteStringValue($"/Date({epoch})/"); } } public sealed class NullableEpochDateTimeConverter : JsonConverter { public override DateTime? Read( ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { if (reader.TokenType == JsonTokenType.Null) return null; var converter = new EpochDateTimeConverter(); return converter.Read(ref reader, typeof(DateTime), options); } public override void Write( Utf8JsonWriter writer, DateTime? value, JsonSerializerOptions options) { if (!value.HasValue) { writer.WriteNullValue(); return; } var converter = new EpochDateTimeConverter(); converter.Write(writer, value.Value, options); } } }