Tutorial: Pięć formatów znacznika czasu w stosie pomiarowym i ich przeliczanie

Management Summary
Znaczniki czasu określają w analityce moment zakupu lub innego działania. Różne narzędzia używają odmiennych jednostek i stref, dlatego pomyłki mogą przypisać to samo zdarzenie do niewłaściwego dnia raportowego. Wspólny punkt odniesienia UTC, czyli światowy standard czasu, ułatwia porównania. Czas zdarzenia nadal trzeba odróżniać od czasu otrzymania danych. Spójna obsługa pomaga uzgadniać raporty sprzedaży, szczególnie w pobliżu północy oraz przy zmianie czasu.
Spis treści
- 1. Rozróżnienie pięciu częstych zapisów
- 2. Przeliczenie z jawną jednostką
- 3. Oddzielenie daty raportowej GA4 od znacznika czasu
- 4. Funkcje BigQuery zgodne z jednostką
- 5. Zmiana czasu tworzy powtórzone i nieistniejące godziny
- 6. Zasady interfejsu odbierającego dane
- Pytania i odpowiedzi
- Źródła i dokumentacja techniczna
Zdarzenie pomiarowe może występować jako sekundy Unix, milisekundy, mikrosekundy albo tekst z przesunięciem względem UTC. Przesunięcie określa różnicę względem UTC: +02:00 oznacza dwie godziny do przodu. Gdy czas UTC wynosi 12:00, zegar z przesunięciem +02:00 wskazuje 14:00. Raporty zapisują także datę kalendarzową. Poprawne przeliczenie wymaga trzech informacji: jednostki, znaczenia pola i docelowej strefy czasowej.
Czas przeglądarki, uruchomienie kontenera, czas zdarzenia i czas odbioru nie oznaczają automatycznie tej samej chwili. Dopiero porównanie znaczeń pozwala sensownie zestawić liczby. Liczba cyfr stanowi wskazówkę, a nie definicję pola.

1. Rozróżnienie pięciu częstych zapisów
Poniższe przykłady odnoszą się do 2026-09-26T23:30:00Z, czyli godziny 01:30 dnia 27 września 2026 w Berlinie. Tylko trzy pierwsze formaty liczą jednostki od epoki Unix: 1 stycznia 1970 o 00:00 UTC. Zapis ISO przedstawia pola kalendarza, a sama data nie wskazuje pojedynczej chwili.
| Zapis | Przykład | Znaczenie |
|---|---|---|
| Sekundy | 1790465400 | Sekundy Unix, na przykład Meta event_time |
| Milisekundy | 1790465400000 | Milisekundy Unix, na przykład Date.now() |
| Mikrosekundy | 1790465400000000 | Mikrosekundy Unix, na przykład GA4 event_timestamp |
| ISO 8601 / RFC 3339 | 2026-09-27T01:30:00+02:00 | Pola kalendarza z jawnym przesunięciem |
| Data kalendarzowa | 20260927 | Data w Berlinie w tym przykładzie, nie chwila |
Date.now() zwraca milisekundy bieżącego zegara systemowego. W standardowej instalacji GTM gtm.start oznacza inicjalizację kontenera, a nie każde późniejsze kliknięcie. Poprawna jednostka może więc występować razem z błędnym znaczeniem.
Dla wartości z 2026 roku dziesięć, 13 i 16 cyfr to typowe długości sekund, milisekund i mikrosekund. Starsze lub ujemne znaczniki, inne epoki oraz zwykłe identyfikatory nie pasują do takiej reguły rozpoznawania. Osiem cyfr również nie potwierdza prawidłowej daty: 20260230 nie jest dniem kalendarzowym.
2. Przeliczenie z jawną jednostką
Funkcja przyjmuje liczby całkowite lub ciągi cyfr dziesiętnych z opcjonalnym znakiem. Wartość i jednostka są osobnymi argumentami. Liczby zmiennoprzecinkowe, wartości logiczne i tekst z częścią ułamkową są odrzucane, aby uniknąć niezauważonego zaokrąglenia. Nanosekundy są dopuszczane wyłącznie przy dokładnej konwersji na mikrosekundy.
import re
from datetime import datetime, timedelta, timezone
EPOCH = datetime(1970, 1, 1, tzinfo=timezone.utc)
def epoch_to_utc(value, unit):
if isinstance(value, bool):
raise TypeError("boolean is not a timestamp")
if isinstance(value, str):
if not re.fullmatch(r"[+-]?[0-9]{1,20}", value):
raise ValueError("expected an integer string")
value = int(value)
elif not isinstance(value, int):
raise TypeError("expected an integer or integer string")
factors = {"s": 1_000_000, "ms": 1_000, "us": 1}
if unit == "ns":
micros, remainder = divmod(value, 1_000)
if remainder:
raise ValueError("nanoseconds would lose precision")
elif unit in factors:
micros = value * factors[unit]
else:
raise ValueError("unit must be s, ms, us or ns")
try:
return EPOCH + timedelta(microseconds=micros)
except OverflowError as exc:
raise ValueError("outside Python datetime range") from exc
print(epoch_to_utc("1790465400000000", "us").isoformat())
# 2026-09-26T23:30:00+00:00Wynik to datetime w UTC z rozdzielczością mikrosekund. Funkcja sprawdza typ, jednostkę i zakres Pythona, ale nie biznesowe terminy przesyłania. Zgodność daty ze zdarzeniem wymaga dodatkowej oceny na podstawie źródła. Błędnie zadeklarowana jednostka nadal może dać technicznie poprawną, lecz merytorycznie błędną datę.
Przykładowo 1790465400 odczytane jako milisekundy daje 1970-01-21T17:21:05.400Z. Odczyt milisekund jako sekund może utworzyć bardzo odległą datę albo błąd zakresu. Nawet oczywiście błędna data nie jest nieszkodliwa, gdy interfejs lub raport przetwarza wartość dalej.
3. Oddzielenie daty raportowej GA4 od znacznika czasu
Aktualny schemat eksportu GA4 opisuje event_timestamp jako czas odbioru przez Google Analytics w mikrosekundach UTC. Kilka zdarzeń z tego samego żądania może mieć jednakowy znacznik. Wartość nie jest więc unikalnym kluczem zdarzenia ani, bez dodatkowej kontroli, dokładnym czasem kliknięcia.
event_date to zarejestrowana data w formacie YYYYMMDD według skonfigurowanej strefy raportowania. Tabele dzienne mają nazwy events_YYYYMMDD. Lokalnej daty obliczonej ze znacznika odbioru nie należy automatycznie utożsamiać z zapisaną datą zdarzenia: opóźnienia, znaczenie czasu i zmiany strefy raportowania wymagają osobnego uwzględnienia.
Dla samego porównania stref tego samego przykładowego momentu:
UTC 2026-09-26T23:30:00Z
Europe/Berlin 2026-09-27T01:30:00+02:00
Data UTC 2026-09-26
Data w Berlinie 2026-09-27We wrześniu Berlin wyprzedza UTC o dwie godziny, a zimą zwykle o godzinę. Raport według dnia lokalnego i raport według UTC wybierają zatem różne przedziały. Różnica sum dziennych nie jest stała: zdarzenia przesuwają się przy obu granicach, a liczby zależą od rzeczywistej aktywności.
Porównania wymagają zgodnych definicji dnia, znaczeń zdarzeń, filtrów i aktualności danych. Filtr tabel wybiera tabele eksportu; dodatkowy filtr znacznika wybiera chwile wewnątrz tych tabel. Odtworzenie innego dnia raportowego wymaga także uwzględnienia tabel przy granicach oraz spóźnionych danych. LIMIT ogranicza jedynie wynik i nie zapewnia ograniczenia liczby odczytanych bajtów.
4. Funkcje BigQuery zgodne z jednostką
TIMESTAMP_SECONDS, TIMESTAMP_MILLIS i TIMESTAMP_MICROS wymagają jednostki wskazanej w nazwie. Przykład wybiera kilka pól z jednej jawnie nazwanej tabeli dziennej. Projekt, zbiór danych, data i strefa raportowania wymagają dopasowania.
SELECT
event_date,
event_timestamp,
TIMESTAMP_MICROS(event_timestamp) AS timestamp_utc,
DATETIME(TIMESTAMP_MICROS(event_timestamp),
'Europe/Berlin') AS wall_clock_berlin,
DATE(TIMESTAMP_MICROS(event_timestamp)) AS date_utc,
DATE(TIMESTAMP_MICROS(event_timestamp),
'Europe/Berlin') AS timestamp_date_berlin,
FORMAT_TIMESTAMP('%Y-%m-%dT%H:%M:%E6S%Ez',
TIMESTAMP_MICROS(event_timestamp),
'Europe/Berlin') AS text_with_offset,
UNIX_SECONDS(TIMESTAMP_MICROS(event_timestamp)) AS whole_seconds
FROM `project_id.analytics_123456789.events_20260927`
LIMIT 10;TIMESTAMP oznacza konkretną chwilę. DATETIME przechowuje datę kalendarzową i wskazanie zegara bez strefy. Lokalny zapis jest przydatny, lecz jednoznaczne przeliczenie wstecz wymaga dodatkowych informacji. %E6S zachowuje sześć cyfr części ułamkowej w tekście; UNIX_SECONDS zaokrągla w dół do początku sekundy i odrzuca mikrosekundy.
Odczyt tekstu z jawnym przesunięciem, jawną strefą lub domyślnym UTC daje różne wyniki:
SELECT
PARSE_TIMESTAMP('%FT%T%Ez', '2026-09-27T01:30:00+02:00') AS explicit_offset,
PARSE_TIMESTAMP('%FT%T', '2026-09-27T01:30:00',
'Europe/Berlin') AS explicit_zone,
PARSE_TIMESTAMP('%FT%T', '2026-09-27T01:30:00') AS utc_default;Dwa pierwsze wyniki wskazują 2026-09-26T23:30:00Z, trzeci natomiast 2026-09-27T01:30:00Z, czyli dwie godziny później. Sam wzorzec formatu nie ustala poprawnej strefy czasowej. Inne obsługiwane funkcje parsowania również potrzebują jasnych zasad dotyczących jednostki, formatu i strefy czasowej danych wejściowych.
5. Zmiana czasu tworzy powtórzone i nieistniejące godziny
W Europe/Berlin godzina 02:30 występuje 25 października 2026 dwa razy: z +02:00 oznacza 00:30 UTC, a z +01:00 oznacza 01:30 UTC. Sama nazwa strefy nie rozstrzyga wieloznaczności. Potrzebne jest znane przesunięcie lub jawny wybór pierwszego albo drugiego wystąpienia.
Z kolei 29 marca 2026 godzina 02:30 w Berlinie nie istnieje, ponieważ zegar przeskakuje z 02:00 na 03:00. Parser może przesunąć, odrzucić albo inaczej rozstrzygnąć taką wartość zgodnie z przyjętą regułą. Ciche ustawienie domyślne nie odtwarza rzeczywistego zdarzenia.
Te dni kalendarzowe trwają odpowiednio 25 i 23 godziny. Granice dnia wynikają więc z lokalnej północy i następnej lokalnej północy, przeliczonych osobno na UTC. Dodanie 86 400 sekund nie jest ogólnym zamiennikiem.
Dla zdarzeń już zakończonych przydatny jest zapis jednoznacznie ustalonej chwili UTC. Pierwotna strefa i tekst źródłowy mogą pozostać jako dodatkowy ślad. Zapis UTC nie naprawia jednak wcześniejszej błędnej interpretacji czasu lokalnego.
6. Zasady interfejsu odbierającego dane
Meta CAPI używa dla event_time sekund Unix rzeczywistego zdarzenia. Niezmieniona wartość Date.now() nie jest więc odpowiednia. Dopuszczalny wiek zdarzenia i obsługa błędów zależą od konkretnego interfejsu oraz źródła zdarzenia; poprawna odpowiedź HTTP nie dowodzi pełnego przetworzenia ani atrybucji. Osobnej analizy wymagają obiekty błędów, szczegóły odpowiedzi i diagnostyka.
Google Ads API wymaga strefy czasowej w ClickConversion.conversion_date_time, w formacie yyyy-mm-dd hh:mm:ss+|-hh:mm. Pasujący przykład to 2026-09-27 01:30:00+02:00. Braku przesunięcia nie należy ogólnie traktować jako automatycznego użycia strefy konta. Inne drogi przesyłania mają oddzielne wymagania.
Przy ponowieniu nadal obowiązuje rzeczywisty czas zdarzenia. Zastąpienie starego czasu wartością „teraz” w celu obejścia terminu fałszuje dane. Po potwierdzonej korekcie zegara pierwotny czas, dopuszczalne okno i ochrona przed duplikatami wymagają wspólnej kontroli.
Pytania i odpowiedzi
Czy 16-cyfrowe mikrosekundy są zawsze dokładne w JavaScripcie?
Nie. Number.MAX_SAFE_INTEGER wynosi 9007199254740991. Przykład z 2026 roku jest mniejszy, ale część liczb 16-cyfrowych jest większa. W mikrosekundach granica odpowiada 5 czerwca 2255 o 23:47:34.740991 UTC. Date nadal operuje milisekundami; dokładnej liczby całkowitej mikrosekund nie można przekazać bez zmiany jako wartości Date.
Co dzieje się z nanosekundami w JSON?
Obecne nanosekundy Unix przekraczają bezpieczny zakres liczb całkowitych JavaScriptu. Niektóre wartości pozostają przypadkowo reprezentowalne, ale sąsiednie już nie. Ciąg cyfr dziesiętnych i późniejsze BigInt zachowują liczbę. Konwersja na BigInt po zaokrągleniu podczas odczytu jako Number nie odzyskuje utraconych cyfr. Redukcja do milisekund świadomie zmniejsza dokładność.
Czy UTC jako strefa usługi usuwa wszystkie rozbieżności dzienne?
UTC ujednolica wybraną granicę dnia, lecz nie różne znaczenia pól, przetwarzanie ani atrybucję. Dla dnia działalności w Berlinie lokalna strefa nadal może być właściwa. Zmiana strefy usługi dotyczy przyszłych danych i może wywołać przejściowe efekty w raportach; cała historia nie jest przeliczana ponownie.
Jak zbadać nieprawidłowy zegar serwera?
timedatectl status pokazuje stan synchronizacji w odpowiednich systemach Linux. Brak synchronizacji nie potwierdza konkretnej różnicy czasu. Istotne są również odchylenie wskazywane przez używaną usługę czasu, ostatnia udana synchronizacja i dostępność źródła. Nagłówek HTTP Date daje tylko przybliżoną kontrolę: pamięć podręczna, czas transmisji, dokładność do pełnych sekund oraz infrastruktura odpowiedzi ograniczają wnioski. Dokładność pomiaru do jednej sekundy nie jest gwarantowana.


