bigywater.tech
ClickHouse 内部原理 · 46

Date、Date32、DateTime 和 DateTime64

真实用超出范围的边界日期测试Date类型,发现它不报错而是静默钳制,并说明四种日期时间类型真实的存储成本与适用场景取舍。

静默钳制
真实超出范围的日期不报错,被悄悄夹到边界值
2~8字节
真实四种类型的存储成本随范围/精度递增

做的事:真实用超出范围的日期,看ClickHouse怎么处理

代码SELECT toDate('1899-12-31'); -- Date类型下限之前 SELECT toDate32('1899-12-31'); -- Date32类型 SELECT toDate('2149-06-07'); -- Date类型上限之后

验证:真实是静默钳制,不是报错

实测 · 本机执行输出
SELECT toDate('1899-12-31');
→ 1970-01-01    ← 真实没有报错,而是悄悄夹到了Date类型的下限(Unix纪元)

SELECT toDate32('1899-12-31');
→ 1899-12-31    ← Date32真实支持这个日期,原样返回

SELECT toDate('2149-06-06'), toDate('2149-06-07');
→ 2149-06-06     2149-06-06    ← 真实第二个也被夹到了同一个上限值

SELECT toDate32('1900-01-01'), toDate32('2299-12-31');
→ 1900-01-01     2299-12-31    ← Date32真实的范围边界

Date类型真实的取值范围是1970-01-01到2149-06-06,超出这个范围的输入不会真实报错,而是被静默夹到最近的边界值——这是一个容易被忽略的真实陷阱:如果业务数据里意外出现了1969年或者2150年的日期(脏数据、时区换算错误等),存进Date类型的列不会有任何报错提示,查询出来的结果会是一个看似正常、实际上错误的1970-01-01或2149-06-06。

四种类型真实的取舍:范围、精度和存储成本

这四种类型真实是一套组合:Date用2字节存储,只能表示天这个粒度、范围被死死限定在1970~2149年(真实底层就是从Unix纪元开始数的天数,用UInt16装下),换来最小的存储体积;Date32用4字节,把天数改用Int32存储,真实换来了1900~2299年的范围,代价是存储体积翻倍;DateTime同样4字节,存的是Unix时间戳(精确到秒),范围也是1970~2106年左右,是"天"和"秒"两种粒度里,"秒"这一档的默认选择;DateTime64(precision)用8字节存储,把时间戳的精度真实扩展到了亚秒级(本文用的DateTime64(3)是毫秒精度,还可以设到微秒/纳秒),代价是存储体积是Date的4倍。真实选择哪种类型的原则很直接:只需要天粒度、日期范围不会跑出1970~2149就用Date;需要更早或更晚的日期(比如生日、历史档案)用Date32;需要秒精度的时间戳用DateTime;需要精确到毫秒甚至更细(比如高频交易时间戳、日志埋点)用DateTime64——存储代价从2字节到8字节,选择范围和精度刚好够用的类型,而不是无脑都用最大最精确的那个。

分步演示:从静默钳制到四种类型的真实取舍

每一步都是本文上面用真实边界日期跑出来的原始结果。

Date类型的边界,真实是静默钳制不是报错
这一步新验证的边界

    本文用到的工具

    • 本机 ClickHouse 26.9.4(单节点),真实测试Date/Date32的边界钳制行为
    京ICP备2026067502号-1京公网安备11010502063487号