Как получить JSON.NET для сериализации даты и времени для ISO 8601?

У меня есть приложение Web API, которое возвращает JSON для потребителей, которые, возможно, не используют технологии Microsoft. Когда мой контроллер возвращает объект с свойствами DateTime как JSON, он сериализует дату в этом формате:

2017-03-15T00:00:00-04:00

Это дает потребителю немного головной боли, поскольку они ожидают, что он будет в формате ISO 8601. Некоторые исследования говорят мне, что JSON.NET теперь использует ISO 8601 по умолчанию (я использую 9.0.1). Когда я запускаю этот код...

Clipboard.Copy(JsonConvert.SerializeObject(DateTime.Now));

... Я получаю это:

2017-03-15T09:10:13.8105498-04:00

Википедия показывает их как действительные форматы ISO 8601 при выражении полной даты и времени:

2017-03-15T11:45:42+00:00
2017-03-15T11:45:42Z
20170315T114542Z

Однако вывод, который я получил выше, точно не соответствует ни одному из них. Я хочу, чтобы форматтер использовал 2017-03-15T11:45:42Z.

И, вероятно, достойный другого вопроса, добавление ниже строки в моей конфигурации веб-API кажется проигнорированным, поскольку оно продолжает возвращать JSON в дату, первоначально показанную выше:

config.Formatters.JsonFormatter.SerializerSettings.Converters.Add(new IsoDateTimeConverter());

Я предполагаю, что как только я выясню основную проблему, проблема веб-API также может быть решена.

Ответ 1

Формат, который вы получаете, - это формат ISO 8601 (см. раздел "Временные и часовые пояса" в Википедии), это просто, что ваши даты, по-видимому, не настроены на время UTC, поэтому вы получаете смещение часового пояса, добавленное к дате а не индикатором часового пояса Z Zulu.

IsoDateTimeConverter имеет настройки, которые вы можете использовать для настройки своего вывода. Вы можете автоматически настроить даты в UTC, установив DateTimeStyles в AdjustToUniversal. Вы также можете настроить выходной формат, чтобы опустить дробные секунды, если вы этого не хотите. По умолчанию преобразователь не настраивается на время UTC и включает в себя столько десятков значений точности, сколько доступно для секунд.

Попробуйте следующее:

IsoDateTimeConverter converter = new IsoDateTimeConverter
{
    DateTimeStyles = DateTimeStyles.AdjustToUniversal,
    DateTimeFormat = "yyyy'-'MM'-'dd'T'HH':'mm':'ssK"
};

config.Formatters.JsonFormatter.SerializerSettings.Converters.Add(converter);

Если ваши даты уже установлены в формате UTC, но DateTimeKind на них не установлен на Utc, как это должно быть (например, это Unspecified), тогда в идеале вы должны исправить свой код, чтобы этот индикатор был установлен правильно до сериализации. Однако, если вы не можете (или не хотите) этого делать, вы можете обойти это, изменив настройки преобразователя, чтобы всегда включать индикатор Z в формате даты (вместо использования спецификатора K, который смотрит на DateTimeKind на дату) и удаляет директиву AdjustToUniversal.

IsoDateTimeConverter converter = new IsoDateTimeConverter
{
    DateTimeFormat = "yyyy'-'MM'-'dd'T'HH':'mm':'ss'Z'"
};

Ответ 2

Добавление к ответу @Brian Rogers, для ASP Core добавьте в Startup.cs:

services.AddMvc()
  .SetCompatibilityVersion(CompatibilityVersion.Version_2_2)
  .AddJsonOptions(options =>
    options.SerializerSettings.Converters.Add(new IsoDateTimeConverter
    {
      DateTimeStyles = DateTimeStyles.AdjustToUniversal
    }));