Einleitung: Warum Zeit mehr als nur Tick-Tack ist
\n\nWenn Sie jemals eine Go-Anwendung entwickelt haben, die mit Daten und Benutzern aus verschiedenen Ländern arbeitet, kennen Sie sicherlich das Kopfzerbrechen, das „Zeitzonen“ verursachen können. Es scheint so einfach: time.Now() – und fertig. Aber nur so lange, bis Ihr Server in UTC läuft und Ihr Kunde in MEZ+1, und Ihr „heute“ sich auf magische Weise in „gestern“ verwandelt.
In diesem Artikel zeigen wir Ihnen, wie Sie in Go professionell mit Zeitzonen umgehen – anhand realer Beispiele, ohne überflüssiges Wissen und mit einsatzbereitem Code. Sie erfahren, wie Sie Datenverlust vermeiden, die Fehlersuche erleichtern und Ihre Anwendung zeitzonenresistent machen. Los geht's!
\n\nGrundlagen: Was hinter time.Time steckt
\n\nIn Go ist der Typ time.Time nicht einfach eine Zahl von Millisekunden. Es ist eine Struktur, die zwei wesentliche Komponenten speichert:
- \n
- Absolute Zeit – die Anzahl der Nanosekunden seit dem 1. Januar 1970 (Unix-Timestamp). \n
- Location – ein Zeiger auf die Zeitzone. \n
Genau dieses Location bestimmt, wie die Zeit angezeigt wird: als 14:00 MEZ oder als 12:00 UTC. Der gefährlichste Irrglaube von Anfängern ist, dass time.Now() die „lokale Zeit“ zurückgibt. Tatsächlich liefert es die aktuelle Zeit mit der lokalen Zone des Servers. Verschieben Sie den Code auf einen anderen Server – und alles „schwimmt“.
Zeit mit einer bestimmten Zone erstellen
\n\nDer einfachste Weg ist die Verwendung von time.LoadLocation. Es lädt die Zone aus der systemweiten IANA-Datenbank (normalerweise in /usr/share/zoneinfo).
package main\n\nimport (\n "fmt"\n "time"\n)\n\nfunc main() {\n loc, err := time.LoadLocation("Europe/Berlin")\n if err != nil {\n panic(err)\n }\n\n // Zeit in einer bestimmten Zone erstellen\n t := time.Date(2025, 3, 15, 14, 30, 0, 0, loc)\n fmt.Println(t) // 2025-03-15 14:30:00 +0100 CET\n}\n\n\nBeachten Sie: time.Date erwartet das Location als letztes Argument. Wenn Sie time.UTC oder time.Local übergeben, verhält es sich anders.
Was tun, wenn die Zone nicht im System vorhanden ist?
\n\nManchmal (z. B. in minimalen Docker-Images) fehlt die Zoneinfo-Datenbank. Die Lösung ist die Verwendung von time.FixedZone, um eine Zone mit einem festen Offset zu erstellen:
loc := time.FixedZone("CET", 1*60*60) // +1 Stunde von UTC\nt := time.Now().In(loc)\nfmt.Println(t)\n\n\nNachteil: FixedZone kennt keine Sommerzeit oder historische Änderungen. Für die meisten modernen Anwendungen ist das akzeptabel, aber wenn Sie historische Genauigkeit benötigen (z. B. für Finanztransaktionen aus dem Jahr 2014), verwenden Sie LoadLocation.
Die goldene Regel der Backend-Entwicklung: Speichern in UTC
\n\nDie goldene Regel für die Arbeit mit Zeitzonen in Go (und jeder anderen Sprache) lautet: Speichern Sie die Zeit immer in UTC und konvertieren Sie sie nur auf der Anzeigeebene. Warum?
\n\n- \n
- Der Server könnte in eine andere Zeitzone verlegt werden. \n
- Benutzer können aus verschiedenen Zonen kommen. \n
- Vergleich und Sortierung von UTC-Zeiten sind trivial. \n
- Logs sind einfacher zu analysieren. \n
So sieht das in der Praxis aus:
\n\n// Benutzerzeit abrufen (z. B. aus einem Formular)\nuserTime := "2025-03-15 14:30"\nuserLoc, _ := time.LoadLocation("Asia/Tokyo")\n\n// Als lokale Benutzerzeit parsen\nt, _ := time.ParseInLocation("2006-01-02 15:04", userTime, userLoc)\n\n// Für die Speicherung in UTC konvertieren\nutcTime := t.UTC()\nfmt.Println(utcTime) // 2025-03-15 05:30:00 +0000 UTC\n\n\nTypischer Fehler: Location bei der Serialisierung zurücksetzen
\n\nWenn Sie time.Time in einer Datenbank speichern (z. B. PostgreSQL über database/sql), kann der Treiber das Location auf UTC oder Local zurücksetzen. Um Überraschungen zu vermeiden, konvertieren Sie immer