百积木文档
开发指南数据契约规范

时间与日期规范

业务时间统一使用 Unix epoch 毫秒,格式化字符串只用于显示和外部协议适配。

百积木平台控制的业务时间和系统时间统一使用 Unix epoch 毫秒整数。这一规则覆盖 API、模块方法、Connector 业务载荷、事件 payload、工作流、任务、缓存和数据库。

标准表示

位置类型示例
JSON / Connector / 事件 payloadinteger1785984572000
CModel DataTypeinteger{ "@type": "DataType", "type": "integer" }
Javalong / Long1785984572000L
Rusti641785984572000_i64
JavaScript / TypeScript安全整数 number1785984572000
PostgreSQL / MySQLBIGINT1785984572000

字段名使用业务语义,例如 createdAtupdatedAtlastTimestampdeliveryTime。单位由全局契约确定,不需要在每个时间点字段后追加 Millis。持续时间不是时间点,必须显式标明单位,例如 timeoutMsdurationMs

禁止的做法

  • 不得在同一字段中同时接受秒、毫秒和 RFC 3339 字符串。
  • 不得根据数值位数或字符串形态静默猜测单位并自动兼容。
  • 不得把 DateInstantDateTime 或数据库时间类型的默认 JSON 序列化结果当成公共契约。
  • 不得同时提供 lastTimestamplastOccurredAt 两套同义字段。
  • 不得在后端和数据库保存格式化后的本地时间字符串。

入参类型或单位错误时应立即拒绝,并返回 CModel 结构化校验错误。只接受现代业务时间的接口还应按业务范围校验,防止秒级值误入;历史日期场景则应使用适合该领域的范围,不能用一个全局阈值排除合法历史时间。

为什么数据库也存 BIGINT

数据库使用 TIMESTAMP/TIMESTAMPTZ 虽然便于日期函数计算,但 ORM 和驱动通常会把它序列化为格式化字符串,导致服务边界出现多种表示、隐式时区转换和精度差异。使用 BIGINT 可以让持久化、消息和接口保持同一份原始值,避免来回转换后改变语义。

排序、范围过滤和索引对 BIGINT 同样有效。确实需要按自然日、月份或时区做分析时,在查询或分析视图中显式转换,例如 PostgreSQL:

to_timestamp(created_at / 1000.0)

显示时区属于调用方或报表层。数据库中的原始时间点不带显示时区;格式化时必须显式选择用户或工作区时区。

日期和显示文本

不是所有看起来像时间的字段都是时间点:

  • 仅表示自然日的 reportDatebirthday 可以使用 ISO 日期字符串 YYYY-MM-DD,其 CModel 语义应明确为 date-only。
  • 面向人阅读的“明天下午”“预计半小时内”等内容是显示文本,应使用 etaTextscheduleDisplayText 等名称,不能命名为 etaAt
  • UI 的 datetime-local、中文日期和 RFC 3339 都是显示或输入格式。前端必须在组件边界转换为毫秒,提交给服务后不再携带格式化副本。
  • 外部标准强制使用 RFC 3339 时,只能在边界适配器转换;进入内部模型后立即恢复为毫秒。

迁移顺序

存量字段迁移必须一次完成生产者、消费者和数据:

  1. 发布结构化 CModel 契约和新主版本。
  2. 将数据库列确定性转换为 BIGINT,保留空值和毫秒精度。
  3. 更新服务模型、SQL 默认值和响应 envelope。
  4. 更新 Connector、前端、工作流和 Bundle Manifest。
  5. 部署后验证毫秒输入成功,秒级与字符串输入返回结构化错误。

不要长期保留“秒或毫秒都接受”的兼容分支。它会把契约错误隐藏到更深层,并让同一数据在不同链路产生不同含义。

本页内容