news 2026/9/14 16:32:32

NocoBase 日期时间(不含时区)字段:datetimeNoTz 的配置、存储与筛选实现原理

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
NocoBase 日期时间(不含时区)字段:datetimeNoTz 的配置、存储与筛选实现原理

NocoBase 日期时间(不含时区)字段:datetimeNoTz 的配置、存储与筛选实现原理

【免费下载链接】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 的「日期时间(不含时区)」(Date time without timezone)字段用于保存不做时区转换的日期时间,是排班、营业时间、课程时间等本地时间业务的基础数据类型。本文以该字段的创建与配置方法为主体,深入结合@nocobase/database中的DatetimeNoTzField源码,讲清它在不同数据库方言下的真实存储类型、写入时的时区处理逻辑,以及筛选操作符如何按+00:00固定时区解析条件,帮助你在建模、写 API 查询和理解测试断言时做到心中有数。

字段定位与适用场景

在 NocoBase 中,日期时间(不含时区)用于保存不做时区转换的日期和时间,适合更关注本地显示值的业务:

  • 本地排班时间
  • 课程开始时间、考试时间
  • 门店营业时间点
  • 不希望跨时区转换的业务时间

选型上它与两个近邻类型形成互补:如果业务需要表达一个全球一致的真实时间点(预约、截止时间、跨时区协作),应选择日期时间(含时区);如果只需要日期部分,选择日期。完整的字段分类与映射逻辑见字段总览。

前端界面上,该字段的注册信息在 datetimeNoTz.ts 中可以确认:name = 'datetimeNoTz'、分组为datetime、组内排序order = 2、界面标题为 "Datetime (without time zone)",并且标记sortable = truevalidationType = 'date',默认 UI 组件是带showTime: falseDatePicker。这解释了文档中「页面组件:编辑模式使用日期时间选择器」「支持按时间排序」这两条特性。

创建字段与配置项

在数据表的「Configure fields」页面中,点击「Add field」,选择「日期时间(不含时区)」即可创建该类型字段。创建表单中的配置项如下:

配置说明
Field interface字段的界面类型。日期时间(不含时区)对应datetimeNoTz,决定页面中如何录入和展示。
Field display name字段在界面中显示的名称,比如「排班时间」「课程时间」「营业时间」。建议使用业务人员能直接理解的名称。
Field name字段标识名称,用于 API、关系字段、权限、工作流等内部引用。创建后通常不再修改,只支持字母、数字和下划线,并且必须以字母开头。
Field type字段在数据层的类型。日期时间(不含时区)通常使用datetimeNoTz
Default value默认值。新增记录时,如果用户没有填写,可以自动带出默认值。
Validation rules校验规则。可以配置必填、时间范围等。
Description字段说明。适合写字段含义、填写要求、数据来源或维护人。

注意:字段名创建后会被页面区块、权限、工作流和 API 引用。创建前先确认命名,避免后续修改带来配置调整成本。

界面上的两个专属配置项同样来自 datetimeNoTz.ts 中的properties定义:

  • defaultToCurrentTime(「默认值取当前服务器时间」):新增记录时未填写该字段,自动写入当前时间;
  • onUpdateToCurrentTime(「更新时自动把时间戳刷新为当前服务器时间」):每次更新记录时把该字段重置为当前时间。

后端字段选项类型见 datetime-no-tz-field.ts 中的DatetimeNoTzFieldOptions,其type固定为'datetimeNoTz',并在 fields/index.ts 中被并入全局FieldOptions联合类型,保证集合建模时类型校验通过。

默认行为一览

日期时间(不含时区)字段的默认行为如下:

特性说明
默认 Field interfacedatetimeNoTz
默认 Field typedatetimeNoTz
可选 Field typedatetimeNoTz(前端availableTypes另允许string,见下文源码分析)。
页面组件编辑模式使用日期时间选择器。
筛选支持按时间点、区间、为空、不为空筛选。
排序支持按时间排序。
校验支持必填和时间范围等校验。

存储实现:不同数据库方言下的真实列类型

「不含时区」的语义最终落在数据库列类型上。从源码看,DatetimeNoTzField 的dataType按方言分支:

get dataType() { if (this.database.inDialect('postgres')) { return DatetimeNoTzTypePostgres; // key = 'TIMESTAMP' } if (this.database.isMySQLCompatibleDialect()) { return DatetimeNoTzTypeMySQL; // key = 'DATETIME' } return DataTypes.DATE; }

也就是说:

  • PostgreSQL下创建TIMESTAMP(不带time zone后缀),数据库不保存任何时区偏移;
  • MySQL 及兼容方言(含 MariaDB)下创建DATETIME,同样不携带时区信息;
  • 其他方言(如 SQLite)退化为 Sequelize 的DATE类型。

这与含时区字段形成对照:后两者在数据库层都可能保存带偏移的时间,而datetimeNoTz列中存的就是"字面值"本身。

写入路径:set/get 钩子与时区归一化

DatetimeNoTzField通过additionalSequelizeOptions()返回一对自定义 getter/setter(datetime-no-tz-field.ts),这是"值进出模型"时实际生效的时区逻辑:

读值(getter):如果从数据库取出的值是Date实例,用 moment 格式化为YYYY-MM-DD HH:mm:ss字符串返回。因此 API 响应中该字段呈现的正是列中存储的原始字面时间,测试用例也验证了这一点——在时区为+01:00的数据库中写入'2023-03-24 12:00:00',读回toJSON()得到的仍是'2023-03-24 12:00:00'(见 datetime-no-tz.test.ts)。

写值(setter)

const dateOffset = new Date().getTimezoneOffset(); const momentVal = moment(val); if ((typeof val === 'string' && isIso8601(val)) || val instanceof Date) { momentVal.utcOffset(timezone); // timezone = rawTimezone || '+00:00' momentVal.utcOffset(-dateOffset, true); // 折算到服务器本地时区 } if (isMySQLCompatibleDialect) { momentVal.millisecond(0); // MySQL 列不保留毫秒 }

可以推断出这套逻辑的业务含义:

  1. 普通YYYY-MM-DD HH:mm:ss字符串原样落库,不做任何转换——这正是"不含时区"的核心承诺:业务传什么本地时间,库里就存什么;
  2. 带时区语义的输入(严格 ISO 8601 的...Z字符串或Date对象)会先按rawTimezone(默认+00:00)对齐,再折算为服务器本地时区表示后落库。测试用例证实:时区+01:00的库中写入'2023-03-24T12:00:00.892Z',最终存为'2023-03-24 13:00:00'(datetime-no-tz.test.ts);
  3. MySQL 系方言下毫秒被截断,避免DATETIME列的精度丢精度告警。

此外,beforeSave钩子(绑定到beforeSavebeforeBulkCreate事件)实现了两个默认值行为:新建记录且未赋值时若配置了defaultToCurrentTime,写入new Date();配置了onUpdateToCurrentTime时,任何更新都会把该字段刷新为当前时间。两者均有对应测试覆盖(datetime-no-tz.test.ts)。

筛选实现:操作符如何固定 +00:00

筛选能力由日期操作符模块 operators/date.ts 提供,其中对datetimeNoTz有两处特判:

  1. 时区解析parseDateTimezone(ctx)判断字段是否为DatetimeNoTzFieldfield?.type === 'datetimeNoTz'),是则强制返回'+00:00',否则回落到数据库级timezone配置(date.ts)。这意味着筛选条件中的时间不会被服务器时区二次折算,直接按字面值比较。
  2. 条件值格式化toDate()中,datetimeNoTz字段的条件值被格式化为moment(val).utcOffset('+00:00').format('YYYY-MM-DD HH:mm:ss')(date.ts),与列中存储的字面格式对齐。

在此基础上导出了一组可用的操作符,覆盖了文档所述"按时间点、区间、为空、不为空筛选":

操作符语义
$dateOn等于某天/某时间点(值可为范围数组,展开为gte + lt
$dateNotOn不等于
$dateBefore/$dateAfter早于 / 晚于(数组取对应边界)
$dateNotBefore/$dateNotAfter不早于(含) / 不晚于(含)
$dateBetween闭开区间gte + lt

操作符级行为由 operator/date/datetime-no-tz.test.ts 单独验证,可用于确认各操作符在不同方言下的实际 SQL 效果。

编辑与删除字段

创建后,点击字段右侧的「Edit」可以编辑字段配置。编辑字段主要用于调整字段在 NocoBase 中的展示和使用方式,比如修改显示名称、说明、默认值、校验规则或字段专属配置。如果字段来自主数据库中已经同步的表,编辑时通常是在做字段映射——把数据库字段映射为 NocoBase 的 Field type 和 Field interface。

配置允许编辑说明
Field display name修改字段在界面中的显示名称,不改变字段标识名称。
Field name字段标识名称创建后通常不能在编辑表单中修改。
Field interface条件支持主数据库字段或同步字段在字段映射时可以调整。调整后会影响页面输入、展示和校验方式。
Field type条件支持主数据库字段或同步字段在字段映射时可以调整。调整前需要确认已有数据能否按新类型使用。
Default value调整新增记录时的默认值。
Validation rules调整字段校验规则。
Description补充字段含义、填写要求、数据来源或维护人。

注意:切换 Field type 或 Field interface 不等于简单改一个显示名称。它会影响字段的存储方式(上文dataType分支)、输入组件、校验规则、筛选条件和工作流变量使用方式。已有数据较多时,先确认数据格式是否匹配——比如从string映射为datetimeNoTz后,存量非标准格式字符串会在 setter 解析时产生异常行为。

删除方面:点击字段右侧的「Delete」可以删除日期时间(不含时区)字段,主数据库中还可以勾选多个字段后批量删除。删除主数据库中新建的字段时,通常会同时删除数据库中的真实列及该列已有数据;删除从数据库同步或外部数据源映射出的字段时,影响范围取决于对应数据源和字段来源。

警告:删除字段可能影响页面区块、表单、筛选、权限、工作流、API、导入导出和已有数据。删除前先确认字段是否仍被业务配置引用。

页面与工作流中的使用

日期时间(不含时区)字段适合本地时间业务,典型用法:

场景用途
表单区块选择日期和时间。
表格区块展示、排序和筛选时间。
日历区块作为本地事件时间字段。
工作流作为时间条件字段。

在普通表中创建和管理字段的完整流程见普通表文档。与日期时间(含时区)相比,不含时区类型在工作流定时条件等场景下更保守——它只比较字面时间,不会因运行环境与数据录入者时区不同而产生偏移,适合"北京时间 09:00 的门店开店"这类明确绑定单一本地时区的业务。

小结

日期时间(不含时区)字段的完整技术画像可以归纳为三点:配置层面它是datetimeNoTz界面对应的本地时间类型,支持默认值、自动更新时间与日期校验;存储层面它在 PostgreSQL 落为TIMESTAMP、MySQL 落为DATETIME,普通字符串原样落库、带时区的输入折算后落库且 MySQL 截断毫秒;查询层面筛选操作符固定按+00:00解析条件值并与列值字面对比。理解了这三层,你就能准确判断它何时该选、何时应改用含时区字段,以及测试断言中那些"看起来反直觉"的时间值从何而来。

【免费下载链接】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),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/14 16:29:39

AI建站工具选型指南:We0.ai、ChatGPT Sites、Lovable与Bolt怎么选

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/14 16:29:15

用户画像驱动的协同过滤:冷启动友好型推荐系统实现

简介:本资源是一个面向人工智能初学者与项目实践者的音乐推荐系统完整工程,融合用户画像构建与基于用户的协同过滤算法,解决个性化音乐推荐中的冷启动与精度提升问题。项目基于KKBox公开竞赛数据集实现,采用Python3开发&#xff0…

作者头像 李华
网站建设 2026/9/14 16:28:43

基于OpenCV和dlib构建人脸识别考勤系统:从环境搭建到部署调优

简介:这套基于Python的员工人脸识别考勤系统,面向需要实现摄像头实时检测与身份验证的中高级Python开发者,结合OpenCV与Dlib完成人脸检测、特征点定位及模型训练,可应用于企业门禁、课堂签到等场景。压缩包共657个文件&#xff0c…

作者头像 李华