using System.Text.Json;
using System.Text.Json.Serialization;
namespace Web.Api.Common;
///
/// DB 의 모든 DateTime 컬럼은 UTC 로 저장된다(Domain Entity 가 일관되게 DateTime.UtcNow 사용).
/// SQL Server datetime2 컬럼은 timezone 정보가 없어 EF Core 가 Kind = Unspecified 로 materialize 한다.
/// System.Text.Json 의 기본 동작은 Unspecified DateTime 을 Z/offset 없이 출력해서, 클라이언트 JS 가
/// 로컬 시간으로 오해 → 9시간(KST 오프셋) 차이 발생. 이 converter 가 Z 마커를 강제해서 문제를 해결.
///
public sealed class UtcDateTimeConverter : JsonConverter
{
public override DateTime Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
var dt = reader.GetDateTime();
return dt.Kind == DateTimeKind.Utc ? dt : DateTime.SpecifyKind(dt, DateTimeKind.Utc);
}
public override void Write(Utf8JsonWriter writer, DateTime value, JsonSerializerOptions options)
{
var utc = value.Kind switch
{
DateTimeKind.Utc => value,
DateTimeKind.Local => value.ToUniversalTime(),
_ => DateTime.SpecifyKind(value, DateTimeKind.Utc)
};
writer.WriteStringValue(utc.ToString("yyyy-MM-ddTHH:mm:ss.fffZ"));
}
}
public sealed class UtcNullableDateTimeConverter : JsonConverter
{
private static readonly UtcDateTimeConverter 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 is null)
{
writer.WriteNullValue();
return;
}
Inner.Write(writer, value.Value, options);
}
}