您能否告诉 JSON.Net 即使未指定也将日期时间序列化为 UTC

Can you tell JSON.Net to serialize DateTime as Utc even if unspecified?

本文关键字:日期 时间 时间序列 UTC 序列化 未指定 JSON Net      更新时间:2023-09-26

数据库中的日期存储为 Utc。但是当我使用实体框架检索它们时,它们显示为未指定类型。

当 JSON.Net 序列化它们时,它们不是 UTC 格式。有没有办法告诉 JSON.Net 将日期时间序列化为 UTC,即使它们的类型未指定为 Utc?

DateTimeZoneHandling设置为JsonSerializerSettingsUtc 。这会在序列化之前将所有日期转换为 UTC。

public void SerializeObjectDateTimeZoneHandling()
{
  string json = JsonConvert.SerializeObject(
    new DateTime(2000, 1, 1, 1, 1, 1, DateTimeKind.Unspecified),
    new JsonSerializerSettings
    {
      DateTimeZoneHandling = DateTimeZoneHandling.Utc
    });
  Assert.AreEqual(@"""2000-01-01T01:01:01Z""", json);
}

文档:日期时区处理设置

上面的响应完全有效,所以我使用它来创建一个属性来将 API 响应从 PST 转换为 UTC。

首先,我需要创建一个JsonConverter

public class UTCDateTimeConverter : Newtonsoft.Json.JsonConverter {
    private TimeZoneInfo pacificZone = TimeZoneInfo.FindSystemTimeZoneById("Pacific Standard Time");
    public override bool CanConvert(Type objectType) {
        return objectType == typeof(DateTime);
    }
    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) {
        if (reader.Value == null) return null;
        var pacificTime = DateTime.Parse(reader.Value.ToString());
        return TimeZoneInfo.ConvertTimeToUtc(pacificTime, pacificZone);
    }
    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) {
        writer.WriteValue(TimeZoneInfo.ConvertTimeFromUtc((DateTime) value, pacificZone));
    }
}

然后我必须将其应用于需要转换的属性

public class Order{
    [JsonConverter(typeof(UTCDateTimeConverter))]
    public DateTime OrderDate {get;set;}
}

正如@dez注释中提到的,您可以在从数据库加载 DateTime 对象并在序列化它们之前直接在 .net 代码中将 DateTime 对象"标记为"UTC:

var item = GetItemFromDb(...);
// mark appropriate DateTime fields manually as needed
item.OrderDate = DateTime.SpecifyKind(item.OrderDate, DateTimeKind.Utc);
// now it will be serialized to "2018-10-17T16:21:23.507Z" with the Z at the end
// and javascript will parse it properly and convert to local timezone as needed

对我来说,为 DateTime 属性创建 UTC 转换器更简单(基于 Newtonsoft.Json.Converters.IsoDateTimeConverter 的实现)。

public class UtcJsonDateTimeConverter : DateTimeConverterBase
{
    private const string DefaultDateTimeFormat = "yyyy'-'MM'-'dd'T'HH':'mm':'ss.FFFFFFFZ";
    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        string text;
        if (value is DateTime dateTime)
        {
            text = dateTime.ToString(DefaultDateTimeFormat, CultureInfo.InvariantCulture);
        }
        else
        {
            throw new JsonSerializationException(
                $"Unexpected value when converting date. Expected DateTime or DateTimeOffset, got {value.GetType()}.");
        }
        writer.WriteValue(text);
    }
    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        bool nullable = objectType == typeof(DateTime?);
        if (reader.TokenType == JsonToken.Null)
        {
            if (!nullable)
            {
                throw new JsonSerializationException($"Cannot convert null value to {objectType}.");
            }
            return null;
        }
        if (reader.TokenType == JsonToken.Date)
        {
            return reader.Value;
        }
        else if (reader.TokenType != JsonToken.String)
        {
            throw new JsonSerializationException($"Unexpected token parsing date. Expected String, got {reader.TokenType}.");
        }
        string date_text = reader.Value.ToString();
        if (string.IsNullOrEmpty(date_text) && nullable)
        {
            return null;
        }
        return DateTime.Parse(date_text, CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal);
    }
}
public class SomeEntity
{
   [JsonProperty(PropertyName = "id", Order = 1)]
   public int ID { get; set; }
   [JsonProperty(PropertyName = "created", Order = 2)]
   [JsonConverter(typeof(UtcJsonDateTimeConverter))]
   public DateTime Created { get; set; }
}

我使用了公认的答案,但应用于默认设置:

        JsonConvert.DefaultSettings = (() =>
        {
            var settings = new JsonSerializerSettings();
            settings.Converters.Add(new StringEnumConverter());
            settings.Formatting = Formatting.Indented;
            settings.DateTimeZoneHandling = DateTimeZoneHandling.Utc;
            return settings;
        });