DST转换参考

testland2026-10-25查看 749复制 347

夏令时(DST)转换模式及其规范 bug 类别的纯参考目录。涵盖春季前跳(跳过小时:本地 02:00 → 03:00)和秋季回拨(重复小时:本地 02:00 → 01:00)转换、DST 的历史不规则性(不同司法管辖区、不同日期的转换、某些地区废除 DST 或从未采用)、作为规范来源的 IANA 时区数据库(tz / Olson DB)以及 DST 产生的可测试行为(重复 / 缺失...

Download (.md)

📘如何使用这个技能

  1. 1
    复制技能内容·点击上方「Copy SKILL.md」复制全部内容,或 Download 下载到本地
  2. 2
    打开你的 AI 客户端·支持 Claude Code、Trae、Cursor、ChatGPT、DeepSeek、Kimi 等任意 AI 助手
  3. 3
    粘贴为系统提示/知识文件·Claude Code:放入 .claude/skills/ 目录;普通对话:作为首条消息发送并说「请严格按这份技能文档执行」
  4. 4
    直接提出你的需求·AI 会按技能定义的工作流、格式和约束来处理,无需再解释背景

💡 提示:技能生效后,每次对话只需一句需求,AI 会按 SKILL.md 的流程执行。

SKILL.md9103 chars
---
name: dst-transition-reference
description: "Pure-reference catalog of Daylight Saving Time (DST) transition patterns and their canonical bug classes. Covers the spring-forward (skipped hour: 02:00 → 03:00 local) and fall-back (repeated hour: 02:00 → 01:00 local) transitions, the historical irregularity of DST (different jurisdictions, transitions on different dates, some regions abolish DST or never adopted it), the IANA timezone database (tz / Olson DB) as the canonical source, and the testable behaviors DST creates (duplicate / missing local timestamps, cron jobs that fire 0 or 2 times, billing periods that miss / double-count, recurring meetings on transition days). Per-jurisdiction DST-rule tables, refreshable per-region test-data fixtures, and the leap-second reference (23:59:60 insertion, time_t stalls, leap-smear vs step, monotonic-clock fixes) live in references/. Use when designing or auditing time-handling code or test cases, or when auditing leap-second assumptions."
---

# dst-transition-reference

## Overview

DST transitions cause a large share of production time-bugs:
spring-forward creates **non-existent local times**, fall-back
creates **duplicate local times**. The IANA Time Zone Database
([iana.org/time-zones](https://www.iana.org/time-zones)) is the
canonical source of historical and current DST rules.

## How to use this reference

1. **Identify the transition** the code crosses - spring-forward
   (skipped hour) or fall-back (repeated hour) - from the DST
   mechanics section below.
2. **Match the bug class** the code is exposed to (cron, billing,
   duration arithmetic, recurring meeting, storage) and apply its
   mitigation.
3. **Turn it into an assertion** using the testable-behaviours table -
   construct the transition timestamp and assert the library's
   documented result (see the worked example).
4. **Pick zones + fixture timestamps** from
   [references/jurisdictions-and-fixtures.md](references/jurisdictions-and-fixtures.md),
   then refresh them against IANA before each release.

## When to use

- Designing time-handling code that crosses DST boundaries.
- Auditing existing code for DST-safety.
- Writing test cases that exercise DST behaviour.
- Investigating "scheduled job ran twice / didn't run" reports.

## DST mechanics

### Spring-forward (skipped hour)

Per
[en.wikipedia.org/wiki/Daylight_saving_time](https://en.wikipedia.org/wiki/Daylight_saving_time),
in US Eastern: on the 2nd Sunday of March, at 02:00 local time
the clock jumps to 03:00. The 02:00-02:59 hour **does not
exist** in local time.

Tests against `2026-03-08 02:30 America/New_York` produce
ambiguous or invalid results depending on library:

| Library | Behaviour at non-existent local time |
|---|---|
| Python `pytz` (legacy) | `pytz.exceptions.NonExistentTimeError` |
| Python `zoneinfo` (3.9+) | Returns the "would-be" time + 1h (=03:30 EDT) |
| Java `ZonedDateTime` | Constructor takes a resolver: STRICT / SMART_BACKWARD / SMART_FORWARD |
| JS Intl | Browsers vary; often returns the post-transition time |

### Fall-back (repeated hour)

In US Eastern: 1st Sunday of November at 02:00 local time, the
clock falls back to 01:00. The 01:00-01:59 hour **occurs
twice** - once as EDT (UTC-4), once as EST (UTC-5).

`2026-11-01 01:30 America/New_York` is ambiguous. Libraries
either:
- Pick one (typically the first occurrence in pytz/zoneinfo)
- Raise an error
- Take an `is_dst` / `fold` flag (Python 3.6+ has `fold=0|1`)

## Worked example - a spring-forward assertion

Goal: prove the code under test handles a **non-existent** local time
deterministically.

1. Pick the transition: America/New_York spring-forward on
   2026-03-08 - 02:00 local jumps to 03:00, so 02:00-02:59 does not
   exist.
2. Construct `2026-03-08 02:30 America/New_York` in the code path.
3. Assert against the library's documented behaviour (from the table
   above):
   - Python `zoneinfo` normalises to 03:30 EDT - assert the
     normalised value, never 02:30.
   - Python `pytz` raises `NonExistentTimeError` - assert the raise.
   - Java `ZonedDateTime` applies its resolver - assert per the
     chosen STRICT / SMART_FORWARD rule.
4. Repeat for fall-back: `2026-11-01 01:30 America/New_York` occurs
   twice; assert the `fold` / `is_dst` selection picks the intended
   offset.

## Per-jurisdiction differences

Per-region DST rules (US, EU, Australia, and the growing list of
regions that abolished DST) and refreshable 2026 fixture timestamps
live in
[references/jurisdictions-and-fixtures.md](references/jurisdictions-and-fixtures.md).
Per IANA, rules change frequently - test against current zoneinfo,
not assumptions.

## Common bug classes

### Cron jobs

A "daily at 02:30" cron in `America/New_York`:

- Spring-forward day: **doesn't fire** (02:30 doesn't exist).
- Fall-back day: **fires twice** (02:30 EDT, then 02:30 EST).

Mitigation:
- Use UTC cron expressions when possible.
- For local-time business hours, accept the irregularity or
  schedule outside transition hours (04:00 is safe everywhere).
- Per `cron-job-test-author` (in the qa-async-jobs plugin):
  always test DST + leap-day edge cases.

### Billing periods

"Bill on the 1st of each month at 00:00 local time":

- Fine in jurisdictions without DST.
- Risk in DST-observing: 00:00 local on Nov 1 (US) might
  overlap with the fall-back hour if billing involves more than
  one event.

Mitigation: bill at UTC, or at a local hour known to be safe
(e.g., 06:00).

### Duration arithmetic

`tomorrow_same_time = today_same_time + Duration("24 hours")`:

- Spring-forward: result is 23 hours later in local time.
- Fall-back: result is 25 hours later in local time.

Mitigation: distinguish "24 hours from now" (Duration) from
"this time tomorrow" (calendar addition).

### Recurring meeting

"Every Monday at 09:00 local time":

- Crosses DST boundary → still 09:00 local, but **2 minutes
  before or after the UTC equivalent** of the previous week.
- Calendar systems handle this; custom scheduling code often
  doesn't.

### Storage

Storing wall-clock-local strings ("2026-03-08 02:30") is unsafe
across DST. Always store UTC + zone identifier.

## Testable behaviours

| Behaviour | Test |
|---|---|
| Code handles non-existent local time | Construct `2026-03-08 02:30 America/New_York`; library raises or normalises; assert expected |
| Code handles ambiguous local time | Construct `2026-11-01 01:30 America/New_York`; library raises or picks; assert |
| Cron-equivalent fires 0 / 1 / 2 times | Simulate clock across the transition; count invocations |
| Duration vs calendar addition consistent | Assert difference on transition day |
| Storage uses UTC + zone | Parse stored value; expect ISO format with offset or `Z` |

Per
`timezone-test-matrix-builder`,
the test matrix combines (zone, transition-type, library-version).

## Anti-patterns

| Anti-pattern | Why it fails | Fix |
|---|---|---|
| Storing local times as strings | Ambiguous on fall-back; nonexistent on spring-forward | UTC + zone, or RFC 3339 with explicit offset |
| Assuming all jurisdictions observe DST | Half the world doesn't | Per-zone testing |
| Using "24 hours" for "tomorrow" | Off by 1 hour on transition days | Calendar arithmetic primitives |
| Pinning to a specific year's transition date | Rules change annually | Use IANA zoneinfo dynamically |
| Crossing DST with `naive` datetime | Behaviour undefined | Always tz-aware |
| Cron in local time without DST testing | Misses / duplicates jobs | Test transition days |
| Hardcoded UTC offset (-5:00) | Wrong when DST is in effect | Use zone identifier |

## Limitations

- **IANA zoneinfo changes throughout the year.** Sept 2026 may
  add or remove DST observance for some jurisdictions; test
  data goes stale.
- **OS / runtime zoneinfo versions differ.** Java's tzdata
  ships with the JDK; system tzdata is separate; Python's
  `zoneinfo` reads system tzdata. Mismatches cause subtle bugs.
- **Polar regions, antimeridian, and historic timezones.**
  Special cases not covered here.
- **Leap seconds are a separate discontinuity class.** The 23:59:60
  insertion mechanics, bug classes, and monotonic-clock fixes are in
  [references/leap-seconds.md](references/leap-seconds.md).

## References

- IANA Time Zone Database:
  [www.iana.org/time-zones](https://www.iana.org/time-zones).
- Wikipedia DST:
  [en.wikipedia.org/wiki/Daylight_saving_time](https://en.wikipedia.org/wiki/Daylight_saving_time).
- Python zoneinfo:
  [docs.python.org/3/library/zoneinfo.html](https://docs.python.org/3/library/zoneinfo.html).
- Deep references (with their own citations): per-jurisdiction rules +
  fixtures in [references/jurisdictions-and-fixtures.md](references/jurisdictions-and-fixtures.md);
  leap-second mechanics + bug classes in
  [references/leap-seconds.md](references/leap-seconds.md) with the
  smear-vs-step record in
  [references/smear-strategies-and-history.md](references/smear-strategies-and-history.md).
- Cross-plugin:
  `cron-job-test-author` (qa-async-jobs).
- Consumed by:
  `fake-clock-testing`,
  `timezone-test-matrix-builder`.
来源:skillsdirectory

评论0

登录后参与评论

还没有评论,来抢沙发吧