6. Data Types (数据类型)
所有NTP时间值都以二进制补码格式 (twos-complement format) 表示, 位按照大端序 (big-endian, 如 [RFC0791] 附录A中所述) 从零开始编号, 从左侧或高位开始. 有三种NTP时间格式, 一个128位日期格式 (date format), 一个64位时间戳格式 (timestamp format), 和一个32位短格式 (short format), 如图3所示. 128位日期格式用于有足够存储和字长的地方. 它包括一个64位有符号秒字段 (signed seconds field), 跨越5840亿年, 和一个64位分数字段 (fraction field), 分辨率为0.05阿秒 (attosecond, 即0.5e-18). 为了方便在格式之间映射, 秒字段被划分为一个32位纪元编号字段 (Era Number field) 和一个32位纪元偏移字段 (Era Offset field). 纪元 (Eras) 不能由NTP直接产生, 也没有必要这样做. 必要时, 可以从外部方式派生, 例如文件系统或专用硬件.
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短格式
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时间戳格式
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日期格式
图3: NTP时间格式
64位时间戳格式用于数据包头和其他字长受限的地方. 它包括一个32位无符号秒字段 (unsigned seconds field), 跨越136年, 和一个32位分数字段, 分辨率为232皮秒 (picoseconds). 32位短格式用于延迟和离散头字段, 其中不需要其他格式的完整分辨率和范围. 它包括一个16位无符号秒字段和一个16位分数字段.
在日期和时间戳格式中, 主纪元 (prime epoch) 或纪元0的基准日期是1900年1月1日0时UTC, 此时所有位都为零. 应该注意的是, 严格来说, UTC在1972年1月1日之前并不存在, 但假设它自古以来就存在是方便的, 即使所有历史闰秒的知识都已丢失. 日期相对于主纪元; 大于零的值表示该日期之后的时间; 小于零的值表示它之前的时间. 请注意, 日期格式的纪元偏移字段和时间戳格式的秒字段具有相同的解释.
时间戳是无符号值, 对它们的操作在同一或相邻纪元中产生结果. 纪元0包括从主纪元到2036年2月7日6时28分16秒的日期, 此时最显著位设置为零. 纪元1从那时开始并持续136年, 之后开始纪元2. 依此类推, 正负纪元交替. 请注意, 时间戳为零的时间表示主纪元, 2^(31)秒表示2036年, 2^(32)-1秒表示主纪元之前一秒. 使用无符号时间戳时, 它们以相同方式处理. 跨越纪元的64位时间戳计算可能存在的危险 (例如2036年可能出现的情况) 可能导致溢出. 事实上, 如果在协议启动之前客户端设置在服务器的68年内, 即使客户端和服务器在相邻纪元中, 也能获得正确的值.
某些时间值以指数格式 (exponent format) 表示, 包括精度 (precision)、时间常数 (time constant) 和轮询间隔 (poll interval). 这些是8位有符号整数格式, 以log2 (以2为底的对数) 秒表示. 对它们允许的唯一算术操作是递增和递减. 为了本文档的目的并简化演示, 通过名称引用这些变量之一意味着指数值, 例如, 轮询间隔为1024秒, 而通过名称和指数引用意味着实际值, 例如, 轮询指数为10.
要将任何格式的系统时间转换为NTP日期和时间戳格式, 需要确定从主纪元到系统时间的秒数s. 给定s, 确定整数纪元和时间戳:
era = s / 2^(32) 且 timestamp = s - era * 2^(32),
这适用于正负日期. 给定纪元和时间戳, 确定s:
s = era * 2^(32) + timestamp.
在NTP和系统时间之间转换可能有点混乱, 超出了本文档的范围. 请注意, 纪元0的天数比大多数其他纪元的天数多一天, 这种情况在纪元3的2400年之前不会再次发生.
在下面要描述的状态变量中, 对整数类型的显式引用意味着32位无符号整数. 这简化了边界检查, 因为只需要定义上限. 没有显式引用时, 默认类型是64位浮点双精度 (floating double). 必要时将注意例外情况.