Сегодня начнём про приём дат во входящих запросах 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
How to Easily Find YouTube Videos: A Comprehensive Guide