Le basi in un programma
time.Now() restituisce l'ora locale attuale, time.Sleep mette in pausa la goroutine corrente, time.Since misura il tempo trascorso e time.Date costruisce un momento preciso. Il resto della pagina tratta ogni parte una alla volta. Gli esempi usano date fisse e UTC, così il loro output è lo stesso ovunque tu li esegua.
Sleep e Duration
Un time.Duration è un int64 che conta nanosecondi. Il pacchetto definisce delle costanti per costruire valori leggibili:
| Costante | Valore |
|---|---|
time.Nanosecond | 1 |
time.Microsecond | 1000 ns |
time.Millisecond | 1000 µs |
time.Second | 1000 ms |
time.Minute | 60 s |
time.Hour | 60 min |
Non esiste time.Day, perché un giorno non dura sempre 24 ore quando un fuso orario cambia per l'ora legale. Usa AddDate per i giorni di calendario (vedi più avanti).
Due bug comuni nascono dal fatto che Duration è un intero:
time.Sleep(5)dorme 5 nanosecondi. Moltiplica sempre per un'unità:time.Sleep(5 * time.Second).time.Sleep(n * time.Second)non compila quandonè una variabileint. Convertila:time.Duration(n) * time.Second. Una costante come5funziona senza conversione perché le costanti senza tipo si adattano al tipo Duration.
ParseDuration accetta ns, us (o µs), ms, s, m e h, combinati come "2h45m" o "1.5s". Non ha un'unità per i giorni.
Formattare: il layout di riferimento 2006-01-02
Go non usa %Y-%m-%d né yyyy-MM-dd. Un layout è l'orario di riferimento
Mon Jan 2 15:04:05 MST 2006
scritto nel modo in cui vuoi che appaia l'output. I valori sono scelti in modo che ognuno sia unico: mese 1, giorno 2, ora 3 (o 15), minuto 4, secondo 5, anno 6 (2006), offset del fuso -7 (-0700). Leggilo come 01/02 03:04:05PM '06 -0700.
I token di layout che userai di più:
| Token | Significato | Esempio |
|---|---|---|
2006 / 06 | anno, 4 o 2 cifre | 2026 / 26 |
01 / 1 / Jan / January | mese | 03 / 3 / Mar / March |
02 / 2 / _2 | giorno del mese (con zero iniziale, semplice, con spazio iniziale) | 05 / 5 / " 5" |
Mon / Monday | giorno della settimana | Thu / Thursday |
15 | ora, formato 24 ore | 09 |
03 / 3 | ora, formato 12 ore | 09 / 9 |
04 / 4 | minuto | 07 / 7 |
05 / 5 | secondo | 03 / 3 |
PM / pm | indicatore AM o PM | AM |
.000 / .999 | frazioni di secondo (fisse / senza zeri finali) | .250 / .25 |
MST | abbreviazione del fuso | UTC |
-0700 / -07:00 | offset numerico del fuso | +0000 / +00:00 |
Z07:00 | come -07:00, ma stampa Z per UTC | Z |
Layout predefiniti:
| Costante | Layout |
|---|---|
time.RFC3339 | 2006-01-02T15:04:05Z07:00 (usalo per API e 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 (per le date HTTP usa invece http.TimeFormat) |
L'errore classico è scrivere un layout con i numeri sbagliati, per esempio "2023-01-01". Go non lo rifiuta. Copia i caratteri che non riconosce e sostituisce quelli che riconosce: ogni 2 è il giorno, 3 è l'ora nel formato 12 ore ed entrambi gli 01 sono il mese, quindi il 5 marzo alle 9:07 diventa 5059-03-03. Un layout come "YYYY-MM-DD" non contiene nessun token e stampa se stesso invariato. Se le date formattate sembrano strane, controlla che il layout usi esattamente i valori di riferimento.
Convertire stringhe in date
time.Parse(layout, value) usa gli stessi layout e restituisce un errore che devi controllare:
Se l'input non ha un fuso, Parse restituisce UTC. La stessa stringa di ora locale può indicare istanti diversi a seconda del fuso, ed è per questo che esiste ParseInLocation. Il messaggio di errore indica la parte che non corrisponde, e questo aiuta quando fai il debug di un layout.
Fusi orari
Un time.Time è un istante più una location usata per la visualizzazione. Cambiare la location con In cambia il modo in cui viene stampato, non il momento che rappresenta.
time.UTCè sempre disponibile. Memorizza e trasmetti gli orari in UTC (o in RFC 3339 con un offset) e converti solo per la visualizzazione.time.Localè il fuso della macchina. Su server e container è spesso UTC, su un portatile no, quindi il codice che dipende da esso si comporta in modo diverso in ogni posto.time.LoadLocation("Europe/Berlin")legge il database IANA dal sistema operativo. Le immagini container minimali spesso non ce l'hanno, e allora la chiamata restituisce un errore. L'import vuoto_ "time/tzdata"incorpora il database nel tuo binario (circa 450 KB) così funziona sempre. Gestisci sempre l'errore.time.FixedZone(name, offsetSeconds)crea un fuso con un offset costante. Non ha regole per l'ora legale, quindi usalo per offset che hai ricevuto, non per regioni con un nome.
Calcoli e confronti
Cose da notare nell'output:
AddDate(0, 1, 0)sul 31 gennaio dà il 3 marzo, non il 28 febbraio. Go aggiunge un mese ottenendo il "31 febbraio" e poi normalizza l'eccedenza. Se ti serve "lo stesso giorno del mese successivo, limitato all'ultimo giorno", scrivi tu quella logica.Subrestituisce unaDuration, che arriva al massimo a circa 292 anni. Per le differenze in giorni di calendario, confronta le date a mezzanotte UTC e dividi per 24 ore.- Per ottenere l'inizio della giornata, ricostruisci l'orario da
Date().t.Truncate(24 * time.Hour)arrotonda rispetto all'orario zero in UTC, quindi dà la risposta sbagliata per qualsiasi fuso diverso da UTC.
Confronta con Equal, Before, After, mai con ==. time.Now() include una lettura dell'orologio monotono, usata perché time.Since resti corretto se l'orologio di sistema viene regolato. == confronta anche quella lettura e la location, quindi due valori Time per lo stesso istante possono risultare diversi. Per lo stesso motivo, non usare time.Time come chiave di una mappa senza prima normalizzarlo (t.UTC().Round(0) rimuove la lettura monotona).
Il time.Time al valore zero è il 1 gennaio dell'anno 1, 00:00 UTC. Verificalo con t.IsZero().
Misurare il tempo trascorso
start := time.Now()
doWork()
log.Printf("doWork took %v", time.Since(start))
time.Since(start) equivale a time.Now().Sub(start), e time.Until(deadline) equivale a deadline.Sub(time.Now()). Entrambi usano l'orologio monotono quando è disponibile, quindi non risentono delle modifiche all'orologio di sistema. Per fare benchmark del codice, usa il supporto ai benchmark di Go nel pacchetto testing invece di cronometrare a mano.
Timer e ticker
time.After(d) restituisce un canale che riceve un valore una volta dopo d. Un time.Timer è la stessa cosa con un metodo Stop. Un time.Ticker consegna un valore a ogni periodo finché non lo fermi.
I tre tick a 20, 40 e 60 ms arrivano ben prima del timer da 110 ms, quindi il programma si ferma dopo il terzo tick. time.AfterFunc(d, f) esegue f in una goroutine separata dopo d, comodo per un lavoro ritardato una tantum.
Da Go 1.23 timer e ticker non più referenziati vengono raccolti dal garbage collector anche se non chiami mai Stop, e il canale di un timer fermato o reimpostato non consegna più un valore scaduto. Chiamare Stop con defer resta il modo chiaro per dire che la vita del ticker è finita. Quando un limite di tempo si applica a un'intera operazione e non a una singola attesa, un context.WithTimeout di solito si legge meglio di un timer.
Date in JSON
time.Time viene convertito automaticamente da e verso stringhe RFC 3339 in JSON, con precisione al nanosecondo:
type Event struct {
Name string `json:"name"`
At time.Time `json:"at"`
}
// {"name":"deploy","at":"2026-09-23T14:30:00Z"}
Una stringa JSON in un formato diverso non viene decodificata. Per timestamp Unix o formati personalizzati, memorizza un int64 o una stringa e converti, oppure definisci un tipo con un proprio UnmarshalJSON.
Errori comuni
time.Sleep(1)otime.Sleep(n)con un numero da solo. Sono nanosecondi.- Layout con le cifre sbagliate.
"2023-01-01"o"YYYY-MM-DD"non sono layout. Usa"2006-01-02". - Confondere minuti e mesi. I minuti sono
04, i mesi01."15:01"stampa l'ora e poi il mese, non il minuto. - Confrontare con
==. UsaEqual. - Affidarsi a
time.Local. Cambia da una macchina all'altra. Sii esplicito contime.UTCo con una location caricata. - Ignorare l'errore di
LoadLocationoParse. Entrambi falliscono con input reali. UnParsefallito restituisce l'orario zero, che viene stampato come anno 1, e unLoadLocationfallito restituisce una locationnilche fa andare in panict.In(loc).
Domande frequenti
Come faccio una pausa (sleep) in Go?
Chiama time.Sleep con un time.Duration: time.Sleep(2 * time.Second) oppure time.Sleep(500 * time.Millisecond). Un numero da solo come time.Sleep(2) compila ma dorme 2 nanosecondi, perché una Duration conta i nanosecondi. Sleep blocca solo la goroutine corrente.
Perché Go usa 2006-01-02 15:04:05 per i formati delle date?
Go formatta le date per esempio. Il layout è l'orario di riferimento Mon Jan 2 15:04:05 MST 2006 scritto nel modo in cui vuoi che appaia l'output. Le sue parti seguono una sequenza crescente nell'ordine americano: mese 1, giorno 2, ora 3 (15 nel formato 24 ore), minuto 4, secondo 5, anno 6 (2006), offset del fuso 7 (-0700). Quindi "2006-01-02" significa anno-mese-giorno e "02/01/2006" significa giorno/mese/anno.
Come converto una stringa in una data in Go?
Usa time.Parse(layout, value) con un layout scritto con l'orario di riferimento: t, err := time.Parse("2006-01-02", "2026-09-23"). Controlla sempre err. Se la stringa non contiene informazioni sul fuso, il risultato è in UTC; usa time.ParseInLocation per interpretarla in un altro fuso.
Come ottengo un timestamp Unix in Go?
time.Now().Unix() restituisce i secondi trascorsi dal 1 gennaio 1970 UTC come int64. UnixMilli(), UnixMicro() e UnixNano() danno unità più fini. Per la conversione inversa usa time.Unix(sec, 0) o time.UnixMilli(ms).
Come confronto due orari in Go?
Usa t1.Before(t2), t1.After(t2) e t1.Equal(t2). Non usare ==: confronta anche la location e la lettura dell'orologio monotono, quindi due valori per lo stesso istante possono risultare diversi. t2.Sub(t1) dà la differenza come Duration.