Tutorial: Die fünf Zeitstempelformate eines Tracking-Stapels und ihre Umrechnung

Inhalt
Ein einziger Klick erzeugt einen Zeitstempel im Browser, einen weiteren im Tag-Manager, einen dritten im Analyse-Export und einen vierten in der Werbeplattform, an die er weitergereicht wird. Alle vier beschreiben denselben Augenblick. Keiner ist gleich geschrieben.
Der größte Teil der Verwirrung löst sich durch Ziffernzählen. Der Teil, der übrig bleibt, ist eine Tagesgrenze – und die lohnt sich zu kennen, bevor ein Bericht mit einem anderen verglichen wird.

Fünf Formate, die in einem Stapel zusammentreffen
Alle zählen vom selben Nullpunkt: Mitternacht am 1. Januar 1970, in UTC. Verschieden sind Einheit und Verpackung.
| Format | Beispiel | Wo es auftritt |
|---|---|---|
| Sekunden | 1790465400 | Meta-CAPI event_time, die meisten REST-Schnittstellen |
| Millisekunden | 1790465400000 | Date.now(), gtm.start, das meiste JavaScript |
| Mikrosekunden | 1790465400000000 | GA4-BigQuery event_timestamp |
| ISO 8601 | 2026-09-27T01:30:00+02:00 | Protokolle, Search-Console-API, die meisten Exporte |
| Nur Datum | 20260927 | GA4 event_date, BigQuery-Tabellensuffix |
Die ersten drei sind eindeutig – eine Anzahl Einheiten seit einem festen Punkt und tragen keine Zeitzone, weil sie keine brauchen. Das vierte ist eindeutig, solange es einen Versatz trägt – das +02:00 am Ende macht daraus einen Augenblick statt einer Beschreibung.
Das fünfte ist von anderer Art. Ein Datum ohne Uhrzeit ist kein Augenblick, sondern ein Bereich von vierundzwanzig Stunden – und welche vierundzwanzig Stunden das sind, hängt an einer Zeitzone, die im Wert nirgends steht.
Die Formate durch Ziffernzählen unterscheiden
Für eine Zahl aus einem Protokoll, einem Export oder einer Nutzlast entscheidet die Länge.
10 Stellen Sekunden 1790465400 bis zum Jahr 2286
13 Stellen Millisekunden 1790465400000
16 Stellen Mikrosekunden 1790465400000000
8 Stellen Datum 20260927 gar kein Augenblick
Aus dem Übergehen entstehen zwei Fehler, und beide erzeugen ein Ergebnis, das plausibel aussieht. Millisekunden als Sekunden zu lesen verschiebt das Datum ins Jahr 58 707 – offensichtlich falsch und damit harmlos. Sekunden als Millisekunden zu lesen verschiebt es auf den 21. Januar 1970, was überhaupt nicht offensichtlich falsch ist und in einem Bericht als winziger Balken ganz links in jedem Diagramm erscheint.
Eine Absicherung kostet drei Zeilen und lohnt sich überall dort, wo ein Wert von außen ankommt.
def zu_sekunden(wert):
z = len(str(int(wert)))
if z == 16: return int(wert) / 1_000_000
if z == 13: return int(wert) / 1_000
if z == 10: return int(wert)
raise ValueError(f"unbekanntes Zeitformat mit {z} Stellen: {wert}")
Der Tag, der nicht derselbe Tag ist
Der GA4-BigQuery-Export trägt sowohl einen Augenblick als auch ein Datum, und beide folgen verschiedenen Konventionen.
event_timestamp sind Mikrosekunden seit der Epoche und damit UTC. event_date ist eine Zeichenkette der Form YYYYMMDD in der Berichtszeitzone der Property. Bei einer Property mit Einstellung Berlin trägt deshalb jedes Ereignis zwischen Mitternacht und zwei Uhr Ortszeit ein Datum, das einen Tag weiter liegt als das, was der eigene Zeitstempel in UTC sagt.
Ein Ereignis am 27.09.2026 um 01:30 Uhr Berliner Zeit
event_date 20260927
DATE(TIMESTAMP_MICROS(event_timestamp)) 2026-09-26
DATE(TIMESTAMP_MICROS(event_timestamp),
"Europe/Berlin") 2026-09-27
Das ist kein Fehler und lässt sich nicht abschalten. Es ist der Grund, weshalb zwei Abfragen über dieselbe Tabelle verschiedene Tagessummen liefern können – und der Unterschied hat immer dieselbe Größe: die Ereignisse der ersten ein bis zwei Stunden jedes Tages.
Die Regel daraus ist kurz. Eine Gruppierung nach Tagen verwendet entweder durchgehend event_date oder durchgehend DATE(..., "Europe/Berlin"), nie beides gemischt – und eine Abfrage, die zwei Tabellen verbindet, muss auf beiden Seiten dieselbe Konvention benutzen. Das Tabellensuffix _TABLE_SUFFIX folgt event_date, weshalb ein Datumsfilter auf dem Suffix und einer auf dem umgerechneten Zeitstempel verschiedene Zeilen auswählen.
In BigQuery umrechnen
Drei Funktionen decken jeden Fall ab, und jede trägt die Einheit im Namen.
SELECT
event_timestamp,
TIMESTAMP_MICROS(event_timestamp) AS moment_utc,
DATETIME(TIMESTAMP_MICROS(event_timestamp),
"Europe/Berlin") AS ortszeit,
DATE(TIMESTAMP_MICROS(event_timestamp), "Europe/Berlin") AS tag_lokal,
FORMAT_TIMESTAMP("%FT%T%Ez",
TIMESTAMP_MICROS(event_timestamp),
"Europe/Berlin") AS iso_mit_versatz,
UNIX_SECONDS(TIMESTAMP_MICROS(event_timestamp)) AS sekunden
FROM `projekt.analytics_123456789.events_*`
WHERE _TABLE_SUFFIX = "20260927"
LIMIT 10;
Der Unterschied zwischen TIMESTAMP und DATETIME lohnt sich zu verinnerlichen. Ein TIMESTAMP ist ein Augenblick und weiß, was er ist; ein DATETIME ist eine Uhrzeitablesung ohne Zone, und einen Zeitstempel in eine solche zu verwandeln ist genau die Stelle, an der die Zoneninformation absichtlich verworfen wird. Ein gespeichertes DATETIME anderswo wieder einzulesen ist der Weg, auf dem eine Stunde verschwindet.
In die andere Richtung wird aus einer Zeichenkette nur mit angegebenem Format ein Zeitstempel – und die Formatangabe ist die Stelle, an der ein Versatz entweder gelesen oder erfunden wird.
PARSE_TIMESTAMP("%FT%T%Ez", "2026-09-27T01:30:00+02:00") -- korrekt
PARSE_TIMESTAMP("%FT%T", "2026-09-27T01:30:00") -- als UTC gelesen
Die Fenster, die einen Zeitstempel abweisen
Zwei empfangende Systeme prüfen den Wert, statt ihn nur zu speichern, und beide scheitern auf eine leicht zu übersehende Weise.
Die Meta Conversions API nimmt ein event_time innerhalb der letzten sieben Tage an. Ein älteres Ereignis wird abgewiesen, und ein Stapelupload historischer Abschlüsse verliert deshalb stillschweigend alles jenseits des Fensters – die Antwort meldet, wie viele Ereignisse empfangen wurden, nicht wie viele behalten. Die Gegenprobe ist die Zahl in der Antwort gegen die Zahl der gesendeten.
Ein Zeitstempel in der Zukunft ist die andere Hälfte derselben Regel und kommt häufiger vor als die Vergangenheit: Ein Server, dessen Uhr einige Minuten vorgeht, erzeugt Ereignisse, die die Plattform ablehnt. Ein scheiternder Abschluss-Upload lohnt sich deshalb als Uhrenproblem zu untersuchen, bevor er als Datenproblem untersucht wird.
Uploads von Offline-Abschlüssen an Werbeplattformen wollen meist eine Ortszeit mit ausdrücklichem Versatz statt eines Epochenwerts.
2026-09-27 01:30:00+02:00
Der Versatz ist keine Zierde. Ohne ihn wendet die Plattform die Zeitzone des Kontos an, und ein Konto in einer anderen Zone als der Shop verschiebt jeden Abschluss um die Differenz – was als Abschlüsse am falschen Tag erscheint und an einer Monatsgrenze im falschen Monat.
Die zwei Mehrdeutigkeiten, die sich nachträglich nicht beheben lassen
Alles Bisherige ist eine Umrechnung. Diese beiden sind Verluste, und das einzige Mittel ist, sie nicht entstehen zu lassen.
Die erste ist eine Ortszeit ohne Versatz. 2026-09-27 01:30:00 ist kein Augenblick, sondern ein Augenblick in irgendeiner Zone – und wurde die Zone nicht mitgeschrieben, holt keine spätere Verarbeitung sie zurück. Eine Vermutung ist möglich und bleibt eine Vermutung: Dieselbe Zeichenkette kann zwei Augenblicke im Abstand einer Stunde meinen.
Die zweite ist die Umstellungsnacht. Bei der Rückstellung geschieht die Stunde zwischen zwei und drei Uhr zweimal, und eine Ortszeit darin ist selbst bei bekannter Zone wirklich mehrdeutig. Ein Versatz löst das auf, denn die beiden Durchgänge haben verschiedene Versätze. Ein Zonenname allein tut es nicht.
Beides verschwindet, wenn Werte als UTC gespeichert und nur zur Anzeige umgerechnet werden. Das ist eine einzeilige Regel, die selbstverständlich klingt und ständig gebrochen wird, denn eine Ortszeit ist das, was ein Mensch lesen möchte – und die Abhilfe besteht darin, beim Lesen umzurechnen statt beim Schreiben.
Fragen und Antworten
Ein Wert hat 19 Stellen. Was ist das?
Mit großer Wahrscheinlichkeit Nanosekunden seit der Epoche. Dieses Format verwenden zum Beispiel OpenTelemetry und die Funktion UnixNano in Go; in einem Tracking-Stapel taucht es vor allem in Server- und Protokolldaten auf. Die Absicherung weist es zu Recht ab, statt zu raten, und lässt sich um eine Zeile für 19 Stellen mit dem Teiler 1_000_000_000 ergänzen.
Verarbeitet JavaScript 16-stellige Mikrosekunden verlustfrei?
Heute ja, aber knapp. JavaScript speichert Zahlen als Gleitkommazahlen doppelter Genauigkeit, und ganze Zahlen sind damit nur bis 9 007 199 254 740 991 exakt (Number.MAX_SAFE_INTEGER). Ein Mikrosekundenwert aus dem Jahr 2026 liegt mit 1 790 465 400 000 000 rund fünfmal darunter; in Mikrosekunden gerechnet reicht die Grenze bis ins Jahr 2255.
Nanosekunden liegen dagegen weit darüber. Ein 19-stelliger Wert verliert beim Einlesen mit JSON.parse seine letzten Stellen, ohne dass ein Fehler auftritt. Werden solche Werte in JavaScript gebraucht, sind sie als Zeichenkette einzulesen und mit BigInt umzuwandeln oder schon auf dem Server auf Millisekunden herunterzuteilen.
Warum nicht einfach die Zeitzone der Property auf UTC stellen?
Das beseitigt den Unterschied zwischen event_date und dem Zeitstempel, verschiebt das Problem aber an eine andere Stelle. Alle Tagesberichte in GA4 beginnen dann um Mitternacht UTC, für einen Shop in Berlin also um ein Uhr im Winter und um zwei Uhr im Sommer. Ein Einkauf um halb eins in der Nacht zählt zum Vortag, und die Tageswerte passen nicht mehr zu Kasse, Warenwirtschaft und Werbekonten, die in Ortszeit rechnen.
Dazu kommt der Wechsel selbst. Eine geänderte Berichtszeitzone gilt nur für Daten ab der Umstellung, frühere Tage bleiben, wie sie waren. Der Tag der Umstellung ist deshalb kürzer oder länger als vierundzwanzig Stunden, und eine Zeitreihe über den Wechsel hinweg stellt Berliner Tage neben UTC-Tage.
Tragfähiger ist die Regel aus dem Artikel: Die Property behält die Zone, in der das Geschäft rechnet, und jede Abfrage entscheidet sich für eine Konvention. Eine Gruppierung nach Berliner Tagen in BigQuery hat dann dieselben Tagesgrenzen wie die Oberfläche, und eine Gruppierung nach UTC ist die bewusste Wahl eines anderen Tages.
Wie lässt sich prüfen, ob die Serveruhr hinter einem abgewiesenen Upload steckt?
Mit zwei Proben. Auf dem Server selbst zeigt timedatectl unter Linux, ob die Uhr mit einem Zeitserver abgeglichen wird; steht in der Zeile System clock synchronized der Wert no, ist eine abweichende Uhr wahrscheinlich. Die zweite Probe braucht keinen Zugang zum Betriebssystem: Jede HTTP-Antwort der Plattform trägt im Header Date die Uhrzeit ihres Servers, und der Vergleich mit der eigenen Uhrzeit im selben Augenblick zeigt die Abweichung auf etwa eine Sekunde genau.
Liegt sie im Bereich von Minuten, verschwindet sie, sobald der Abgleich mit einem Zeitserver läuft. Ereignisse, die mit falscher Zeit gesendet und abgewiesen wurden, sind danach mit korrigiertem Zeitstempel erneut zu senden, solange sie noch im Fenster von sieben Tagen liegen.