# 时钟
`clock/*` 是 TCL 风格的日期和时间集合。跨越命名空间边界的每个值都是数字:epoch 秒是通用货币(`format`、`scan` 和 `add` 都接收或返回它),而 `clock/format` / `clock/scan` 是由同一张 strptime 风格说明符表驱动的严格对偶。`clock/clicks` 是例外——一个单调纳秒计数器,只有作为两次采样之间的差值才有意义。所有函数都注册在每个宿主中,无需导入。
## 函数
| 函数 | 用途 |
|---|---|
| `clock/seconds` | 当前墙上时钟时间,以 Unix epoch 以来的整数秒表示 |
| `clock/millis` | 当前墙上时钟时间,以 epoch 毫秒表示 |
| `clock/micros` | 当前墙上时钟时间,以 epoch 微秒表示 |
| `clock/clicks` | 高分辨率单调时间戳,单位为纳秒(不以 epoch 为基准) |
| `clock/format time ?format? ?timezone?` | 将 epoch 秒格式化为字符串;spec-map 形式 `%{ :format :timezone :locale }` |
| `clock/scan str %{ :format ... }` | 将字符串解析为 epoch 秒;`:format` 必填 |
| `clock/add time n :unit ...` | 日历感知的日期运算;spec-map 形式 `%{ :days n :months n :timezone ... }` |
墙上时钟与单调时钟:
- `clock/seconds`、`clock/millis` 和 `clock/micros` 读取系统墙上时钟。当时钟被调整时,它们可能向后跳变,因此不适合测量持续时间。
- `clock/clicks` 读取单调计数器。其原点未指定,且可能因运行和进程而异——只有两次读数之间的差值才有意义。用它来测量耗时和性能分析。
格式化与解析:
- `clock/format` 默认为 `"%Y-%m-%dT%H:%M:%S%z"`(ISO-8601)和本地时区。时区可以是 `"UTC"`、`"local"`,或带符号的 `+HHMM` / `+HHMMSS` 偏移量。
- 两个函数共用一张说明符表——`%Y %m %d %H %M %S %j %u %A %B %z` 等等,外加复合说明符 `%T %R %D %F %r %+`。
- `clock/scan` 刻意严格:不支持自由格式解析,且 `:format` 是必需的。缺失的时刻字段解析为午夜;缺失的日期字段回退到 `:base`(默认:当前时间);输入中的 `%z` 覆盖 `:timezone` 选项。格式错误的输入返回 `%Err{:message}`。
日历运算:
- 单位有 `:second :minute :hour :day :week :month :year`;位置增量从左到右应用。`:second`/`:minute`/`:hour` 将固定时长直接加到 epoch 值上。
- `:day` 和 `:week` 保持墙上时钟:`clock/add` 转换为 `:timezone` 中的分解时间,加上天数,再转换回来。
- `:month` 和 `:year` 是日历运算,将日期钳制到目标月份的最后一天(1 月 31 日 + 1 个月变为 2 月 28/29 日)。
- `clock/add` 在 `:timezone`(默认:`"local"`)中解释日历单位。用 spec-map 形式传入时区,使运算与你用 `clock/scan` 构建时间戳时处于同一时区。
## 示例
### 读取墙上时钟
set now [clock/seconds] echo [clock/format $now] # ISO-8601, local tz echo [clock/format $now "%Y-%m-%d %H:%M:%S" "UTC"] echo [clock/format 0 "%A, %B %d %Y" "UTC"] # Thursday, January 01 1970 echo [clock/millis] # epoch milliseconds
### 解析与日历运算
set t [clock/scan "2024-01-31 14:30:00" %{ :format "%Y-%m-%d %H:%M:%S" :timezone "UTC" }] # Month-end clamping, using the same timezone for the arithmetic: echo [clock/format [clock/add $t %{ :months 1 :timezone "UTC" }] "%F" "UTC"] # => "2024-02-29" # Fixed-duration units add to the epoch value directly: echo [clock/format [clock/add $t 26 :hours] "%F %T" "UTC"] # => "2024-02-01 16:30:00"
### 用单调时钟测量耗时
set start [clock/clicks] # ... work ... set elapsed-ns ([clock/clicks] - $start) echo "took" ($elapsed-ns / 1000000) "ms"