Как преобразовать объект DateTime в строку формата ISO 8601 в C#?
Основное решение: использование спецификатора "O" и DateTimeKind
Проблема в вашем коде связана с тем, что объект DateTime не содержит информации о часовом поясе. По умолчанию DateTime.Now имеет тип DateTimeKind.Local, а new DateTime(...) — DateTimeKind.Unspecified. Для корректного форматирования ISO 8601 нужно явно указать часовой пояс.
Стандартный подход (рекомендуемый)
Используйте спецификатор формата "O" (круглая буква O, а не ноль), который соответствует стандарту ISO 8601. Однако он работает правильно только если у DateTime установлено свойство Kind:
// Для UTC
DateTime utcDate = new DateTime(2023, 10, 5, 14, 30, 0, DateTimeKind.Utc);
string isoUtc = utcDate.ToString("O"); // "2023-10-05T14:30:00.0000000Z"
// Для локального времени с часовым поясом
DateTime localDate = new DateTime(2023, 10, 5, 14, 30, 0, DateTimeKind.Local);
string isoLocal = localDate.ToString("O"); // "2023-10-05T14:30:00.0000000+03:00" (зависит от вашего пояса)
// Для указания произвольного смещения используйте DateTimeOffset
DateTimeOffset dto = new DateTimeOffset(2023, 10, 5, 14, 30, 0, TimeSpan.FromHours(3));
string isoOffset = dto.ToString("O"); // "2023-10-05T14:30:00.0000000+03:00"
Пользовательский формат (если нужно убрать миллисекунды)
Если API не принимает дробные секунды, используйте кастомный формат:
// Без миллисекунд (UTC)
string isoShort = utcDate.ToString("yyyy-MM-ddTHH:mm:ssZ"); // "2023-10-05T14:30:00Z"
// Без миллисекунд (с часовым поясом)
string isoShortOffset = dto.ToString("yyyy-MM-ddTHH:mm:ssK"); // "2023-10-05T14:30:00+03:00"
Важные моменты
- Всегда работайте с UTC для международных систем. Используйте
DateTime.UtcNowи конвертируйте в UTC перед сериализацией. - Для JSON-сериализации лучше использовать
Newtonsoft.JsonилиSystem.Text.Json— они корректно обрабатывают ISO 8601 по умолчанию. - Если вы получаете дату из БД, проверьте, что она загружается как
DateTimeс правильнымKind. Для SQL Server используйтеdatetime2или настройте Entity Framework.
Пример для реального API
// Исходная дата из БД (Kind = Unspecified)
DateTime dbDate = new DateTime(2023, 10, 5, 14, 30, 0);
// Явно указываем, что это UTC
DateTime utcDate = DateTime.SpecifyKind(dbDate, DateTimeKind.Utc);
// Форматируем в ISO 8601
string result = utcDate.ToString("O"); // "2023-10-05T14:30:00.0000000Z"
// Отправляем в JSON
var json = JsonSerializer.Serialize(new { timestamp = result });
<<Альтернативный подход: использование DateTimeOffset
Если вы часто работаете с разными часовыми поясами, рекомендую полностью перейти на DateTimeOffset вместо DateTime. Этот тип всегда содержит информацию о смещении от UTC, что делает его идеальным для ISO 8601:
// Создаём дату с конкретным смещением
DateTimeOffset dto = new DateTimeOffset(2023, 10, 5, 14, 30, 0, TimeSpan.FromHours(3));
string isoString = dto.ToString("O"); // "2023-10-05T14:30:00.0000000+03:00"
// Текущее время в UTC
DateTimeOffset utcNow = DateTimeOffset.UtcNow;
string isoUtcNow = utcNow.ToString("O"); // "2023-10-05T14:30:00.0000000+00:00"
Дополнительные советы
- Для сериализации в JSON используйте
System.Text.Jsonс настройкойJsonSerializerDefaults.Web— он автоматически форматируетDateTimeOffsetв ISO 8601. - Избегайте использования
DateTime.Now— всегда предпочитайтеDateTimeOffset.NowилиDateTime.UtcNow. - Если нужно только время без даты, рассмотрите
TimeOnly(C# 10+).
Почему это лучше? DateTimeOffset однозначно определяет момент времени, в то время как DateTime может интерпретироваться по-разному в зависимости от контекста. Это особенно важно для API, которые работают с клиентами из разных часовых поясов.