1. Formatowanie dat
Jeśli myślisz, że wyświetlanie 2025-06-19T17:30:00 wygląda całkiem spoko, spróbuj pokazać taki format swojej babci albo księgowej. Najpewniej woleliby coś prostszego: 19.06.2025 17:30. A anglojęzyczni koledzy oczekują 06/19/2025 5:30 PM. DevOpsi i maszyny w ogóle kochają 2025-06-19T17:30:00Z. Więc jeden DateTime w pamięci programu może być pokazany na ekranie w dziesiątkach różnych formatów.
Odwrotne zadanie też jest klasykiem: użytkownik wpisuje datę w polu ("19.06.2025"), a my musimy rozpoznać ją jako obiekt C# i nie pomylić dnia z miesiącem. Automatyzacja, integracje, raporty — wszędzie trzeba poprawnie interpretować tekstowe daty.
Podstawowe formatowanie
C# pozwala zamienić obiekt daty/czasu (DateTime, DateOnly, TimeOnly, DateTimeOffset) na stringa przez metodę .ToString():
DateTime now = DateTime.Now;
Console.WriteLine(now.ToString()); // Wyjście: 19.06.2025 17:30:25
Jeśli wywołasz .ToString() bez parametrów — zostanie użyty format charakterystyczny dla aktualnej kultury systemu (np. niemiecki Windows pokaże jeden format, amerykański — inny).
Standardowe formaty
Możesz jawnie wskazać format wyjścia, używając tzw. standardowych formatów dla daty i czasu:
| Format | Opis | Przykład wyjścia |
|---|---|---|
|
Krótka data | 19.06.2025 |
|
Pełna data | 19 czerwca 2025 r. |
|
Pełna data + krótki czas | 19 czerwca 2025 r. 17:30 |
|
Pełna data + pełny czas | 19 czerwca 2025 r. 17:30:25 |
|
Krótka data i czas | 19.06.2025 17:30 |
|
Krótka data + pełny czas | 19.06.2025 17:30:25 |
|
Krótki czas | 17:30 |
|
Pełny czas | 17:30:25 |
|
Miesiąc i dzień | 19 czerwca |
|
Rok i miesiąc | Czerwiec 2025 r. |
|
ISO 8601 (round tripper) | 2025-06-19T17:30:25.0000000 |
Console.WriteLine(now.ToString("d")); // 19.06.2025
Console.WriteLine(now.ToString("F")); // 19 czerwca 2025 r. 17:30:25
Console.WriteLine(now.ToString("O")); // 2025-06-19T17:30:25.0000000
Własne (customowe) szablony
Jeśli potrzebujesz bardziej specyficznych formatów, możesz użyć własnych szablonów:
| Symbol | Znaczenie |
|---|---|
| yyyy | Rok, 4 cyfry |
| yy | Rok, 2 cyfry |
| MM | Miesiąc, 2 cyfry |
| MMMM | Nazwa miesiąca |
| dd | Dzień, 2 cyfry |
| d | Dzień, 1-2 cyfry |
| HH | Godzina (24h) |
| mm | Minuty |
| ss | Sekundy |
| 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")); // środa, czerwca 19
Różnice kulturowe w formatowaniu
Jeśli chcesz uzyskać wynik w stylu innego kraju lub języka, użyj przeciążenia ToString(string, IFormatProvider):
var enUS = new System.Globalization.CultureInfo("en-US");
Console.WriteLine(now.ToString("D", enUS)); // June 19, 2025
- Jeśli twardo ustawisz szablon typu "MM/dd/yyyy", to niezależnie od kultury i tak dostaniesz "06/19/2025".
- Ale jeśli używasz tylko "d" lub "D" — format zależy od kultury!
Ważne rzeczy przy formatowaniu
Pracując z formatowaniem dat, warto rozumieć różnicę między standardowymi a customowymi formatami. Standardowe formaty (jak "d", "F", "G") automatycznie dostosowują się do kultury systemu, co jest wygodne dla UI, ale może powodować problemy przy wymianie danych między systemami. Customowe formaty dają pełną kontrolę nad wyjściem, ale wymagają dokładniejszego ustawienia.
Szczególną uwagę warto zwrócić na format "O" (albo "o") — to tzw. "round-trip" format, który gwarantuje, że data zamieniona na stringa i z powrotem będzie dokładnie taka sama. Ten format jest mega ważny przy serializacji danych albo przesyłaniu ich przez sieć.
2. Parsowanie dat i czasu: jak ze stringa zrobić obiekt
Najprostsze parsowanie: DateTime.Parse
DateTime.Parse — metoda, która próbuje rozpoznać stringa z datą z uwzględnieniem kultury systemu.
string input = "19.06.2025";
DateTime parsed = DateTime.Parse(input);
Console.WriteLine(parsed); // 19.06.2025 00:00:00
Jeśli string jest niepoprawny — program wywali się z wyjątkiem (każdemu się zdarza!).
Wskazanie kultury
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
Bezpieczne parsowanie: TryParse
string input = "niepoprawna data";
bool ok = DateTime.TryParse(input, out DateTime safeDate);
if (!ok)
Console.WriteLine("Błąd: nie udało się rozpoznać daty!");
Twarde wskazanie formatu: ParseExact i 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);
Popularne formaty — tabela
| String | Format | Końcowy obiekt |
|---|---|---|
| "2025-06-19" | "yyyy-MM-dd" | 19 czerwca 2025 |
| "19.06.2025" | "dd.MM.yyyy" | 19 czerwca 2025 |
| "06/19/2025" | "MM/dd/yyyy" | 19 czerwca 2025 |
| "2025-06-19T14:15:16" | "s" | 19 czerwca 2025 14:15:16 |
Praca z DateTimeStyles
Przy parsowaniu możesz dodatkowo sterować zachowaniem przez parametr DateTimeStyles. Ten enum pozwala ustawić, jak parser ma interpretować wejściowe dane. Na przykład, DateTimeStyles.AssumeUniversal sprawia, że parser traktuje czas jako UTC, jeśli przesunięcie nie jest jawnie podane. DateTimeStyles.AllowWhiteSpaces pozwala ignorować zbędne spacje w stringu. Te ustawienia są szczególnie przydatne przy pracy z danymi z zewnętrznych źródeł, gdzie format może być nie do końca przewidywalny.
3. Formatowanie i parsowanie DateOnly, TimeOnly
Nowe typy DateOnly i TimeOnly są teraz często używane do przechowywania dat/czasu bez zbędnych szczegółów.
Formatowanie
DateOnly birthday = new DateOnly(2000, 6, 19);
Console.WriteLine(birthday.ToString("dd MMMM yyyy")); // 19 czerwca 2000
Parsowanie
var d = DateOnly.ParseExact("19.06.2000", "dd.MM.yyyy");
Console.WriteLine(d.Day); // 19
Tak samo działają metody dla TimeOnly (tylko szablony dotyczą godzin/minut/sekund):
TimeOnly t = TimeOnly.ParseExact("23:59", "HH:mm");
Console.WriteLine(t.Hour); // 23
Zalety DateOnly i TimeOnly
Używanie DateOnly i TimeOnly zamiast DateTime daje kilka ważnych plusów. Po pierwsze, to semantyczna jasność — jeśli potrzebujesz tylko daty bez czasu (np. data urodzenia), DateOnly jasno to pokazuje. Po drugie, pomaga to uniknąć problemów ze strefami czasowymi, które mogą się pojawić przy DateTime. Po trzecie, te typy zajmują mniej miejsca w pamięci i bazach danych.
Formatowanie i parsowanie z uwzględnieniem stref czasowych
Jeśli przechowujesz czas z informacją o przesunięciu — używaj DateTimeOffset. To szczególnie ważne dla międzynarodowych, rozproszonych systemów. Wszystkie metody formatowania i parsowania są analogiczne, tylko teraz możesz też sterować przesunięciem:
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. Praktyczne uwagi i typowe błędy
Jedna z pułapek — pomylenie "dnia" i "miesiąca" przy parsowaniu międzynarodowych formatów. Na przykład string "01/02/2025" w USA to 2 stycznia, a w większości krajów Europy — 1 lutego (niespodzianka!).
Często też zapomina się o wpływie kultury: jeśli aplikacja działa na serwerze w Niemczech, a użytkownicy są z różnych krajów, format daty z bazy może być różnie interpretowany na każdym serwerze.
Częsty błąd — używanie .ToString() bez wskazania kultury/formatu do logów i plików wymiany: wynik może się zmienić po migracji na inny OS albo jeśli aplikacja będzie działać pod innym użytkownikiem Windows.
Rekomendacje do pracy z datami
- Do wewnętrznego przechowywania i wymiany danych zawsze używaj inwariantnej kultury (CultureInfo.InvariantCulture) lub jawnie wskazanych formatów.
- Do UI stosuj kulturę użytkownika albo pozwól mu wybrać preferowany format.
- Przy pracy z API i bazami danych preferuj format ISO 8601, który jest międzynarodowym standardem.
- Zawsze waliduj dane wejściowe: sprawdzaj istnienie daty, zakres i sensowność wartości.
Praca z różnymi źródłami danych
Przy integracji z zewnętrznymi systemami często trzeba pracować z datami w różnych formatach. Niektóre systemy mogą przesyłać daty jako Unix timestamp (liczba sekund od 1 stycznia 1970), inne — jako stringi w narodowych formatach. Ważne, żeby wcześniej dogadać się co do formatu wymiany danych i zawsze sprawdzać poprawność parsowania.
Warto też pamiętać, że niektóre formaty mogą być niejednoznaczne. Na przykład "12/13/2025" jednoznacznie oznacza 13 grudnia 2025, ale "12/11/2025" może być zarówno 12 listopada, jak i 11 grudnia w zależności od kultury. W takich przypadkach lepiej używać mniej dwuznacznych formatów albo jawnie wskazywać kulturę parsowania.
GO TO FULL VERSION