TimeSpan w C# — Hours vs TotalHours i pełne godziny

Uczysz się programowania 20 minut dziennie przez cały rok. Piszesz w C# kalkulator, który ma powiedzieć, ile to godzin, a on odpowiada: 1 godzina i 40 minut. Program się nie wywrócił, kompilator nic nie zgłosił, nie było nawet ostrzeżenia. A „oczywista” naprawa, która przychodzi do głowy jako pierwsza, też podaje zły wynik — tylko w inny sposób.
Ten wpis to wersja tekstowa odcinka Program .NET Developer #5 na YouTube. Kod jest ten sam co w filmie, a każdy wynik uruchomiłem na .NET 10. Jeśli twój program liczy czas — czas gry, długość filmu, godziny pracy, czas do końca promocji — to dotyczy także ciebie. Zakładam, że znasz zmienne i typy w C#; resztę wyjaśniam po drodze.
TimeSpan w C#: rok nauki = 1 godzina
Kalkulator ma dwie liczby: ile minut dziennie i przez ile dni. Zamieniamy je na odcinek czasu i wypisujemy godziny oraz minuty:
int minutyDziennie = 20; // tyle uczysz się każdego dnia
int dni = 365; // przez cały rok
TimeSpan nauka = TimeSpan.FromMinutes(minutyDziennie * dni);
Console.WriteLine($"Rok nauki: {nauka.Hours} h {nauka.Minutes} min");TimeSpan to typ z biblioteki .NET — po polsku „odcinek czasu”. Trzyma długość czasu (godzinę, trzy dni, 7300 minut), a nie konkretny moment w kalendarzu; do momentów służy DateTime. Metoda TimeSpan.FromMinutes zamienia liczbę minut na taki odcinek. Znak $ przed cudzysłowem pozwala wstawić wartość w nawiasy klamrowe — więcej o tym we wpisie o interpolacji stringów. Wynik:
Rok nauki: 1 h 40 minPoliczmy sami: 20 × 365 = 7300 minut. To dużo więcej niż jedna godzina. Kod się skompilował, program się nie zatrzymał, a wynik jest zły. To najgorszy rodzaj błędu, bo nikt cię o nim nie ostrzeże — zauważysz go tylko wtedy, gdy sam przeliczysz wynik.
Zasada: to, że kod się kompiluje i działa, nie znaczy, że liczy dobrze. Wynik, który dotyczy czasu, przelicz choć raz ręcznie.
Co jest w środku TimeSpan: 5.01:40:00
Wypiszmy cały odcinek czasu naraz, a pod spodem trzy jego części:
TimeSpan nauka = TimeSpan.FromMinutes(20 * 365);
Console.WriteLine($"Całość: {nauka}");
Console.WriteLine($"Days: {nauka.Days}");
Console.WriteLine($"Hours: {nauka.Hours}");
Console.WriteLine($"Minutes: {nauka.Minutes}");Całość: 5.01:40:00
Days: 5
Hours: 1
Minutes: 405.01:40:00 czytamy: 5 dni, 1 godzina, 40 minut i 0 sekund. Przed kropką są dni, po kropce godziny, minuty i sekundy, jak na zegarku. Reszta roku nie zginęła — jest w dniach. Pięć dni to 120 godzin, a TimeSpan trzyma je po prostu w innym miejscu.
Pomyśl o tym jak o zegarze z trzema tarczami:
| Tarcza | Właściwość | Zakres | Tu pokazuje |
|---|---|---|---|
| dni | Days | od 0 w górę | 5 |
| godziny | Hours | 0–23 | 1 |
| minuty | Minutes | 0–59 | 40 |
Kiedy tarcza godzin dobije do 24, wraca na zero, a tarcza dni przeskakuje o jeden. Tak samo jak zegar na ścianie: o pierwszej w nocy pokazuje jedynkę, a nie liczbę godzin, które minęły od twoich urodzin. Hours nie znaczy „wszystkie godziny” — pokazuje tylko to, co jest na tarczy godzin.
Dlatego ten błąd jest podstępny. Gdybyś sprawdził kalkulator na jednym miesiącu (20 minut × 30 dni), dostałbyś Hours = 10 — i to jest prawda. Błąd wychodzi dopiero wtedy, gdy wynik przekroczy jedną dobę.
Zasada:
Days,HoursiMinutesto pojedyncze tarcze, a nie cały czas.Hoursjest zawsze w zakresie 0–23.
Minutes czy TotalMinutes?
Skoro Minutes to tylko tarcza minut, jak dostać cały czas? TimeSpan ma do tego drugi zestaw właściwości, zaczynających się od Total — „całkowity”:
TimeSpan nauka = TimeSpan.FromMinutes(20 * 365);
Console.WriteLine($"Minutes: {nauka.Minutes}");
Console.WriteLine($"TotalMinutes: {nauka.TotalMinutes}");Zanim przewiniesz dalej, odpowiedz sam:
03-Total.cs — tarcza czy całość?
Zmienna nauka to 5.01:40:00 — 20 minut dziennie przez rok, czyli 7300 minut. Co wypisze każda linijka?
Minutes to 40 — tylko tarcza minut. TotalMinutes to 7300 — cały czas w minutach. Bez Total pytasz o jedną tarczę, z Total — o całość.Minutes: 40
TotalMinutes: 7300Minutes to 40, bo tyle jest na tarczy minut. TotalMinutes to 7300 — cały rok nauki zamieniony na minuty, dokładnie tyle, ile wyszło z mnożenia. Tak samo działają TotalHours i TotalDays: każda odpowiada na pytanie „ile to razem, w jednej jednostce”. Jest jedna różnica, którą łatwo przeoczyć: właściwości z Total są typu double, czyli mogą mieć część po przecinku. Tarcze (Days, Hours, Minutes) są typu int.
Zasada: bez
Total— jedna tarcza zegara (int). ZTotal— cały czas w jednej jednostce, razem z ułamkiem (double).
Pułapka: naprawa przez TotalHours liczy 40 minut dwa razy
Teraz „oczywista” naprawa. Skoro Total daje całość, zamiast Hours piszemy TotalHours, a minuty zostawiamy, jak były:
TimeSpan nauka = TimeSpan.FromMinutes(20 * 365);
Console.WriteLine($"Rok nauki: {nauka.TotalHours} h {nauka.Minutes} min");Rok nauki: 121,66666666666667 h 40 minTotalHours to cały czas w godzinach, razem z ułamkiem. A to 0,666… godziny na końcu to właśnie 40 minut — dwie trzecie godziny zapisane po przecinku. Do tego dokleiliśmy jeszcze 40 minut z tarczy minut. Te same 40 minut policzyliśmy dwa razy: raz w ułamku, raz z tarczy. (Przecinek zamiast kropki w 121,66… to polskie ustawienia regionalne komputera — o tym, skąd się bierze, piszę we wpisie o formatowaniu daty i CultureInfo.)
Ta pułapka jest groźniejsza od pierwszej. Wynik 1 h 40 min jest na tyle absurdalny, że łatwo go zauważyć. Wynik 121,67 h 40 min wygląda „prawie dobrze” — i właśnie taki łatwo przepuścić dalej.
Zasada: nie mieszaj dwóch rodzajów pytań w jednym zdaniu. Albo pytasz o tarcze (
Days,Hours,Minutes), albo o całość w jednej jednostce (TotalHours,TotalMinutes). Pół na pół zawsze da bałagan.
Poprawka: pełne godziny z Days * 24 + Hours
Potrzebujemy pełnych godzin, bez ułamka. Tarcze już znamy, więc składamy pełne godziny z dwóch tarcz: każdy dzień to 24 godziny, do tego dokładamy to, co jest na tarczy godzin. Minuty bierzemy z tarczy minut:
int minutyDziennie = 20; // tyle uczysz się każdego dnia
int dni = 365; // przez cały rok
TimeSpan nauka = TimeSpan.FromMinutes(minutyDziennie * dni);
int godziny = nauka.Days * 24 + nauka.Hours;
Console.WriteLine($"Rok nauki: {godziny} h {nauka.Minutes} min");Rok nauki: 121 h 40 minGwiazdka to mnożenie, a C# — jak matematyka — mnoży przed dodawaniem: 5 × 24 + 1 = 121. Wszystkie operatory i ich kolejność znajdziesz we wpisie o operatorach w C#.
Sprawdźmy drugą drogą: 121 × 60 + 40 = 7300 minut. Dokładnie tyle, ile pokazał TotalMinutes. Tym razem kalkulator mówi prawdę.
Trzy sposoby zapytania o godziny i kiedy który ma sens:
| Wyrażenie | Wynik dla 7300 minut | Kiedy używać | Haczyk |
|---|---|---|---|
nauka.Hours | 1 | zegar w obrębie doby: „która godzina na tarczy” | po przekroczeniu doby gubi dni — bez błędu |
nauka.TotalHours | 121,66666666666667 | obliczenia: stawka za godzinę, średnia, porównanie | double z ułamkiem; z Minutes daje 40 minut dwa razy |
nauka.Days * 24 + nauka.Hours | 121 | wypisanie „X h Y min” dla człowieka | trzeba pamiętać o dniach; minuty bierzesz z Minutes |
Zasada: wynik zawsze sprawdź drugą drogą — na przykład przez
TotalMinutes. Jeśli obie drogi dają to samo, liczysz dobrze.
Sprawdź sam: tarcze zegara dla twoich minut
Wybierz, ile minut dziennie się uczysz. Wstawka pokazuje trzy tarcze i to, co dla tej liczby wypisze kalkulator z błędem (01-Rok.cs) i po poprawce (05-Final.cs). Wszystkie siedem wariantów uruchomiłem na .NET 10.
Tarcze zegara — rok nauki
Ile minut dziennie? (przez 365 dni)
Całość: · TotalMinutes: · TotalHours:
Ściągawka na code review
| Właściwość | Typ | Co zwraca | Dla 5.01:40:00 |
|---|---|---|---|
Days | int | tarcza dni | 5 |
Hours | int | tarcza godzin, 0–23 | 1 |
Minutes | int | tarcza minut, 0–59 | 40 |
TotalHours | double | cały czas w godzinach, z ułamkiem | 121,66666666666667 |
TotalMinutes | double | cały czas w minutach | 7300 |
Przeglądając kod, szukaj .Hours i .Minutes w miejscach, gdzie odcinek czasu może być dłuższy niż doba (sumy, czas „od początku”, „od rejestracji”, „w tym roku”), i zdań, które łączą Total… z tarczą w jednym napisie. Gdzie to spotkasz? Wszędzie tam, gdzie program liczy czas: czas gry, długość filmu, godziny pracy nad projektem, czas do końca promocji. Dopóki wynik jest krótszy niż doba, Hours działa. Kiedy przekroczy dobę, zaczyna kłamać. Gdy wynik wyjdzie dziwny, przejdź program krok po kroku debugerem — w podglądzie zmiennych widać wszystkie tarcze naraz.
Zadanie: 25 minut dziennie
Tym razem 25 minut dziennie przez rok. Program wypisuje dni, godziny i TotalHours:
TimeSpan nauka = TimeSpan.FromMinutes(25 * 365);
Console.WriteLine($"Days: {nauka.Days}");
Console.WriteLine($"Hours: {nauka.Hours}");
Console.WriteLine($"TotalHours: {nauka.TotalHours}");- Zanim uruchomisz program, zapisz trzy liczby, które wypisze.
- Dopisz linijkę, która pokaże pełne godziny i minuty.
- Celowo zepsuj: spróbuj wpisać do
nauka.Hourswłasną liczbę i przeczytaj komunikat kompilatora.
Pytanie bonusowe do komentarza pod filmem: co wypisze {nauka} — cały odcinek czasu w nawiasach klamrowych? Odpowiedzi są w przypiętym komentarzu pod filmem.
Podsumowanie
TimeSpantrzyma czas jak zegar z trzema tarczami:Days,Hours(0–23) iMinutes(0–59) — każda osobno.Hoursto nie wszystkie godziny. Po przekroczeniu doby reszta przeskakuje na tarczę dni, bez błędu i bez ostrzeżenia.- Właściwości z
Totaldają cały czas w jednej jednostce, jakodoublez ułamkiem.TotalHoursrazem zMinutesliczy te same minuty dwa razy. - Pełne godziny:
nauka.Days * 24 + nauka.Hours, minuty znauka.Minutes— i sprawdzenie drugą drogą przezTotalMinutes.
🎥 Wersja wideo: ten sam przykład krok po kroku — Program .NET Developer #5 — Rok nauki: 1 godzina
🔴 Napisz taki kalkulator sam — na żywo: „Pierwszy program w C# — zrób to ze mną”: instalujemy Visual Studio i piszesz razem ze mną kalkulator nauki, od zera — transmisja 9.11.2026 o 19:00, potem nagranie pod tym samym linkiem
🎓 Skąd ten temat: kurs C# Podstawy Programowania — lekcje o obsłudze daty i czasu. Kurs to poziom 1 Programu .NET Developer.
📘 Darmowa roadmapa .NET Developera: dev-hobby.pl/lista-vip
Zobacz
- Dzielenie przez zero w C# — double, nieskończoność i NaN — inny wynik, który jest zły, choć program nic nie zgłasza
- Formatowanie daty w C# — DateTime i CultureInfo — jak wypisać datę i liczbę w polskim formacie
- Właściwości w C# — auto-property i init — czym jest właściwość, taka jak
Hours - Pierwszy program w C# — krok po kroku z kodem — jeśli dopiero zaczynasz
🚀 Co dalej?
Zobacz to w praktyce na wideo i pobierz darmową roadmapę, żeby ułożyć naukę w spójną ścieżkę do pierwszej pracy.
- 🗺️ Pobierz darmową roadmapę Junior .NET Developer — 12 poziomów od podstaw C# do pierwszej pracy: dev-hobby.pl
- 🎬 Subskrybuj kanał YouTube — nowe filmy co tydzień.
Zamień wiedzę w umiejętności
Pobierz darmową Roadmapę .NET i ułóż takie tematy jak ten w spójną ścieżkę do pierwszej pracy.
Pobieram roadmapę →