# 时区选择 timeTableFix 顶栏提供时区选择器。加载时自动检测用户所在时区,检测不到则默认 **澳大利亚墨尔本(`Australia/Melbourne`)**。 ## 检测逻辑 `src/lib/timezone.js` 的 `detectTimezone()`: 1. 读取 `new Intl.DateTimeFormat().resolvedOptions().timeZone`(浏览器/Node 标准 API)。 2. 若抛错、返回空串或格式不合法,回退 `DEFAULT_TIMEZONE`(`Australia/Melbourne`)。 非安全上下文(无 `https`)或极旧环境可能拿不到时区,此时一律回退墨尔本。 ## 下拉列表来源 `listTimezones()` = 精选常用 IANA 时区(`COMMON_TIMEZONES`,含墨尔本/上海/UTC 等)∪ 运行时 `Intl.supportedValuesOf('timeZone')`(若可用),去重后排序,`UTC` 置顶。旧浏览器不支持 `supportedValuesOf` 时静默降级为仅精选列表,保证下拉始终可用、无外部依赖。 ## 作用范围(设计要点) 应用内部所有日期/时间均为 **wall-clock 字符串**(`YYYY-MM-DD` + `HH:MM`,见 `date.js`),**不做任何时区换算**。时区仅是一个挂在事件上的 IANA 标识(`tzid`),在 ICS 序列化/反序列化时作为 `DTSTART`/`DTEND` 的 `TZID` 参数读写。 因此选定时区的作用是**日历级默认值/标签**,而非时间换算层: | 行为 | 是否受选定时区影响 | |---|---| | 新建事件(「新增日程」)的 `tzid` | ✅ 使用选定时区 | | 下载 ICS 的 `X-WR-TIMEZONE` | ✅ 写入选定时区 | | 已导入事件的 `tzid` | ❌ 保留原始 TZID,不覆盖、不换算 | | 日期/时间数值(编辑、预览、整理) | ❌ 始终 wall-clock,不换算 | | 间隔去重(`organize.js`) | ❌ 不读 `tzid`,不受影响 | ### 为何不换算 - 应用面向「整理/编辑日程文本」而非「跨时区会议调度」,wall-clock 直观且无歧义。 - `organize.js` 按 `[summary, location, 开始时间, 时长]` 合并重复,引入换算会改变合并键、破坏既有 round-trip 测试。 - 已导入事件保留原 TZID 保证 `parse(serialize(parse(x)))` 等价(见 `ical-io.test.js` round-trip 用例)。 ## 实现位置 | 文件 | 改动 | |---|---| | `src/lib/timezone.js` | 新增:检测/列表/校验/默认值 | | `src/composables/useCalendar.js` | `state.timezone`(初始 `detectTimezone()`)、`setTimezone()`、`addEvent` 传入 tzid、`serialize` 注入 `X-WR-TIMEZONE` | | `src/components/HeaderBar.vue` | 顶栏 `