
NocoBase 日期时间字段类型详解五种存储语义、数据库类型映射与时区处理机制【免费下载链接】nocobaseNocoBase is an open-source AI no-code platform for building business systems fast. Instead of generating everything from scratch, AI works on top of production-proven infrastructure and a WYSIWYG no-code interface, so you get both speed and reliability.项目地址: https://gitcode.com/GitHub_Trending/no/nocobase在 NocoBase 中日期时间类字段是业务系统建模中出现频率最高、也最容易踩坑的字段族预约时间、截止时间、执行时间、统计口径的“天/时”都离不开它。本篇基于 NocoBase 官方文档中的「日期时间字段类型」概述结合packages/core/database中的字段源码完整讲解五种日期时间字段含时区/不含时区、日期、时间、Unix 时间戳的存储语义、在 NocoBase / MySQL / PostgreSQL 之间的类型对照、含时区与不含时区的写入处理流程以及 UTC 时区转换的实际表现帮助你在设计数据模型时正确选择字段类型并理解底层存取机制。一、五种日期时间字段类型NocoBase 的日期时间字段类型包括以下五种它们的核心区别在于“存什么、怎么转”日期时间含时区—— 日期时间会统一转换为 UTC 时间协调世界时并在需要时进行时区转换日期时间不含时区—— 存储不带时区信息的日期和时间日期不含时间—— 仅存储日期不包含时间部分时间—— 仅存储时间不包含日期部分Unix 时间戳—— 存储为 Unix 时间戳通常为自 1970 年 1 月 1 日以来的秒数。各日期相关字段类型的示例值如下字段类型示例值描述日期时间含时区2024-08-24T07:30:00.000Z日期时间会统一转换为 UTC 时间协调世界时日期时间不含时区2024-08-24 15:30:00不带时区的日期时间仅记录日期和时间日期不含时间2024-08-24仅存储日期信息不包括时间时间15:30:00仅存储时间信息不包括日期Unix 时间戳1724437800从 UTC 时间 1970 年 1 月 1 日 00:00:00 开始所经过的秒数从源码结构看这五类字段在 packages/core/database/src/fields/ 目录中各自有独立的实现类datetime-tz-field.ts含时区、datetime-no-tz-field.ts不含时区、date-only-field.ts日期、time-field.ts时间和unix-timestamp-field.tsUnix 时间戳它们的共同基座是 date-field.ts。二、NocoBase / MySQL / PostgreSQL 类型对照各字段类型在三种体系中的底层类型对照如下字段类型NocoBaseMySQLPostgreSQL日期时间含时区Datetime with timezoneTIMESTAMP / DATETIMETIMESTAMP WITH TIME ZONE日期时间不含时区Datetime without timezoneDATETIMETIMESTAMP WITHOUT TIME ZONE日期不含时间DateDATEDATE时间TimeTIMETIME WITHOUT TIME ZONEUnix 时间戳Unix timestampINTEGER / BIGINTINTEGER / BIGINT时间含时区--TIME WITH TIME ZONE备注MySQLTIMESTAMP的数据范围介于 UTC 时间1970-01-01 00:00:01 ~ 2038-01-19 03:14:07之间超出此范围时建议使用DATETIME或BIGINT存储 Unix 时间戳。这个范围限制是选择字段类型时的重要实操依据如果你的业务可能涉及 1970 年以前的历史数据如历史档案或 2038 年以后的远期时间如长期合约含时区日期时间不应依赖 MySQLTIMESTAMP而应落在DATETIME或 Unix 时间戳BIGINT上——后者在数值范围内几乎不受此限制。三、日期时间存储的处理流程3.1 含时区统一以 UTC 为基准存储值受服务端 TZ 影响含时区类型文档中同时提及日期时间不含时区与Unix 时间戳在存储链路中也会经过类似的时区换算环节的核心处理思路是写入时统一换算到 UTC 基准读取/展示时再按需要转回目标时区。这里有一条关键备注必须理解为了支持更广泛的数据范围NocoBase 的“日期时间含时区”字段在 MySQL 数据库里实际使用的是DATETIME存储的日期值是根据服务端 TZ 环境变量转换之后的值如果 TZ 环境变量变更之后日期时间的存值会产生变化。源码印证了这一点。date-field.ts 中的DateField是所有日期时间字段的基础实现数据类型声明为DataTypes.DATE(3)毫秒精度在 MySQL 方言下 Sequelize 会将其映射为DATETIME而非会做会话时区自动换算且范围受限的TIMESTAMP时区解析函数resolveTimeZone支持三种来源timezone server时取数据库配置的rawTimezone即服务端 TZ 环境变量timezone client时优先取请求上下文中的时区未指定时回退到服务端时区见 date-field.ts写入侧的setter会把YYYY-MM-DD HH:mm:ss形式的字符串拼接上解析出的时区偏移构造为真正的Date对象再入库见 date-field.ts读取侧的get()则反向用服务端时区把数据库原始值解析回Date保证上层拿到的始终是可参与时区换算的时间对象。而“含时区”这一变体本身就是很薄的一层封装datetime-tz-field.ts 中DatetimeTzField直接继承DateField仅将type声明为datetimeTz说明含时区语义的复杂度时区解析、默认值、时间戳属性绑定都沉淀在DateField基类中。另一个细节是DateField.bind()中会检查interface createdAt / updatedAt并相应地把模型的_timestampAttributes指向该字段即created_at/updated_at这类系统时间戳接口最终也是走同一套日期时间字段实现。3.2 不含时区原样存取但写入时仍会做一次时区“锚定”不含时区类型看起来“什么都不转”实际实现却并不简单。datetime-no-tz-field.ts 中有几个值得注意的设计按方言选择列类型PostgreSQL 下使用TIMESTAMP即TIMESTAMP WITHOUT TIME ZONEMySQL 兼容方言下使用DATETIME其余方言回退到DataTypes.DATE见 datetime-no-tz-field.ts写入时锚定时区set()逻辑中当输入是严格 ISO 8601 UTC 字符串正则^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$或Date对象时会先用数据库配置的rawTimezone、再用 Node 进程的getTimezoneOffset()做两次utcOffset换算见 datetime-no-tz-field.ts。这样做是为了让“不带时区”的存值与服务端所在时区对齐避免客户端传来的 UTC 字符串原样落库后与本地时间产生 8 小时之类的偏差MySQL 毫秒截断MySQL 方言下会执行momentVal.millisecond(0)因为 MySQLDATETIME默认不带毫秒提前截断可避免精度歧义读取时格式化get()会把Date格式化为YYYY-MM-DD HH:mm:ss字符串返回保证 API 输出形态稳定默认值与更新时间钩子beforeSave事件处理defaultToCurrentTime新记录为空时写入当前时间与onUpdateToCurrentTime每次更新时刷新为当前时间并同样注册到beforeBulkCreate上见 datetime-no-tz-field.ts。DateField基类中还有配套的默认值处理若字段配置了 ISO 8601 字符串默认值且运行在 MySQL 兼容方言下init()阶段会按解析出的时区把它格式化为YYYY-MM-DD HH:mm:ss再交给数据库从源头消除“UTC 字符串默认值”在DATETIME列上被误解的问题见 date-field.ts。3.3 Unix 时间戳BIGINT 数值存取 精度选项Unix 时间戳字段继承DateField但把数据类型换成DataTypes.BIGINT并在存取两侧做数值与Date的双向转换实现位于 unix-timestamp-field.ts精度选项支持accuracy配置可来自字段 options 或uiSchema的x-component-props.accuracy默认second除以 1000 取整即秒级时间戳设为millisecond时直接存毫秒值读取get()把数据库数值乘以对应倍率还原为Date写入set()把Date或数值经Math.floor截断后落库保证库内始终是纯整数由于底层是整数它不受 MySQLTIMESTAMP的 1970~2038 范围限制也便于做数值化的区间计算与跨语言系统交换。四、UTC 与时区转换同一个时间的多种“面貌”UTC协调世界时Coordinated Universal Time是全球时间标准用于协调和统一世界各地的时间。它基于原子钟的高精度时间标准并且与地球自转的时间保持同步。含时区字段的存取链路以 UTC 为统一基准。由于 UTC 时间与本地时间存在时区差直接展示 UTC 原始值可能会让用户误解NocoBase 因此在展示层会按需转换为本地时区。以同一时刻在不同时区的呈现为例时区日期时间UTC2024-08-24T07:30:00.000Z东八区 (UTC8)2024-08-24 15:30:00东五区 (UTC5)2024-08-24 12:30:00西五区 (UTC-5)2024-08-24 02:30:00英国时间 (UTC0)2024-08-24 07:30:00中部时间 (UTC-6)2024-08-23 01:30:00以上表示的都是同一个时间只是时区有所区别。理解这一点后排查“时间差 8 小时”“跨天边界不对”这类问题就有明确抓手先确认字段是含时区还是不含时区再确认服务端 TZ 环境变量与客户端预期时区是否一致。五、选型建议按业务语义选字段结合上述语义给出可落地的选型参考业务诉求推荐字段理由预约时间、任务截止时间、跨时区协作、工作流定时条件日期时间含时区统一 UTC 基准展示时按用户时区转换避免“同一时刻多地显示不一致”只记录本地日历时间、无需时区换算如单据上的开单时间文本语义日期时间不含时区原样存取展示稳定不受展示端时区影响只需要“哪一天”如出生日期、统计日期日期不含时间无时间部分天然适合按天分组与日期区间筛选只需要“几点几分”如营业时段、排班时刻时间无日期部分适合 TIME 语义查询需要跨系统交换、数值比较、避免 2038 边界问题Unix 时间戳BIGINT 整数存取范围大、无时区歧义可选秒级/毫秒级精度各类型字段在界面上的录入组件、筛选能力与校验规则的详细说明可分别参阅 日期时间含时区、日期时间不含时区、日期、时间 与 Unix 时间戳 的分页文档以及 字段总览。六、相关资源文档入口日期时间字段类型概述字段实现源码DateField 基类时区解析、默认值、时间戳属性绑定DatetimeTzField含时区变体DatetimeNoTzField不含时区MySQL DATETIME / PG TIMESTAMPUnixTimestampFieldBIGINT 数值存取与精度控制DateOnlyField日期字段TimeField时间字段适用前提提示以上类型对照与存储行为以当前仓库代码为准MySQL 相关说明适用于 MySQL 兼容方言含 MariaDBPostgreSQL 说明适用于postgres方言“日期时间含时区”在 MySQL 下存值受服务端 TZ 环境变量影响的特性意味着变更部署环境的 TZ 配置前需要评估存量数据的展示变化。【免费下载链接】nocobaseNocoBase is an open-source AI no-code platform for building business systems fast. Instead of generating everything from scratch, AI works on top of production-proven infrastructure and a WYSIWYG no-code interface, so you get both speed and reliability.项目地址: https://gitcode.com/GitHub_Trending/no/nocobase创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考