【问题标题】:JSON Date and DateTime serialisation in c# & newtonsoftc# & newtonsoft 中的 JSON 日期和日期时间序列化
【发布时间】:2017-09-08 15:41:29
【问题描述】:

我们将 JSON 发送到一个由 swagger 定义的 API,其中一些属性是 DateTime,格式为 yyyy-MM-ddThh:mm:ss.000Z(毫秒必须是 3 位数字,否则在端点验证失败),有些是日期(无时间)属性。

我看到很多消息说使用这样的格式化程序:

var jsonSettings = new JsonSerializerSettings();
jsonSettings.DateFormatString = "yyyy-MM-ddThh:mm:ss.000Z"; //try .fffZ too
var jsonObject= Newtonsoft.Json.JsonConvert.DeserializeObject<OurSwaggerObject>(json , setting);

但这不会将 DateTimes 转换为正确的格式,C# 如何处理仅 Date 类型?它似乎总是序列化为 DateTime.MinValue()

这是一个例子:

有人将 json 作为字符串发送给我,但是格式不正确的日期和日期时间要发送到端点。我希望 swagger 类和 json 反序列化会格式化它们,但事实并非如此。

这是swagger生成的类

 public class OurSwaggerObject
    {
        [Newtonsoft.Json.JsonProperty("dateTimeField", Required = Newtonsoft.Json.Required.Always)]
        [System.ComponentModel.DataAnnotations.Required]
        [System.ComponentModel.DataAnnotations.RegularExpression(@"^\d{4}-\d\d-\d\dT\d\d:\d\d:\d\d\.\d{3}Z$")]
        public DateTime dateTimeField { get; set; }

        [Newtonsoft.Json.JsonProperty("dateField", Required = Newtonsoft.Json.Required.Always)]
        [System.ComponentModel.DataAnnotations.Required]
        [System.ComponentModel.DataAnnotations.RegularExpression(@"^\d{4}-\d\d-\d\d$")]
        public DateTime dateField { get; set; }
    }

所以我尝试强制 json 正确,但我做错了或缺少某些东西

string json = @"{ 'dateTimeField': '1995-04-07T00:00:00',
                          'dateField': '1995-04-07T00:00:00'
                           }";

        /* The json we need to satisfy the swagger endpoint is:

          { 'dateTimeField': '1995-04-07T00:00:00.000Z',
            'dateField': '1995-04-07'
                           }              
          */

        OurSwaggerObject deserialisedIntoObject = Newtonsoft.Json.JsonConvert.DeserializeObject<OurSwaggerObject>(json);

        string serialisedToString = Newtonsoft.Json.JsonConvert.SerializeObject(deserialisedIntoObject);
        //serialisedToString= "{\"dateTimeField\":\"1995-04-07T00:00:00\",\"dateField\":\"1995-04-07T00:00:00\"}"

        var jsonSettings = new JsonSerializerSettings();
        jsonSettings.DateFormatString = "yyyy-MM-ddThh:mm:ss.fffZ"; //this won't help much for the 'date' only field!
        deserialisedIntoObject = Newtonsoft.Json.JsonConvert.DeserializeObject<OurSwaggerObject>(json,jsonSettings);
        serialisedToString = Newtonsoft.Json.JsonConvert.SerializeObject(deserialisedIntoObject, jsonSettings);
        //serialisedToString="{\"dateTimeField\":\"1995-04-07T00:00:00\",\"dateField\":\"1995-04-07T00:00:00\"}"

【问题讨论】:

  • 请提供说明问题的mcve
  • Json.NET 从 4.5 版开始使用事实上的标准(当前是 10.x)。它确实正确地序列化日期。格式字符串仅用于 older 版本。发布实际显示问题的代码
  • 另一方面,如果另一个端点需要任意数量的毫秒,那么问题就出在他们身上。 ISO8601 不需要毫秒
  • 仅限日期 - Json 没有日期或时间。这就是为什么提到 defacto 标准的原因。在某些时候,人们只是决定开始使用 ISO8601。日期可以由 DateTime 值表示。您可以将其序列化为 Json,或者作为具有零小时、分钟的完整 ISO8601 字符串,或者作为仅日期字符串。都是有效的。
  • Swagger OpenAPi 确实定义了日期和日期时间 swagger.io/specification 我将发布一些代码示例

标签: c# json date datetime json.net


【解决方案1】:

正如我在评论中提到的,JSON 中没有标准的日期表示。 ISO8601 是de-facto 标准,即大多数人几年前就开始使用它。 ISO8601 不需要毫秒。如果另一个端点需要它们,那就违反了事实上的标准。

Json.NET 从 4.5 版本开始使用 IOS8601。当前版本是 10.0.3。以下代码:

JsonConvert.SerializeObject(DateTime.Now)

返回

"2017-09-08T19:01:55.714942+03:00"

在我的机器上。注意时区偏移。这也是标准的一部分。 Z 表示 UTC。

您可以指定您自己的时间格式,前提是它是正确的。在这种情况下,它应该是yyyy-MM-ddTHH:mm:ss.fffZ。注意fff 表示毫秒和 HH 表示 24 小时。

以下代码

var settings=new JsonSerializerSettings{DateFormatString ="yyyy-MM-ddTHH:mm:ss.fffZ"};
var json=JsonConvert.SerializeObject(DateTime.Now,settings);

返回

"2017-09-08T19:04:14.480Z"

格式字符串不强制时区转换。您可以通过 DateTimeZoneHandling 设置告诉 Json.NET 将时间视为 Local 或 Utc:

var settings=new JsonSerializerSettings{
                              DateFormatString ="yyyy-MM-ddTH:mm:ss.fffZ",
                              DateTimeZoneHandling=DateTimeZoneHandling.Utc};
var json=JsonConvert.SerializeObject(DateTime.Now,settings);

返回:

"2017-09-08T16:08:19.290Z"

更新

正如 Matt Johnson 所解释的,Z 只是一个文字,而 K 生成 Z 或偏移量,具体取决于 DateTimeZoneHandling 设置。

格式字符串yyyy-MM-ddTH:mm:ss.fffK 与 DateTimeZoneHandling.Utc :

var settings=new JsonSerializerSettings{
                              DateFormatString ="yyyy-MM-ddTH:mm:ss.fffK",
                              DateTimeZoneHandling=DateTimeZoneHandling.Utc};
var json=JsonConvert.SerializeObject(DateTime.Now,settings);

将返回:

2017-09-11T9:10:08.293Z

更改为DateTimeZoneHandling.Utc 将返回

2017-09-11T12:15:12.862+03:00

顺便说一句,这是 Json.NET 的 默认 行为,除了强制的毫秒精度。

最后,.NET 还没有Date-only 类型还。 DateTime 用于日期和日期+时间值。您可以使用 DateTime.Date 属性获取 DateTime 的日期部分。您可以使用DateTime.Today 检索当前日期。

一天中的时间由 Timespan 类型表示。您可以使用 DateTime.TimeOfDay 从 DateTime 值中提取一天中的时间。 Timespan 严格来说不是时间类型,因为它可以表示超过 24 小时。

那是什么?

通过CoreFX Lab project 支持显式日期、TimeOfDay。这包含极可能出现在 .NET 运行时中的“实验性”功能,例如 UTF8 支持、日期、字符串、通道。其中一些已经显示为单独的 NuGet 包。

人们已经可以使用System.Time 类,方法是复制代码或通过实验性 NuGet 源添加它们

【讨论】:

  • Z 在格式字符串中是 literal。您应该使用K,它正确反映了与相关DateTime 关联的DateTimeKind。
  • 是的 - 我从来不用手工编码 ISO8601格式
  • 有趣的是,当我使用这个 var settings=new JsonSerializerSettings{DateFormatString ="yyyy-MM-ddTHH:mm:ss.fffZ"}; var json=JsonConvert.SerializeObject(DateTime.Now,settings); 它会产生这个 "effective": { "start": "\"2021-04-09T09:52:47.170Z\"", "end": "\"2023-04-08T09:52:47.170Z\"" }, 注意额外的语音标记和 \
【解决方案2】:

获取当前通用时间到 json 日期时间格式,反之亦然:

DateTime currentDateTime = DateTime.Now.ToUniversalTime();
var jsonDateTime = GetJSONFromUserDateTime(currentDateTime);
DateTime getDateTime = GetUserDateTimeFromJSON(jsonDateTime);

以下是两种方法:

/// <summary>
/// Convert UserDateTime({9/7/2018 8:37:20 AM}) to JSON datetime(1536309440373) format
/// </summary>
/// <param name="givenDateTime"></param>
/// <returns></returns>
public static string GetJSONFromUserDateTime(DateTime givenDateTime)
{
    string jsonDateTime = string.Empty;
    if (givenDateTime != null)
    {
        JsonSerializerSettings microsoftDateFormatSettings = new JsonSerializerSettings
        {
            DateFormatHandling = DateFormatHandling.MicrosoftDateFormat
        };
        jsonDateTime = JsonConvert.SerializeObject(givenDateTime, microsoftDateFormatSettings);
        jsonDateTime = jsonDateTime.Replace("\"\\/Date(", "").Replace(")\\/\"", "");
    }
    return jsonDateTime;
}

/// <summary>
/// Convert JSON datetime(1536309440373) to user datetime({9/7/2018 8:37:20 AM})
/// </summary>
/// <param name="jsonDateTime"></param>
/// <returns></returns>
public static dynamic GetUserDateTimeFromJSON(string jsonDateTime)
{
    dynamic userDateTime = null;
    if (!string.IsNullOrEmpty(jsonDateTime))
    {
        JsonSerializerSettings microsoftDateFormatSettings = new JsonSerializerSettings
        {
            DateFormatHandling = DateFormatHandling.MicrosoftDateFormat
        };
        userDateTime = JsonConvert.DeserializeObject("\"\\/Date(" + jsonDateTime + ")\\/\"", microsoftDateFormatSettings);
    }
    return userDateTime;
}

【讨论】:

  • 关于从 JSON 反序列化我可以补充一点,有时您会从 OpenweatherMap API 中获得较短的数字,在这种情况下,解决方案是将数字乘以 1000 并使用您的函数 GetUserDateTimeFromJSON。
【解决方案3】:

如果像 Willie Esteche 一样,您希望转换一个太低的数字,您可能已经获得了一个 Unix 日期时间值。他的解决方案确实有效,但从 .Net Framework 4.6 开始,有一种更简单的方法。

给定值 1500013000,首先使用 FromUnixTimeSeconds() 方法将其转换为 DateTimeOffset,然后简单地获取 DateTime 组件。

DateTime dt = DateTimeOffset.FromUnixTimeSeconds(1500013000).UtcDateTime;

转换回来(假设 UTC)是这样执行的:

long Udt = new DateTimeOffset(dt,TimeSpan.Zero).ToUnixTimeSeconds();

【讨论】:

    【解决方案4】:

    在我的例子中,我在 vb.net 中使用这个配置,它很容易翻译成 c#

    settings.NullValueHandling = NullValueHandling.Ignore
    settings.DateTimeZoneHandling = DateTimeZoneHandling.Local
    BE = JsonConvert.DeserializeObject(Of EN_BE)(str, settings)
    

    我从 laravel API 得到这个格式日期

    2020-08-02T01:41:05.000000Z 
    

    我转换为与数据库相同的正确值

    2020-08-01 20:41:05
    

    【讨论】:

      猜你喜欢
      • 2010-10-14
      • 1970-01-01
      • 1970-01-01
      • 2012-08-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-11-29
      • 1970-01-01
      相关资源
      最近更新 更多