# Calendar Timezone Policy Protocol

## Version

- Current: `1.0`
- Status: Active

---

## Goal

统一日历事件在 App、Agent、工具、数据库之间的时间语义，消除时区不一致导致的显示和落库偏差。

---

## Canonical Rules

1. 数据库存储基准为 UTC。
2. 事件语义时区使用 IANA 时区字符串（`event_timezone` / `timezone`）。
3. 禁止无时区时间（naive datetime）进入日历写入链路。
4. 日历写入必须显式确定事件时区，不允许工具层硬编码默认时区。

---

## Timezone Resolution Priority

运行时事件时区解析顺序固定如下：

1. `event_timezone`（工具调用显式传参）
2. `forwardedProps.client_time.device_timezone`
3. `users.profile.settings.timezone`
4. `UTC`

---

## Write Contract

### Required fields

- `start_at`: RFC3339 且必须包含时区偏移
- `timezone`: IANA 时区

### Optional fields

- `end_at`: RFC3339 且必须包含时区偏移（若提供）

### Validation

- `timezone` 非法 -> 拒绝请求
- `start_at`/`end_at` 无时区 -> 拒绝请求
- `end_at < start_at` -> 拒绝请求

---

## Read Contract

读接口最小语义要求：

- 返回 UTC 时间字段（`start_at`, `end_at`）
- 返回事件时区字段（`timezone`）
- 前端展示必须以 `timezone` 作为事件本地时间转换基准，不允许直接按设备本地时区隐式渲染

---

## Error Codes

推荐错误码（由后端映射为 4xx）：

- `INVALID_DATETIME_FORMAT`
- `NAIVE_DATETIME_FORBIDDEN`
- `INVALID_TIMEZONE`
- `TIMEZONE_REQUIRED`
- `INVALID_TIME_RANGE`

---

## DST Rule

- 夏令时切换期间，时间解释以 IANA 时区数据库为准。
- 对于歧义本地时间（例如回拨重复小时），由后端统一按标准库解析策略处理并返回确定 UTC 结果。

---

## Non-Goals

- 本协议不引入新的数据库时间列。
- 本协议不改变 `schedule_items` 现有 `TIMESTAMPTZ + timezone` 存储结构。
