Menu

Golang Time: Now, Sleep, Format, Parse und Durations

So arbeitest du in Go mit Datum und Uhrzeit: time.Now und time.Sleep, Rechnen mit Duration, das Referenzlayout 2006-01-02 15:04:05 für Format und Parse, Zeitzonen, Unix-Zeitstempel sowie Timer und Ticker.

Diese Seite enthält ausführbare Editoren - bearbeiten, ausführen und Ausgabe sofort sehen.

Die Grundlagen in einem Programm

time.Now() gibt die aktuelle lokale Zeit zurück, time.Sleep pausiert die aktuelle Goroutine, time.Since misst die vergangene Zeit, und time.Date baut einen bestimmten Zeitpunkt. Der Rest dieser Seite nimmt sich jedes Stück einzeln vor. Die Beispiele nutzen feste Daten und UTC, damit ihre Ausgabe überall gleich ist, wo du sie ausführst.

Sleep und Duration

Eine time.Duration ist eine int64-Anzahl von Nanosekunden. Das Paket definiert Konstanten, mit denen du lesbare Werte baust:

KonstanteWert
time.Nanosecond1
time.Microsecond1000 ns
time.Millisecond1000 µs
time.Second1000 ms
time.Minute60 s
time.Hour60 min

Ein time.Day gibt es nicht, weil ein Tag nicht immer 24 Stunden hat, wenn eine Zeitzone auf Sommerzeit umstellt. Für Kalendertage nimm AddDate (siehe unten).

Zwei häufige Bugs kommen daher, dass Duration eine Ganzzahl ist:

  • time.Sleep(5) schläft 5 Nanosekunden. Multipliziere immer mit einer Einheit: time.Sleep(5 * time.Second).
  • time.Sleep(n * time.Second) kompiliert nicht, wenn n eine int-Variable ist. Konvertiere sie: time.Duration(n) * time.Second. Eine Konstante wie 5 funktioniert ohne Konvertierung, weil sich untypisierte Konstanten an den Typ Duration anpassen.

ParseDuration akzeptiert ns, us (oder µs), ms, s, m und h, kombiniert wie "2h45m" oder "1.5s". Eine Einheit für Tage hat es nicht.

Formatieren: das Referenzlayout 2006-01-02

Go verwendet weder %Y-%m-%d noch yyyy-MM-dd. Ein Layout ist die Referenzzeit

Mon Jan 2 15:04:05 MST 2006

geschrieben so, wie deine Ausgabe aussehen soll. Die Werte sind so gewählt, dass jeder eindeutig ist: Monat 1, Tag 2, Stunde 3 (oder 15), Minute 4, Sekunde 5, Jahr 6 (2006), Zonen-Offset -7 (-0700). Lies es als 01/02 03:04:05PM '06 -0700.

Die Layout-Tokens, die du am häufigsten brauchst:

TokenBedeutungBeispiel
2006 / 06Jahr, 4 oder 2 Ziffern2026 / 26
01 / 1 / Jan / JanuaryMonat03 / 3 / Mar / March
02 / 2 / _2Tag im Monat (mit Null aufgefüllt, einfach, mit Leerzeichen aufgefüllt)05 / 5 / " 5"
Mon / MondayWochentagThu / Thursday
15Stunde, 24-Stunden-Format09
03 / 3Stunde, 12-Stunden-Format09 / 9
04 / 4Minute07 / 7
05 / 5Sekunde03 / 3
PM / pmKennzeichen für AM oder PMAM
.000 / .999Sekundenbruchteile (fest / Nullen am Ende gekürzt).250 / .25
MSTAbkürzung der ZoneUTC
-0700 / -07:00numerischer Zonen-Offset+0000 / +00:00
Z07:00wie -07:00, gibt aber Z für UTC ausZ

Vordefinierte Layouts:

KonstanteLayout
time.RFC33392006-01-02T15:04:05Z07:00 (nimm das für APIs und 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 (HTTP-Datumsangaben nutzen stattdessen http.TimeFormat)

Der klassische Fehler ist ein Layout mit den falschen Zahlen, zum Beispiel "2023-01-01". Go lehnt es nicht ab. Es kopiert Zeichen, die es nicht erkennt, und ersetzt die, die es erkennt: Jede 2 ist der Tag, 3 die Stunde im 12-Stunden-Format, und beide 01 sind der Monat, also wird der 5. März um 9:07 als 5059-03-03 formatiert. Ein Layout wie "YYYY-MM-DD" enthält gar keine Tokens und gibt sich selbst unverändert aus. Sehen formatierte Datumsangaben seltsam aus, prüf, ob das Layout genau die Referenzwerte verwendet.

Strings in Zeiten parsen

time.Parse(layout, value) nutzt dieselben Layouts und gibt einen Fehler zurück, den du prüfen musst:

Ohne Zone in der Eingabe gibt Parse UTC zurück. Derselbe Uhrzeit-String kann je nach Zone verschiedene Zeitpunkte meinen, und deshalb gibt es ParseInLocation. Die Fehlermeldung nennt den Teil, der nicht gepasst hat, und das hilft beim Debuggen eines Layouts.

Zeitzonen

Eine time.Time ist ein Zeitpunkt plus eine Location für die Anzeige. Die Location mit In zu ändern ändert, wie sie ausgegeben wird, nicht, welcher Moment es ist.

  • time.UTC ist immer verfügbar. Speichere und übertrage Zeiten in UTC (oder RFC 3339 mit Offset) und konvertiere nur für die Anzeige.
  • time.Local ist die Zone des Rechners. Auf Servern und in Containern ist das oft UTC, auf einem Laptop nicht, also verhält sich Code, der davon abhängt, an jedem Ort anders.
  • time.LoadLocation("Europe/Berlin") liest die IANA-Datenbank aus dem Betriebssystem. Minimalen Container-Images fehlt sie oft, und der Aufruf gibt dann einen Fehler zurück. Der Blank Import _ "time/tzdata" bettet die Datenbank in dein Binary ein (etwa 450 KB), damit es immer funktioniert. Behandle den Fehler immer.
  • time.FixedZone(name, offsetSeconds) erzeugt eine Zone mit festem Offset. Sie hat keine Sommerzeitregeln, also nimm sie für Offsets, die du bekommen hast, nicht für benannte Regionen.

Rechnen und Vergleichen

Was dir in der Ausgabe auffallen sollte:

  • AddDate(0, 1, 0) auf den 31. Januar ergibt den 3. März, nicht den 28. Februar. Go addiert einen Monat, bekommt den „31. Februar“ und normalisiert dann den Überlauf. Brauchst du „derselbe Tag im nächsten Monat, begrenzt“, schreib diese Logik selbst.
  • Sub gibt eine Duration zurück, die bei etwa 292 Jahren endet. Für Differenzen in Kalendertagen vergleichst du Daten um Mitternacht UTC und teilst durch 24 Stunden.
  • Den Tagesanfang bekommst du, indem du die Zeit aus Date() neu baust. t.Truncate(24 * time.Hour) rundet relativ zur Null-Zeit in UTC und liefert daher für jede Zone außer UTC die falsche Antwort.

Vergleiche mit Equal, Before, After, nie mit ==. time.Now() enthält eine Ablesung der monotonen Uhr, damit time.Since auch dann korrekt bleibt, wenn die Systemuhr verstellt wird. == vergleicht diese Ablesung und außerdem die Location, also können zwei Time-Werte für denselben Zeitpunkt ungleich sein. Aus demselben Grund solltest du time.Time nicht ohne vorherige Normalisierung als Map-Schlüssel verwenden (t.UTC().Round(0) entfernt die monotone Ablesung).

Die Null-time.Time ist der 1. Januar im Jahr 1, 00:00 UTC. Prüf darauf mit t.IsZero().

Vergangene Zeit messen

start := time.Now()
doWork()
log.Printf("doWork took %v", time.Since(start))

time.Since(start) ist time.Now().Sub(start), und time.Until(deadline) ist deadline.Sub(time.Now()). Beide verwenden die monotone Uhr, wenn sie verfügbar ist, und sind daher sicher gegen Änderungen der Systemuhr. Um Code zu benchmarken, nimm die Benchmark-Unterstützung von Go im Paket testing, statt von Hand zu messen.

Timer und Ticker

time.After(d) gibt einen Channel zurück, der nach d einmal einen Wert empfängt. Ein time.Timer ist dasselbe mit einer Methode Stop. Ein time.Ticker liefert in jeder Periode einen Wert, bis du ihn stoppst.

Die drei Ticks bei 20, 40 und 60 ms kommen lange vor dem Timer bei 110 ms, also stoppt das Programm nach dem dritten Tick. time.AfterFunc(d, f) führt f nach d in einer eigenen Goroutine aus, praktisch für einmalige verzögerte Arbeit.

Seit Go 1.23 werden Timer und Ticker, auf die nichts mehr verweist, vom Garbage Collector eingesammelt, auch wenn du nie Stop aufrufst, und der Channel eines gestoppten oder zurückgesetzten Timers liefert keinen veralteten Wert mehr. Stop mit defer aufzurufen bleibt der klare Weg zu sagen, dass die Lebenszeit des Tickers vorbei ist. Gilt ein Zeitlimit für eine ganze Operation statt für ein einzelnes Warten, liest sich ein context.WithTimeout meist besser als ein Timer.

Zeit in JSON

time.Time wird in JSON automatisch in RFC-3339-Strings mit Nanosekunden-Genauigkeit umgewandelt und daraus gelesen:

type Event struct {
	Name string    `json:"name"`
	At   time.Time `json:"at"`
}
// {"name":"deploy","at":"2026-09-23T14:30:00Z"}

Ein JSON-String in einem anderen Format lässt sich nicht unmarshallen. Für Unix-Zeitstempel oder eigene Formate speicherst du ein int64 oder einen String und konvertierst, oder du definierst einen Typ mit eigenem UnmarshalJSON.

Häufige Fehler

  • time.Sleep(1) oder time.Sleep(n) mit einer nackten Zahl. Das sind Nanosekunden.
  • Layouts mit den falschen Ziffern. "2023-01-01" oder "YYYY-MM-DD" sind keine Layouts. Nimm "2006-01-02".
  • Minuten und Monate verwechseln. Minuten sind 04, Monate 01. "15:01" gibt die Stunde und dann den Monat aus, nicht die Minute.
  • Mit == vergleichen. Nimm Equal.
  • Sich auf time.Local verlassen. Es unterscheidet sich zwischen Rechnern. Sei explizit mit time.UTC oder einer geladenen Location.
  • Den Fehler von LoadLocation oder Parse ignorieren. Beide scheitern an echten Eingaben. Ein gescheitertes Parse gibt die Null-Zeit zurück, die als Jahr 1 erscheint, und ein gescheitertes LoadLocation gibt eine nil-Location zurück, bei der t.In(loc) eine Panic auslöst.

Häufig gestellte Fragen

Wie lasse ich ein Go-Programm schlafen?

Ruf time.Sleep mit einer time.Duration auf: time.Sleep(2 * time.Second) oder time.Sleep(500 * time.Millisecond). Eine nackte Zahl wie time.Sleep(2) kompiliert, schläft aber 2 Nanosekunden, weil eine Duration Nanosekunden zählt. Sleep blockiert nur die aktuelle Goroutine.

Warum verwendet Go 2006-01-02 15:04:05 für Datumsformate?

Go formatiert Datumsangaben nach Beispiel. Das Layout ist die Referenzzeit Mon Jan 2 15:04:05 MST 2006, geschrieben so, wie deine Ausgabe aussehen soll. Ihre Teile zählen in amerikanischer Reihenfolge hoch: Monat 1, Tag 2, Stunde 3 (15 im 24-Stunden-Format), Minute 4, Sekunde 5, Jahr 6 (2006), Zonen-Offset 7 (-0700). Also bedeutet "2006-01-02" Jahr-Monat-Tag und "02/01/2006" Tag/Monat/Jahr.

Wie parse ich in Go einen Datums-String?

Mit time.Parse(layout, value) und einem Layout in der Referenzzeit: t, err := time.Parse("2006-01-02", "2026-09-23"). Prüf immer err. Ohne Zoneninformation im String ist das Ergebnis in UTC; mit time.ParseInLocation interpretierst du ihn in einer anderen Zone.

Wie bekomme ich in Go einen Unix-Zeitstempel?

time.Now().Unix() gibt die Sekunden seit dem 1. Januar 1970 UTC als int64 zurück. UnixMilli(), UnixMicro() und UnixNano() liefern feinere Einheiten. Für den umgekehrten Weg nimm time.Unix(sec, 0) oder time.UnixMilli(ms).

Wie vergleiche ich in Go zwei Zeiten?

Mit t1.Before(t2), t1.After(t2) und t1.Equal(t2). Nimm nicht ==: Das vergleicht auch die Location und die Ablesung der monotonen Uhr, also können zwei Werte für denselben Zeitpunkt ungleich sein. t2.Sub(t1) liefert die Differenz als Duration.

Coddy programming languages illustration

Lerne mit Coddy zu programmieren

LOS GEHT'S