文章摘要
2026年3月8日起,加拿大不列颠哥伦比亚省永久采用太平洋夏令时(UTC-7),不再回退。文章提醒,若在数据库中存储基于UTC的时间戳,该地区11月至次年3月的预约时间可能偏差一小时,因为timestamptz列只存UTC值,时区仅用于转换。
文章总结
好的,这是根据您的要求,对原文进行中文重述和精简后的版本:
标题:不列颠哥伦比亚省、时区变更与PostgreSQL数据库
核心事件: 自2026年3月起,加拿大不列颠哥伦比亚省(BC省)将永久实行夏令时(PDT),时区偏移固定为UTC-7,不再在11月回拨至UTC-8。
对数据库的影响: 如果您的数据库使用timestamptz类型存储BC省未来的预约时间,那么2026年11月至次年3月期间的预约时间可能会出错。这是因为timestamptz列只存储UTC时间,在插入和查询时使用时区规则进行转换。如果存储和查询时的时区规则不同(例如,BC省取消了冬令时),查询返回的本地时间将与用户最初设定的时间不一致。
如何检查数据库是否已更新时区规则:
您可以通过以下SQL查询来检查PostgreSQL的tzdata包是否已更新,以反映BC省的时区变更:
sql
SELECT to_char('2026-12-01 10:00:00'::timestamp AT TIME ZONE 'America/Vancouver', 'HH24:MI:SS OF') AS november_2026_vancouver_offset;
- 如果返回 17:00:00 +00,说明tzdata已更新。
- 如果返回 18:00:00 +00,说明tzdata尚未更新。
问题示例:
用户在2026年初预约了11月10日上午10点(太平洋标准时间,UTC-8)的会议。数据库将其存储为UTC时间 2026-11-10 18:00:00+00。当tzdata在4月更新后,查询该预约时,系统会使用新的时区规则(UTC-7)进行转换,导致返回的本地时间为上午11点,比用户原意晚了一小时。
解决方案:双列模式
为了应对时区规则变化,建议采用“双列模式”来存储未来时间,该模式包含三个字段:
1. 本地时间 (local_time):存储用户设定的墙上时钟时间(如 2026-11-10 10:00:00)。
2. 时区名称 (timezone_name):存储IANA时区名称(如 America/Vancouver)。
3. UTC时间 (starts_at_utc):由前两个字段计算得出的UTC时间,用于索引、查询和约束检查。
实现方式:
由于PostgreSQL不允许将timestamptz类型用于生成列(因其非不可变),您需要使用触发器在插入或更新时自动计算starts_at_utc:
```sql
CREATE OR REPLACE FUNCTION recomputeappointmentutc()
RETURNS TRIGGER AS $$
BEGIN
NEW.startsatutc := NEW.localtime AT TIME ZONE NEW.timezonename;
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
CREATE TRIGGER tsrecomputestartsatutc BEFORE INSERT OR UPDATE ON appointments FOR EACH ROW EXECUTE FUNCTION recomputeappointmentutc(); ```
应对时区规则更新:
当tzdata更新后,您需要重新计算所有受影响的未来预约的UTC时间:
sql
UPDATE appointments
SET starts_at_utc = local_time AT TIME ZONE timezone_name
WHERE timezone_name = 'America/Vancouver'
AND starts_at_utc > now();
关于RFC 9557:
该标准(如 1996-12-19T16:39:57-08:00[America/Los_Angeles])并未解决因政治决策导致的未来时区规则变化问题,因此不适用于此场景。
如果tzdata已更新且数据已受影响:
您需要执行一个数据修复项目,包括:确定tzdata更新时间、找出受影响记录、通知用户、在非生产环境测试、备份并执行迁移,以及更新用户界面以提示时区问题。
总结:
BC省拥有580万人口,其时区变更将广泛影响相关数据集。请务必关注tzdata包的更新,并考虑采用双列模式来保护未来时间的本地意图。
评论总结
根据评论内容,总结如下:
主要观点与论据:
数据库存储应使用UTC时间戳(评论1、10):评论1强调“只应在数据库中存储当前UTC时间戳”,并建议“将所有时间转换交给经过实战检验的软件库”。评论10指出“时间戳是计数器,不受时区影响”,应“存储未调整的数值作为事实来源”。
未来事件应存储本地时间及时区(评论4、5):评论4主张“未来事件应存储事件地点的本地日期、时间和时区”,并强调“人类可读字符串适合长期存储”。评论5建议“使用ANSI SQL的DATE和TIME类型,转换在表示层完成”。
时区转换的复杂性(评论2、8):评论2指出“夏令时回拨时,同一时间可能对应两个不同时刻”。评论8警告“不要自己实现时区转换”,并引用“使用库,不要自己动手”。
对不列颠哥伦比亚省变更的批评(评论7、9):评论7直言“不列颠哥伦比亚省的变化很愚蠢”。评论9希望“美国西海岸也能效仿,停止每年两次调整时钟”。
不同观点平衡:
- 支持UTC存储:评论1、10强调其作为事实来源的可靠性。
- 支持本地时间存储:评论4、5认为未来事件需保留上下文。
- 反对时区变更:评论7、9批评政策调整带来的混乱。
关键引用(保留中英文):
- 评论1:“I would contend that you shouldn't store anything but current unix timestamps in UTC in your database.”(“我主张数据库中只应存储当前UTC时间戳。”)
- 评论4:“Future events: store the local (at the event) date and time and timezone.”(“未来事件:存储事件地点的本地日期、时间和时区。”)
- 评论8:“Use a library, do not roll it yourself.”(“使用库,不要自己实现。”)
- 评论10:“A unix timestamp does not have different timezones. It is a counter.”(“Unix时间戳没有不同时区,它是一个计数器。”)