Podstawy w jednym programie
time.Now() zwraca bieżący czas lokalny, time.Sleep wstrzymuje bieżącą goroutine, time.Since mierzy upływ czasu, a time.Date tworzy konkretną chwilę. Reszta tej strony omawia każdy element po kolei. Przykłady używają stałych dat i UTC, więc ich wynik jest taki sam, gdziekolwiek je uruchomisz.
Sleep i Duration
time.Duration to liczba nanosekund typu int64. Pakiet definiuje stałe do tworzenia czytelnych wartości:
| Stała | Wartość |
|---|---|
time.Nanosecond | 1 |
time.Microsecond | 1000 ns |
time.Millisecond | 1000 µs |
time.Second | 1000 ms |
time.Minute | 60 s |
time.Hour | 60 min |
Nie ma time.Day, bo doba nie zawsze ma 24 godziny, gdy strefa czasowa zmienia się przy przejściu na czas letni. Do dni kalendarzowych używaj AddDate (patrz niżej).
Dwa częste błędy wynikają z tego, że Duration to liczba całkowita:
time.Sleep(5)usypia na 5 nanosekund. Zawsze mnóż przez jednostkę:time.Sleep(5 * time.Second).time.Sleep(n * time.Second)się nie kompiluje, gdynjest zmienną typuint. Skonwertuj ją:time.Duration(n) * time.Second. Stała taka jak5działa bez konwersji, bo stałe bez typu dopasowują się do typu Duration.
ParseDuration przyjmuje ns, us (lub µs), ms, s, m i h, łączone w stylu "2h45m" lub "1.5s". Nie ma jednostki dla dni.
Formatowanie: układ wzorcowy 2006-01-02
Go nie używa %Y-%m-%d ani yyyy-MM-dd. Układ to czas wzorcowy
Mon Jan 2 15:04:05 MST 2006
zapisany tak, jak ma wyglądać wynik. Wartości dobrano tak, by każda była unikalna: miesiąc 1, dzień 2, godzina 3 (lub 15), minuta 4, sekunda 5, rok 6 (2006), przesunięcie strefy -7 (-0700). Czytaj to jako 01/02 03:04:05PM '06 -0700.
Najczęściej potrzebne tokeny układu:
| Token | Znaczenie | Przykład |
|---|---|---|
2006 / 06 | rok, 4 lub 2 cyfry | 2026 / 26 |
01 / 1 / Jan / January | miesiąc | 03 / 3 / Mar / March |
02 / 2 / _2 | dzień miesiąca (z zerem, bez dopełnienia, ze spacją) | 05 / 5 / " 5" |
Mon / Monday | dzień tygodnia | Thu / Thursday |
15 | godzina, zegar 24-godzinny | 09 |
03 / 3 | godzina, zegar 12-godzinny | 09 / 9 |
04 / 4 | minuta | 07 / 7 |
05 / 5 | sekunda | 03 / 3 |
PM / pm | znacznik AM lub PM | AM |
.000 / .999 | ułamki sekund (stała liczba cyfr / bez końcowych zer) | .250 / .25 |
MST | skrót strefy | UTC |
-0700 / -07:00 | liczbowe przesunięcie strefy | +0000 / +00:00 |
Z07:00 | jak -07:00, ale dla UTC wypisuje Z | Z |
Predefiniowane układy:
| Stała | Układ |
|---|---|
time.RFC3339 | 2006-01-02T15:04:05Z07:00 (używaj go w API i JSON) |
time.RFC3339Nano | 2006-01-02T15:04:05.999999999Z07:00 |
time.DateTime (Go 1.20) | 2006-01-02 15:04:05 |
time.DateOnly (Go 1.20) | 2006-01-02 |
time.TimeOnly (Go 1.20) | 15:04:05 |
time.Kitchen | 3:04PM |
time.RFC1123 | Mon, 02 Jan 2006 15:04:05 MST (daty HTTP używają zamiast tego http.TimeFormat) |
Klasyczny błąd to układ zapisany złymi liczbami, na przykład "2023-01-01". Go go nie odrzuca. Kopiuje znaki, których nie rozpoznaje, i podstawia te, które rozpoznaje: każda 2 to dzień, 3 to godzina w zegarze 12-godzinnym, a oba 01 to miesiąc, więc 5 marca o 9:07 zostaje sformatowany jako 5059-03-03. Układ taki jak "YYYY-MM-DD" nie zawiera żadnych tokenów i wypisuje się bez zmian. Jeśli sformatowane daty wyglądają dziwnie, sprawdź, czy układ używa dokładnie wartości wzorcowych.
Parsowanie stringów na czas
time.Parse(layout, value) używa tych samych układów i zwraca błąd, który musisz sprawdzić:
Bez strefy w danych wejściowych Parse zwraca UTC. Ten sam zapis czasu zegarowego może oznaczać różne chwile w zależności od strefy, dlatego istnieje ParseInLocation. Komunikat błędu wskazuje część, która nie pasuje, co pomaga przy debugowaniu układu.
Strefy czasowe
time.Time to chwila plus lokalizacja używana do wyświetlania. Zmiana lokalizacji przez In zmienia sposób wypisywania, a nie to, jaka to chwila.
time.UTCjest zawsze dostępne. Przechowuj i przesyłaj czasy w UTC (albo w RFC 3339 z przesunięciem) i konwertuj tylko do wyświetlania.time.Localto strefa maszyny. Na serwerach i w kontenerach to często UTC, a na laptopie nie, więc kod, który od niej zależy, zachowuje się różnie w każdym miejscu.time.LoadLocation("Europe/Berlin")czyta bazę IANA z systemu operacyjnego. Minimalne obrazy kontenerów często jej nie mają i wtedy wywołanie zwraca błąd. Pusty import_ "time/tzdata"osadza bazę w binarce (około 450 KB), więc zawsze działa. Zawsze obsługuj błąd.time.FixedZone(name, offsetSeconds)tworzy strefę ze stałym przesunięciem. Nie ma reguł czasu letniego, więc używaj jej dla otrzymanych przesunięć, a nie dla nazwanych regionów.
Arytmetyka i porównywanie
Na co zwrócić uwagę w wyniku:
AddDate(0, 1, 0)dla 31 stycznia daje 3 marca, a nie 28 lutego. Go dodaje jeden miesiąc, otrzymuje „31 lutego”, a potem normalizuje nadmiar. Jeśli potrzebujesz „tego samego dnia w następnym miesiącu, przyciętego do jego końca”, napisz tę logikę samodzielnie.SubzwracaDuration, która kończy się na około 292 latach. Dla różnic w dniach kalendarzowych porównuj daty o północy UTC i dziel przez 24 godziny.- Aby dostać początek dnia, zbuduj czas na nowo z
Date().t.Truncate(24 * time.Hour)zaokrągla względem czasu zerowego w UTC, więc daje złą odpowiedź dla każdej strefy innej niż UTC.
Porównuj przez Equal, Before, After, nigdy przez ==. time.Now() zawiera odczyt zegara monotonicznego, dzięki któremu time.Since pozostaje poprawne, nawet gdy zegar systemowy zostanie przestawiony. == porównuje ten odczyt, a także lokalizację, więc dwie wartości Time dla tej samej chwili mogą być różne. Z tego samego powodu nie używaj time.Time jako klucza mapy bez wcześniejszej normalizacji (t.UTC().Round(0) usuwa odczyt monotoniczny).
Zerowy time.Time to 1 stycznia roku 1, 00:00 UTC. Sprawdzaj go przez t.IsZero().
Mierzenie upływu czasu
start := time.Now()
doWork()
log.Printf("doWork took %v", time.Since(start))
time.Since(start) to time.Now().Sub(start), a time.Until(deadline) to deadline.Sub(time.Now()). Oba używają zegara monotonicznego, gdy jest dostępny, więc są odporne na zmiany zegara systemowego. Do benchmarkowania kodu używaj wsparcia dla benchmarków w pakiecie testing zamiast ręcznego mierzenia czasu.
Timery i tickery
time.After(d) zwraca kanał, który odbiera wartość raz, po czasie d. time.Timer to to samo, ale z metodą Stop. time.Ticker dostarcza wartość co określony okres, dopóki go nie zatrzymasz.
Trzy tyknięcia po 20, 40 i 60 ms przychodzą długo przed timerem 110 ms, więc program kończy się po trzecim tyknięciu. time.AfterFunc(d, f) uruchamia f we własnej goroutine po czasie d, co przydaje się do jednorazowej opóźnionej pracy.
Od Go 1.23 timery i tickery, do których nie ma już odwołań, są zbierane przez garbage collector, nawet jeśli nigdy nie wywołasz Stop, a kanał zatrzymanego lub zresetowanego timera nie dostarcza już przestarzałej wartości. Wywołanie Stop przez defer nadal jasno mówi, że życie tickera się skończyło. Gdy limit czasu dotyczy całej operacji, a nie jednego oczekiwania, context.WithTimeout zwykle czyta się lepiej niż timer.
Czas w JSON
time.Time automatycznie serializuje się do stringów RFC 3339 w JSON i z nich deserializuje, z precyzją do nanosekund:
type Event struct {
Name string `json:"name"`
At time.Time `json:"at"`
}
// {"name":"deploy","at":"2026-09-23T14:30:00Z"}
String JSON w innym formacie nie da się zdeserializować. Dla znaczników czasu Unix lub własnych formatów przechowuj int64 albo string i konwertuj, albo zdefiniuj typ z własną metodą UnmarshalJSON.
Typowe błędy
time.Sleep(1)lubtime.Sleep(n)z samą liczbą. To nanosekundy.- Układy ze złymi cyframi.
"2023-01-01"ani"YYYY-MM-DD"nie są układami. Użyj"2006-01-02". - Mylenie minut z miesiącami. Minuty to
04, miesiące to01."15:01"wypisuje godzinę, a potem miesiąc, a nie minutę. - Porównywanie przez
==. UżyjEqual. - Poleganie na
time.Local. Różni się między maszynami. Jawnie używajtime.UTCalbo wczytanej lokalizacji. - Ignorowanie błędu z
LoadLocationlubParse. Oba zawodzą na prawdziwych danych. NieudaneParsezwraca czas zerowy, który wypisuje się jako rok 1, a nieudaneLoadLocationzwraca lokalizacjęnil, przez którąt.In(loc)wywołuje panic.
Najczęściej zadawane pytania
Jak zrobić sleep w Go?
Wywołaj time.Sleep z wartością time.Duration: time.Sleep(2 * time.Second) albo time.Sleep(500 * time.Millisecond). Sama liczba, jak time.Sleep(2), się skompiluje, ale uśpi program na 2 nanosekundy, bo Duration liczy nanosekundy. Sleep blokuje tylko bieżącą goroutine.
Dlaczego Go używa 2006-01-02 15:04:05 w formatach dat?
Go formatuje daty na przykładzie. Układ to czas wzorcowy Mon Jan 2 15:04:05 MST 2006 zapisany tak, jak ma wyglądać wynik. Jego części rosną w amerykańskiej kolejności: miesiąc 1, dzień 2, godzina 3 (15 w zegarze 24-godzinnym), minuta 4, sekunda 5, rok 6 (2006), przesunięcie strefy 7 (-0700). Dlatego "2006-01-02" oznacza rok-miesiąc-dzień, a "02/01/2006" dzień/miesiąc/rok.
Jak sparsować string z datą w Go?
Użyj time.Parse(layout, value) z układem zapisanym czasem wzorcowym: t, err := time.Parse("2006-01-02", "2026-09-23"). Zawsze sprawdzaj err. Bez informacji o strefie w stringu wynik jest w UTC; użyj time.ParseInLocation, żeby zinterpretować go w innej strefie.
Jak pobrać znacznik czasu Unix w Go?
time.Now().Unix() zwraca liczbę sekund od 1 stycznia 1970 UTC jako int64. UnixMilli(), UnixMicro() i UnixNano() dają mniejsze jednostki. W drugą stronę użyj time.Unix(sec, 0) albo time.UnixMilli(ms).
Jak porównać dwa czasy w Go?
Użyj t1.Before(t2), t1.After(t2) i t1.Equal(t2). Nie używaj ==: porównuje też lokalizację i odczyt zegara monotonicznego, więc dwie wartości dla tej samej chwili mogą być różne. t2.Sub(t1) daje różnicę jako Duration.