بریتیش کلمبیا، تایمزون و تلهٔ Postgres
خلاصهٔ کاملتر
به گفتهٔ Christopher Winslett در بلاگِ Crunchy Data، از ۸ مارس ۲۰۲۶ بریتیش کلمبیا ساعتش رو یک ساعت جلو کشید و دیگه در نوامبر عقب نمیکشه؛ یعنی از این به بعد آفستِ منطقهٔ America/Vancouver بهطور دائم UTC-7 میمونه. نویسنده از همین اتفاق استفاده میکنه تا دربارهٔ ذخیرهٔ درستِ تاریخ و تایمزون حرف بزنه.
نکتهٔ کلیدی اینه: ستونِ timestamptz زمانِ محلی رو ذخیره نمیکنه، بلکه فقط UTC رو نگه میداره و تایمزون رو موقعِ درج و خواندن برای تبدیل بهکار میبره. اگه یه قرارِ آینده رو با تایمزون Vancouver ذخیره کرده باشی، اون لحظه با قانونِ همونموقع به UTC تبدیل شده؛ ولی وقتی بعداً میخونیش، با قانونِ جدید به محلی برمیگرده. پس اگه قانون عوض شده باشه، زمانی که تحویل میگیری همونی نیست که کاربر خواسته بود — قرارهای نوامبر تا مارس ممکنه یک ساعت پرت بشن.
نویسنده میگه این وابسته به آپدیتِ بستهٔ tzdataست؛ تا وقتی آپدیتش نکنی، Postgres از قانونِ قدیمی استفاده میکنه. مثلاً یه قرارِ ۱۰ صبحِ ۱۰ نوامبر که بهشکل timestamptz ذخیره شده، بعد از آپدیتِ قانون ممکنه موقعِ خواندن ۱۱ صبح نشون داده بشه — درست همون یک ساعت اختلاف. برای همین حتی نگهداشتنِ زمانِ درست هم به این بستگی داره که آپدیتِ tzdata کِی رسیده.
راهحلی که نویسنده پیشنهاد میده «الگوی سهستونه»ست: زمانِ محلی، نامِ تایمزون، و یه ستونِ UTCِ محاسبهشده. دو ستونِ اول «قصدِ کاربر» رو نگه میدارن و فقط به درخواستِ خودش عوض میشن؛ ستونِ سوم همونیه که ایندکس و کوئری میکنی:
CREATE TABLE appointments (
id bigint PRIMARY KEY GENERATED ALWAYS AS IDENTITY,
local_time timestamp NOT NULL,
timezone_name text NOT NULL,
starts_at_utc timestamptz NOT NULL
);چون Postgres اجازه نمیده ستونِ محاسبهشده (generated) از نوعِ timestamptz باشه — به این خاطر که قوانینِ تایمزون تغییر میکنن و این نوع immutable حساب نمیشه — نویسنده بهجاش از یه تریگر استفاده میکنه که موقعِ درج و آپدیت، UTC رو حساب کنه:
CREATE OR REPLACE FUNCTION recompute_appointment_utc()
RETURNS TRIGGER AS $$
BEGIN
NEW.starts_at_utc := NEW.local_time AT TIME ZONE NEW.timezone_name;
RETURN NEW;
END;
$$ LANGUAGE plpgsql;به گفتهٔ نویسنده این الگو فقط وقتی لازمه که «قصدِ محلی» اصل باشه: قرارها، مهلتهای قانونی، رویدادهای تقویم. برای گذشته یا وقتی خودِ لحظهٔ UTC اصله (لاگ، تراکنشِ مالی، دادهٔ سنسور) همون timestamptz ساده بهتره. اگه هم قانونِ تایمزون عوض شد، کافیه با یه UPDATE مقادیرِ UTCِ آینده رو دوباره حساب کنی. نویسنده در آخر یادآوری میکنه که استانداردِ تازهٔ RFC 9557 هم این مشکلِ خاص — یعنی زمانِ محلیِ آینده وقتی تعریفِ تایمزون عوض میشه — رو حل نمیکنه، پس فعلاً همین الگوی سهستونه بهترین انتخابه.
نکات کلیدی:
- بریتیش کلمبیا بهطور دائم روی UTC-7 موند و دیگه ساعت رو عقب نمیکشه
- ستونِ timestamptz فقط UTC رو ذخیره میکنه؛ با تغییرِ قانونِ تایمزون، قرارهای آینده جابهجا میشن
- الگوی سهستونه (زمان محلی + نام تایمزون + UTCِ محاسبهشده) در برابر این تغییرها دوام میاره
- چون timestamptz برای ستونِ generated مجاز نیست، UTC با یه تریگر حساب میشه
- برای گذشته یا وقتی لحظهٔ UTC اصله، همون timestamptz ساده کافیه




