Сегодня начнём про приём дат во входящих запросах
ASP.NET - в частности, про приём дат в json-теле запроса.
За привязку моделей из тела запроса у нас отвечают
Input formatters.
Из коробки за привязку json отвечает
SystemTextJsonInputFormatterОн в свою очередь использует System.Text.Json, поэтому все особенности обработки дат, будут специфичны для этого сериализатора.
Есть статья описывающая как System.Text.Json работает с датами -
DateTime and DateTimeOffset support in System.Text.Json, но она рассказывает далеко не о всех особенностях десериализации, поэтому будем подсматривать в исходном коде.
Начнём с десериализации дат из тела запроса в поля с типом DateTime в моделях.
Дата поддерживается в формате
ISO 8601-1:2019Нас интересует как десериализатор обработает вот такие даты:
"2024-02-08T00:00:00+00:00"
"2024-02-08T00:00:00+01:00"
"2024-02-08T00:00:00:10Z"
"2024-02-08T00:00:00"
"2024-02-08"
В исходном коде сначала выходим на
TryGetDateTime в
Utf8JsonReader а затем на
JsonHelper.TryParseAsISO()Правила следующие:
- Если есть offset, то дата создаётся c Kind Local и приводится к локальной дате согласно текущему часовому поясу системы.
- Если Z - то дата создаётся с Kind UTC
- В остальных случаях Kind Unspecified и время из присланной даты или 00:00:00, если времени не было
В первом случае с offset возможна потеря дня из-за часовых поясов.
var jsonWithOffset0 = "{ \"Date\":\"2024-02-08T00:00:00+00:00\"}";
var jsonWithOffset1 = "{ \"Date\":\"2024-02-08T00:00:00+01:00\"}";
var jsonZ = "{ \"Date\":\"2024-02-08T00:00:00Z\"}";
var jsonDateTime = "{ \"Date\":\"2024-02-08T00:00:00\"}";
var jsonOnlyDate = "{ \"Date\":\"2024-02-08\"}";
Console.WriteLine($"Local TimeZone: {TimeZoneInfo.Local}");
var resultJsonWithOffset0 = System.Text.Json.JsonSerializer.Deserialize<DateTimeModel>(jsonWithOffset0);
Console.WriteLine(GetDateTimeString(resultJsonWithOffset0.Date));
var resultJsonWithOffset1 = System.Text.Json.JsonSerializer.Deserialize<DateTimeModel>(jsonWithOffset1);
Console.WriteLine(GetDateTimeString(resultJsonWithOffset1.Date));
var resultJsonZ = System.Text.Json.JsonSerializer.Deserialize<DateTimeModel>(jsonZ);
Console.WriteLine(GetDateTimeString(resultJsonZ.Date));
var resultJsonDateTime = System.Text.Json.JsonSerializer.Deserialize<DateTimeModel>(jsonDateTime);
Console.WriteLine(GetDateTimeString(resultJsonDateTime.Date));
var resultJsonOnlyDate = System.Text.Json.JsonSerializer.Deserialize<DateTimeModel>(jsonOnlyDate);
Console.WriteLine(GetDateTimeString(resultJsonOnlyDate.Date));
static string GetDateTimeString(DateTime dateTime, string json) => $"{dateTime} {dateTime.Kind} ({json})";
class DateTimeModel
{
public DateTime Date { get; set; }
}
Вывод:
Local TimeZone: (UTC) Время в формате UTC
08.02.2024 0:00:00 Local ({ "Date":"2024-02-08T00:00:00+00:00"})
07.02.2024 23:00:00 Local ({ "Date":"2024-02-08T00:00:00+01:00"})
08.02.2024 0:00:00 Utc ({ "Date":"2024-02-08T00:00:00Z"})
08.02.2024 0:00:00 Unspecified ({ "Date":"2024-02-08T00:00:00"})
08.02.2024 0:00:00 Unspecified ({ "Date":"2024-02-08"})
Во втором примере -
2024-02-08T00:00:00+01:00
при локальном часовом поясе +0 (время в UTC) мы во входящем запросе будем получать дату
07.02.2024 23:00:00
@boyarincevdotnet