6. 数据类型
所有 NTP 时间值都以二进制补码 (twos-complement) 格式表示, 位编号采用大端序 (big-endian, 如 [RFC0791] 附录 A 所述), 从左侧即高阶位置的 0 开始编号. 如图 3 所示, NTP 有三种时间格式: 128-bit 日期格式 (date format), 64-bit 时间戳格式 (timestamp format), 以及 32-bit 短格式 (short format). 128-bit 日期格式用于具备足够存储空间和字长的场合. 它包含一个跨越 5840 亿年的 64-bit 有符号 Seconds 字段, 以及一个分辨率为 .05 阿秒 (即 0.5e-18) 的 64-bit Fraction 字段. 为了便于在不同格式之间映射, Seconds 字段被划分为一个 32-bit Era Number 字段和一个 32-bit Era Offset 字段. Era 不能由 NTP 直接产生, 也没有必要这样做. 需要时, 可以从外部手段推导 Era, 例如文件系统或专用硬件.
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Seconds | Fraction |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
NTP Short Format
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Seconds |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Fraction |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
NTP Timestamp Format
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Era Number |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Era Offset |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| |
| Fraction |
| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
NTP Date Format
Figure 3: NTP Time Formats
64-bit 时间戳格式用于数据包头部 (packet header) 以及其他字长受限的位置. 它包含一个跨越 136 年的 32-bit 无符号 Seconds 字段, 以及一个分辨率为 232 皮秒的 32-bit Fraction 字段. 32-bit 短格式用于延迟 (delay) 和离散度 (dispersion) 头部字段, 在这些位置使用其他格式的完整分辨率和范围并不合适. 它包含一个 16-bit 无符号 Seconds 字段和一个 16-bit Fraction 字段.
在日期格式和时间戳格式中, 主纪元 (prime epoch), 即 era 0 的基准日期, 是 UTC 1900 年 1 月 1 日 0 时, 此时所有位均为零. 需要注意的是, 严格来说 UTC 在 1972 年 1 月 1 日之前并不存在, 但为方便起见, 即使所有历史闰秒知识都已丢失, 也可以假定 UTC 一直存在. 日期相对于主纪元表示; 大于零的值表示该日期之后的时间; 小于零的值表示该日期之前的时间. 注意, 日期格式的 Era Offset 字段和时间戳格式的 Seconds 字段具有相同解释.
时间戳 (timestamp) 是无符号值, 对它们执行运算会产生位于同一 era 或相邻 era 的结果. Era 0 包含从主纪元到 2036 年某个时间点之间的日期, 此期间最高有效位为零. Era 1 从该点继续并持续 136 年, 随后 era 2 开始, 依此类推. 在 era 0 中, timestamp 为零表示主纪元, 2^(31) 秒表示 2036 年的时间点, 2^(32)-1 秒表示主纪元之前的那一秒. 使用无符号时间戳时, 这些值以相同方式处理. 人们可能认为跨越一个 era 的 64-bit 时间戳计算存在风险, 例如 2036 年可能发生的情况, 可能导致溢出 (over-run). 事实上, 如果在协议启动前客户端 (client) 的时间被设置在服务器 (server) 时间的 68 年以内, 即使客户端和服务器位于相邻 era, 也可以得到正确值.
某些时间值以指数格式 (exponent format) 表示, 包括精度 (precision), 时间常数 (time constant), 以及轮询间隔 (poll interval). 这些值采用 8-bit 有符号整数格式, 单位为 log2 (以 2 为底的对数) 秒. 允许对它们执行的唯一算术操作是递增 (increment) 和递减 (decrement). 为便于本文表述, 当仅按名称引用这些变量之一时, 指的是取指数后的值, 例如轮询间隔为 1024 s; 当按名称和指数引用时, 指的是实际值, 例如轮询指数 (poll exponent) 为 10.
要将任意格式的系统时间 (system time) 转换为 NTP 日期格式和时间戳格式, 必须确定从主纪元到系统时间的秒数 s. 给定 s 后, 要确定整数 era 和时间戳:
era = s / 2^(32) and timestamp = s - era * 2^(32),
该公式对正日期和负日期都适用. 给定 era 和 timestamp 后, 要确定 s:
s = era * 2^(32) + timestamp.
NTP 时间和系统时间之间的转换可能有些繁琐, 超出本文范围. 注意, era 0 中的天数比大多数其他 era 多一天, 而这种情况直到 era 3 中的 2400 年才会再次出现.
在后续状态变量 (state variable) 描述中, 显式提到 integer 类型时, 表示 32-bit 无符号整数. 这简化了边界检查, 因为只需要定义上限. 如果没有显式说明, 默认类型为 64-bit floating double. 必要时会注明例外情况.