1. Форматирование дат
Если вы думаете, что вывод 2025-06-19T17:30:00 выглядит довольно стильно, попробуйте показать такой формат своей бабушке или бухгалтеру. Скорее всего, они предпочтут что-то попроще: 19.06.2025 17:30. А вот англоязычные коллеги ждут 06/19/2025 5:30 PM. Девопсы и машины вообще любят 2025-06-19T17:30:00Z. Так что один DateTime в памяти программы может быть представлен десятками форматов на экране.
Обратная задача тоже классическая: пользователь вводит дату в поле ввода ("19.06.2025"), а мы должны распознать ее в объект C# и не запутаться, где день, а где месяц. Автоматизация, интеграции, отчеты — везде нужна правильная интерпретация текстовых дат.
Базовое форматирование
C# позволяет преобразовывать объект даты/времени (DateTime, DateOnly, TimeOnly, DateTimeOffset) в строку с помощью метода .ToString():
DateTime now = DateTime.Now;
Console.WriteLine(now.ToString()); // Вывод: 19.06.2025 17:30:25
Если вызвать .ToString() без параметров — будет использоваться формат, характерный для текущей культуры системы (например, немецкая Windows покажет один формат, американская — другой).
Стандартные форматные строки
Можно явно указать формат вывода, используя так называемые стандартные форматные строки для даты и времени:
| Формат | Описание | Пример вывода |
|---|---|---|
|
Краткая дата | 19.06.2025 |
|
Полная дата | 19 июня 2025 г. |
|
Полная дата + короткое время | 19 июня 2025 г. 17:30 |
|
Полная дата + полное время | 19 июня 2025 г. 17:30:25 |
|
Краткая дата и время | 19.06.2025 17:30 |
|
Краткая дата + полное время | 19.06.2025 17:30:25 |
|
Короткое время | 17:30 |
|
Полное время | 17:30:25 |
|
Месяц и день | 19 июня |
|
Год и месяц | Июнь 2025 г. |
|
ISO 8601 (круглый триппер) | 2025-06-19T17:30:25.0000000 |
Console.WriteLine(now.ToString("d")); // 19.06.2025
Console.WriteLine(now.ToString("F")); // 19 июня 2025 г. 17:30:25
Console.WriteLine(now.ToString("O")); // 2025-06-19T17:30:25.0000000
Кастомные (пользовательские) шаблоны
Если нужны более специфичные форматы, можно использовать свои шаблоны:
| Символ | Значение |
|---|---|
| yyyy | Год, 4 цифры |
| yy | Год, 2 цифры |
| MM | Месяц, 2 цифры |
| MMMM | Название месяца |
| dd | День, 2 цифры |
| d | День, 1-2 цифры |
| HH | Час (24ч) |
| mm | Минуты |
| ss | Секунды |
| tt | AM/PM |
Console.WriteLine(now.ToString("yyyy-MM-dd HH:mm")); // 2025-06-19 17:30
Console.WriteLine(now.ToString("dd.MM.yyyy")); // 19.06.2025
Console.WriteLine(now.ToString("dddd, MMMM d")); // среда, июня 19
Культурные различия в форматировании
Если вы хотите получить результат в стиле другой страны или языка, используйте перегрузку ToString(string, IFormatProvider):
var enUS = new System.Globalization.CultureInfo("en-US");
Console.WriteLine(now.ToString("D", enUS)); // June 19, 2025
- Если жёстко задать шаблон вида "MM/dd/yyyy", то вне зависимости от культуры вы всё равно получите "06/19/2025".
- Но если используете только "d" или "D" — формат зависит от культуры!
Важные моменты при работе с форматированием
При работе с форматированием дат важно понимать разницу между стандартными и кастомными форматами. Стандартные форматы (как "d", "F", "G") автоматически адаптируются под культуру системы, что удобно для пользовательского интерфейса, но может создавать проблемы при обмене данными между системами. Кастомные форматы дают полный контроль над выводом, но требуют более детальной настройки.
Особое внимание стоит уделить формату "O" (или "o") — это так называемый "round-trip" формат, который гарантирует, что дата, преобразованная в строку и обратно, останется точно такой же. Этот формат особенно важен при сериализации данных или передаче их по сети.
2. Парсинг дат и времени: как из строки получить объект
Простейший парсинг: DateTime.Parse
DateTime.Parse — метод, который пытается распознать строку с датой с учетом культуры системы.
string input = "19.06.2025";
DateTime parsed = DateTime.Parse(input);
Console.WriteLine(parsed); // 19.06.2025 00:00:00
Если строка некорректна — программа упадёт с исключением (никто не застрахован!).
Указание культуры
string input = "06/19/2025";
var enUS = new System.Globalization.CultureInfo("en-US");
DateTime dt = DateTime.Parse(input, enUS);
Console.WriteLine(dt); // 19.06.2025 00:00:00
Безопасный парсинг: TryParse
string input = "неправильная дата";
bool ok = DateTime.TryParse(input, out DateTime safeDate);
if (!ok)
Console.WriteLine("Ошибка: не удалось распознать дату!");
Жёсткое указание формата: ParseExact и TryParseExact
var culture = System.Globalization.CultureInfo.InvariantCulture;
string dateStr = "2025-06-19";
DateTime d = DateTime.ParseExact(dateStr, "yyyy-MM-dd", culture);
Console.WriteLine(d); // 19.06.2025 00:00:00
bool parsedOk = DateTime.TryParseExact(
"19.06.2025",
"dd.MM.yyyy",
System.Globalization.CultureInfo.InvariantCulture,
System.Globalization.DateTimeStyles.None,
out DateTime myDate);
Таблица популярных форматов
| Строка | Формат | Итоговый объект |
|---|---|---|
| "2025-06-19" | "yyyy-MM-dd" | 19 июня 2025 |
| "19.06.2025" | "dd.MM.yyyy" | 19 июня 2025 |
| "06/19/2025" | "MM/dd/yyyy" | 19 июня 2025 |
| "2025-06-19T14:15:16" | "s" | 19 июня 2025 14:15:16 |
Работа с DateTimeStyles
При парсинге можно дополнительно управлять поведением через параметр DateTimeStyles. Этот enum позволяет настроить, как парсер должен интерпретировать входные данные. Например, DateTimeStyles.AssumeUniversal заставляет парсер считать время UTC, если смещение не указано явно. DateTimeStyles.AllowWhiteSpaces позволяет игнорировать лишние пробелы в строке. Эти настройки особенно полезны при работе с данными из внешних источников, где формат может быть не совсем предсказуемым.
3. Форматирование и парсинг DateOnly, TimeOnly
Новые типы DateOnly и TimeOnly сейчас часто используются для хранения дат/времени без лишних деталей.
Форматирование
DateOnly birthday = new DateOnly(2000, 6, 19);
Console.WriteLine(birthday.ToString("dd MMMM yyyy")); // 19 июня 2000
Парсинг
var d = DateOnly.ParseExact("19.06.2000", "dd.MM.yyyy");
Console.WriteLine(d.Day); // 19
Точно так же работают и методы для TimeOnly (разве что шаблоны — только про часы/минуты/секунды):
TimeOnly t = TimeOnly.ParseExact("23:59", "HH:mm");
Console.WriteLine(t.Hour); // 23
Преимущества DateOnly и TimeOnly
Использование DateOnly и TimeOnly вместо DateTime дает несколько важных преимуществ. Во-первых, это семантическая ясность — если вам нужна только дата без времени (например, дата рождения), DateOnly явно показывает это намерение. Во-вторых, это помогает избежать проблем с часовыми поясами, которые могут возникнуть при работе с DateTime. В-третьих, эти типы занимают меньше места в памяти и базах данных.
Форматирование и парсинг с учетом часовых поясов
Если храните время с информацией о смещении — используйте DateTimeOffset. Это особенно важно для международных, распределённых систем. Все методы форматирования и парсинга аналогичны, только теперь можно управлять и смещением:
DateTimeOffset meeting = new DateTimeOffset(2025, 6, 19, 17, 30, 0, TimeSpan.FromHours(3));
Console.WriteLine(meeting.ToString("o")); // 2025-06-19T17:30:00.0000000+03:00
string input = "2025-06-19T17:30:00+03:00";
var parsedOffset = DateTimeOffset.Parse(input);
Console.WriteLine(parsedOffset.Offset); // 03:00:00
4. Практические замечания и типичные ошибки
Одна из ловушек — путать "день" и "месяц" при парсинге международных форматов. Например, строка "01/02/2025" в США — это 2 января, а в большинстве европейских стран — 1 февраля (сюрприз!).
Также часто забывают про влияние культуры: если приложение крутится на сервере в Германии, а пользователи — из разных стран, формат даты из базы может интерпретироваться иначе на каждом сервере.
Частая ошибка — использование .ToString() без указания культуры/формата для логов и файлов обмена: результат может измениться после миграции на другую ОС или если приложение будет крутиться под разным пользователем Windows.
Рекомендации по работе с датами
- Для внутреннего хранения и обмена данными всегда используйте инвариантную культуру (CultureInfo.InvariantCulture) или явно указанные форматы.
- Для пользовательского интерфейса применяйте культуру пользователя или дайте ему возможность выбрать предпочтительный формат.
- При работе с API и базами данных отдавайте предпочтение ISO 8601 формату, который является международным стандартом.
- Всегда валидируйте входные данные: проверяйте существование даты, диапазон и разумность значений.
Работа с различными источниками данных
При интеграции с внешними системами часто приходится работать с датами в различных форматах. Некоторые системы могут передавать даты в формате Unix timestamp (количество секунд с 1 января 1970 года), другие — в виде строк в национальных форматах. Важно заранее договориться о формате обмена данными и всегда проверять корректность парсинга.
Также стоит помнить, что некоторые форматы могут быть неоднозначными. Например, "12/13/2025" однозначно интерпретируется как 13 декабря 2025 года, но "12/11/2025" может быть как 12 ноября, так и 11 декабря в зависимости от культуры. В таких случаях лучше использовать менее двусмысленные форматы или явно указывать культуру парсинга.
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ