0Pricing
SQL Interview Prep · Lekcja

Strefy czasowe i znaczniki czasu

Przechowywanie czasu UTC, konwersja stref oraz pułapki dotyczące znaczników czasu poruszane przez rekruterów.

Strefy czasowe i znaczniki czasu to bezpłatna lekcja SQL Interview Prep na CoddyKit. To lekcja 4 z 4. Możesz przeczytać całą lekcję poniżej za darmo — a potem ćwiczyć ją interaktywnie w przeglądarce z wbudowanym edytorem kodu i tutorem AI dostępnym 24/7. To część ścieżki edukacyjnej SQL Interview Prep, a Twój postęp synchronizuje się między webem a aplikacją CoddyKit. Kurs SQL Interview Prep zawiera 4 lekcji w sumie.

Dlaczego strefy czasowe sprawiają kandydatom trudność

Strefy czasowe to temat, na którym potykają się pewni siebie kandydaci, dlatego osoby prowadzące rozmowy kwalifikacyjne sprawdzają go, aby ocenić poziom ich wiedzy. Podstawowe pytanie zawsze brzmi: „jak przechowują i porównują Państwo znaczniki czasu pochodzące z różnych regionów?”

Profesjonalna odpowiedź to nie funkcja, lecz zasada: przechowywać wszystko w UTC, a konwertować wyłącznie na obrzeżach systemu, podczas wyświetlania. Gdy model przechowywania jest poprawny, większość zapytań staje się prosta.

  • timestamp a timestamptz
  • Konwersja między strefami
  • UTC jako źródło prawdy

timestamp a timestamptz

PostgreSQL ma dwa typy znaczników czasu, a ich pomylenie jest jednym z najczęstszych błędów podczas rozmów kwalifikacyjnych.

  • timestamp (bez strefy czasowej): wartość czasu zegarowego z nieprzypisaną strefą. Przechowuje dokładnie to, co zostanie przekazane.
  • timestamptz (ze strefą czasową): wewnętrznie przechowywany jako UTC; przy wprowadzaniu konwertuje wartość ze strefy sesji, a przy wyprowadzaniu konwertuje ją z powrotem.

Wbrew nazwie timestamptz nie przechowuje strefy; przechowuje dokładny moment w czasie w UTC. Ten szczegół robi dobre wrażenie podczas rozmowy kwalifikacyjnej.

CREATE TABLE events (
  id          bigint,
  occurred_at timestamptz   -- recommended: an absolute instant
);

Przechowywanie w UTC i konwersja na obrzeżach systemu

Złota zasada. Należy zapisywać momenty w UTC (używając timestamptz), a na lokalną strefę konwertować je dopiero podczas prezentowania użytkownikowi. Pozwala to uniknąć niejednoznaczności związanych ze zmianą czasu i zapewnia poprawne sortowanie według czasu w każdym miejscu.

Jeśli padnie pytanie „dlaczego UTC?”, proszę odpowiedzieć: UTC nie podlega zmianom czasu letniego, więc ta sama wartość czasu zegarowego nigdy nie występuje dwukrotnie ani nie jest pomijana, w przeciwieństwie do czasu lokalnego.

-- Display a UTC instant in a user's zone (Postgres)
SELECT occurred_at AT TIME ZONE 'America/New_York' AS local_time
FROM events;

Podwójne znaczenie AT TIME ZONE

AT TIME ZONE to sprytna konstrukcja i częste źródło pomyłek, ponieważ w zależności od typu danych wykonuje dwie przeciwne operacje:

  • Zastosowana do timestamptz konwertuje absolutny moment do danej strefy i zwraca zwykły timestamp (czas wskazywany przez zegar w tej strefie).
  • Zastosowana do zwykłego timestamp interpretuje ten czas zegarowy jako czas w danej strefie i zwraca wartość timestamptz.

Cała sztuka polega na rozpoznaniu, w którym kierunku działa konwersja.

-- timestamptz -> local wall clock (returns timestamp)
SELECT TIMESTAMPTZ '2024-03-01 12:00:00+00'
         AT TIME ZONE 'Asia/Tokyo';        -- 2024-03-01 21:00:00

-- plain timestamp interpreted in a zone (returns timestamptz)
SELECT TIMESTAMP '2024-03-01 12:00:00'
         AT TIME ZONE 'Asia/Tokyo';        -- 2024-03-01 03:00:00+00

Pobieranie bieżącego momentu

Warto znać funkcje zwracające „teraz”. NOW() i CURRENT_TIMESTAMP zwracają w Postgresie wartość typu timestamptz. Zwracają czas rozpoczęcia transakcji, a nie instrukcji, co ma znaczenie w przypadku długich transakcji.

Aby jawnie uzyskać UTC, należy wykonać konwersję: NOW() AT TIME ZONE 'UTC'. W MySQL funkcja UTC_TIMESTAMP() zwraca czas UTC bezpośrednio.

SELECT
  NOW()                       AS tx_start_tz,
  NOW() AT TIME ZONE 'UTC'    AS utc_walltime;

Czas letni jest prawdziwym wrogiem

Osoby prowadzące rozmowy kwalifikacyjne uwielbiają przypadki brzegowe związane z DST. Gdy zegary przesuwają się do przodu, lokalna godzina wskazywana przez zegar nie istnieje; gdy przesuwają się do tyłu, pewna godzina powtarza się. Przechowywanie czasu lokalnego sprawia, że takie wartości są niejednoznaczne albo nieprawidłowe.

Przechowywanie czasu UTC całkowicie omija ten problem: każdy moment jest jednoznaczny i monotoniczny. Podanie nazwy regionu, takiej jak 'America/New_York' (zamiast stałego offsetu, takiego jak -05:00), pozwala bazie danych poprawnie zastosować reguły DST dla dowolnej daty.

-- Region name applies DST automatically for the given date
SELECT TIMESTAMPTZ '2024-07-01 12:00:00+00'
         AT TIME ZONE 'America/New_York' AS summer, -- EDT (-04)
       TIMESTAMPTZ '2024-01-01 12:00:00+00'
         AT TIME ZONE 'America/New_York' AS winter; -- EST (-05)

Grupowanie według lokalnego dnia w różnych strefach

Realistyczny problem brzmi: „liczba aktywnych użytkowników dziennie w lokalnym czasie każdego użytkownika”. Jeśli bezpośrednio obetnie się znacznik czasu UTC, granice dnia będą nieprawidłowe dla użytkowników spoza strefy UTC.

Przed obcięciem czasu do dnia należy przekonwertować go do strefy użytkownika. Konwersja przesuwa czas zegarowy tak, aby granice dni odpowiadały lokalnemu czasowi.

SELECT
  DATE_TRUNC('day', occurred_at AT TIME ZONE u.tz) AS local_day,
  COUNT(DISTINCT e.user_id)                         AS dau
FROM events e
JOIN users u ON u.id = e.user_id
GROUP BY 1
ORDER BY 1;

Bezpieczne porównywanie znaczników czasu

Podczas filtrowania kolumny typu timestamptz należy porównywać ją z jednoznacznym momentem, najlepiej literałem UTC albo wartością timestamptz z offsetem. Porównanie z nieokreślonym ciągiem znaków może spowodować jego interpretację w nieprzewidywalnej strefie sesji.

Dzięki temu porównanie pozostaje jednoznaczne niezależnie od tego, kto uruchamia zapytanie.

SELECT *
FROM events
WHERE occurred_at >= TIMESTAMPTZ '2024-03-01 00:00:00+00'
  AND occurred_at <  TIMESTAMPTZ '2024-04-01 00:00:00+00';

Epocha i znaczniki czasu Unix

Wiele systemów przechowuje czas jako epokę Unixową (liczbę sekund od 1970-01-01 UTC). Osoba prowadząca rozmowę kwalifikacyjną może pokazać Państwu kolumnę typu integer i poprosić o jej odczytanie.

  • Postgres: TO_TIMESTAMP(epoch_seconds) zwraca wartość typu timestamptz.
  • Z powrotem do epoki: EXTRACT(EPOCH FROM occurred_at).
  • MySQL: FROM_UNIXTIME() i UNIX_TIMESTAMP().

Wartości epoki są z definicji wyrażone w UTC, co jest jednym z powodów ich popularności przy przechowywaniu danych.

SELECT
  TO_TIMESTAMP(1709294400)               AS as_ts,   -- from epoch
  EXTRACT(EPOCH FROM NOW())::bigint        AS as_epoch; -- to epoch

Uwagi o strefach czasowych w różnych dialektach

Oto krótka mapa, która pozwoli swobodnie poruszać się w dowolnym środowisku:

  • Postgres: timestamptz i AT TIME ZONE zapewniają najbogatszą obsługę.
  • MySQL: TIMESTAMP automatycznie konwertuje wartości za pomocą sesyjnej strefy time_zone; CONVERT_TZ(t, from, to) wykonuje jawną konwersję. DATETIME nie uwzględnia strefy czasowej.
  • SQL Server: datetimeoffset przechowuje offset; AT TIME ZONE 'name' wykonuje konwersję na podstawie nazw stref systemu Windows.
-- MySQL explicit conversion
SELECT CONVERT_TZ(event_dt, 'UTC', 'Europe/Istanbul') AS local_dt
FROM events;

Głębszy przykład: sesje przechodzące przez północ

Subtelne pytanie dotyczące raportowania brzmi: jak policzyć sesje przypadające na każdy lokalny dzień kalendarzowy, jeśli sesja może przechodzić przez północ? Rozwiązanie wymaga tej samej dyscypliny: należy przekonwertować czas lokalny, a następnie przypisać go do odpowiedniego przedziału.

Początek i koniec należy przechowywać jako timestamptz; na potrzeby raportu lokalny dzień należy wyznaczyć na podstawie przekonwertowanego czasu rozpoczęcia. Jeśli sesję trzeba podzielić między dwa dni, można dołączyć tabelę obejmującą wszystkie dni, czyli day spine — to świetny temat do poruszenia jako pytanie uzupełniające.

SELECT
  DATE_TRUNC('day', started_at AT TIME ZONE 'Europe/Istanbul') AS local_day,
  COUNT(*) AS sessions
FROM sessions
GROUP BY 1
ORDER BY 1;

Szybkie sprawdzenie

Proszę potwierdzić zalecaną strategię przechowywania danych i wyjaśnić, dlaczego jest zalecana.

Podsumowanie: strefy czasowe i znaczniki czasu

Najważniejsze zasady:

  • Przechowuj UTC jako timestamptz; do nazwanej strefy konwertuj tylko na potrzeby wyświetlania.
  • timestamptz przechowuje moment w UTC, a nie strefę czasową, mimo swojej nazwy.
  • AT TIME ZONE działa w obu kierunkach, zależnie od typu danych wejściowych: konwertuje wartość timestamptz na lokalny czas zegarowy albo interpretuje zwykły timestamp jako czas w danej strefie.
  • Należy używać nazw regionów ('America/New_York'), aby reguły DST były stosowane automatycznie; należy unikać stałych offsetów.
  • Czas należy konwertować na lokalny przed obcięciem go do dnia, a kolumny porównywać z jednoznacznymi momentami UTC.

Często zadawane pytania

Czy lekcja „Strefy czasowe i znaczniki czasu” jest bezpłatna?

Tak — pełny tekst „Strefy czasowe i znaczniki czasu” jest dostępny za darmo tutaj w sieci. Aby ćwiczyć ją interaktywnie (wbudowany edytor kodu i tutor AI dostępny 24/7) i odblokować resztę kursu SQL Interview Prep, przejdź na CoddyKit PRO. Kurs SQL Interview Prep zawiera 4 lekcji w sumie.

Co nauczysz się w „Strefy czasowe i znaczniki czasu”?

Przechowywanie czasu UTC, konwersja stref oraz pułapki dotyczące znaczników czasu poruszane przez rekruterów. Ćwiczysz SQL Interview Prep z praktycznym kodem, który uruchamiasz bezpośrednio w przeglądarce, a tutor AI dostępny 24/7 odpowiada na Twoje pytania podczas pracy nad lekcją.

Czy potrzebuję doświadczenia, aby zacząć SQL Interview Prep?

Nie wymagamy żadnego doświadczenia. SQL Interview Prep w CoddyKit jest strukturyzowany dla początkujących i zaawansowanych użytkowników, więc możesz zacząć tutaj lub od początku i uczyć się w swoim tempie. To lekcja 4 z 4.

Ile czasu zajmuje lekcja „Strefy czasowe i znaczniki czasu”?

Większość lekcji CoddyKit trwa około 5–10 minut. Każda lekcja to mały, interaktywny krok, dzięki czemu robisz systematyczne postępy i zawsze wracasz dokładnie do tego samego miejsca — na webie i w aplikacji.

Czy mogę pisać i uruchamiać kod w tej lekcji SQL Interview Prep?

Tak. Każda lekcja SQL Interview Prep zawiera wbudowany edytor kodu, więc piszesz i uruchamiasz prawdziwy kod bezpośrednio w przeglądarce i od razu otrzymujesz sprzężenie zwrotne od AI — bez konfiguracji na komputerze.

Wszystkie lekcje w tym kursie

  1. Działania na datach i interwały
  2. Obcinanie i grupowanie dat
  3. Analizowanie i formatowanie ciągów znaków
  4. Strefy czasowe i znaczniki czasu
← Powrót do SQL Interview Prep