Menu

Date e orari in Golang: Now, Sleep, Format, Parse e Duration

Come lavorare con date e orari in Go: time.Now e time.Sleep, i calcoli con Duration, il layout di riferimento 2006-01-02 15:04:05 per Format e Parse, i fusi orari, i timestamp Unix, timer e ticker.

Questa pagina include editor eseguibili: modifica, esegui e vedi subito l'output.

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:

CostanteValore
time.Nanosecond1
time.Microsecond1000 ns
time.Millisecond1000 µs
time.Second1000 ms
time.Minute60 s
time.Hour60 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 quando n è una variabile int. Convertila: time.Duration(n) * time.Second. Una costante come 5 funziona 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ù:

TokenSignificatoEsempio
2006 / 06anno, 4 o 2 cifre2026 / 26
01 / 1 / Jan / Januarymese03 / 3 / Mar / March
02 / 2 / _2giorno del mese (con zero iniziale, semplice, con spazio iniziale)05 / 5 / " 5"
Mon / Mondaygiorno della settimanaThu / Thursday
15ora, formato 24 ore09
03 / 3ora, formato 12 ore09 / 9
04 / 4minuto07 / 7
05 / 5secondo03 / 3
PM / pmindicatore AM o PMAM
.000 / .999frazioni di secondo (fisse / senza zeri finali).250 / .25
MSTabbreviazione del fusoUTC
-0700 / -07:00offset numerico del fuso+0000 / +00:00
Z07:00come -07:00, ma stampa Z per UTCZ

Layout predefiniti:

CostanteLayout
time.RFC33392006-01-02T15:04:05Z07:00 (usalo per API e JSON)
time.RFC3339Nano2006-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.Kitchen3:04PM
time.RFC1123Mon, 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.
  • Sub restituisce una Duration, 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) o time.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 mesi 01. "15:01" stampa l'ora e poi il mese, non il minuto.
  • Confrontare con ==. Usa Equal.
  • Affidarsi a time.Local. Cambia da una macchina all'altra. Sii esplicito con time.UTC o con una location caricata.
  • Ignorare l'errore di LoadLocation o Parse. Entrambi falliscono con input reali. Un Parse fallito restituisce l'orario zero, che viene stampato come anno 1, e un LoadLocation fallito restituisce una location nil che fa andare in panic t.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.

Illustrazione dei linguaggi di programmazione di Coddy

Impara a programmare con Coddy

INIZIA