本文整理了 Dynamics 365 、Dataverse 中日期和时间(Date and Time)字段的基础概念、配置选项以及在不同使用场景下的实际表现
Table of contents
Open Table of contents
概述
由于 Dataverse 统一以 UTC 时区存储日期时间数据,而 (1)界面显示、(2)Client API 与 (3)Web API 又根据字段配置和用户时区产生不同的行为,这类字段常常成为以下问题的重灾区:
- 不同用户看到的日期不一致
- 表单显示正确,但数据库或接口返回 “少了/多了8小时”
- Client API / Web API 返回的时间与预期不符
本文通过字段配置说明 + 实际实验 + 数据库存储 / API 返回对比的方式,一次性理清这些容易混淆的点
基本概念
Dataverse 以 UTC 时区存储所有日期和时间值,当应用显示值或处理用户输入时,会根据字段的“格式”和“时区调整”选项,结合当前用户的时区设置进行动态转换
基本概念1:仅日期或日期和时间
在实体新建日期和时间字段时,可以选择以下以下格式
| 格式 | 描述 |
|---|---|
| 仅日期 | 存储时间为 00:00:00(UTC),界面仅显示日期 |
| 日期和时间 | 同时存储并显示日期与时间 |

基本概念2:时区调整
在“高级选项”中,可以设置“时区调整”,需要注意的是,“时区调整”的选项和“格式”(仅日期或日期和时间)联动,联动逻辑如下

格式等于“仅日期”
如果格式选择了“仅日期”,那么“时区调整”将有如下3个选项
| 选项 | 时区调整 | 描述 |
|---|---|---|
| 1 | 用户当地时间 | 默认值,根据用户时区进行转换 |
| 2 | 时区无关 | 不进行时区转换 |
| 3 | 仅日期 | 不存储时间部分(始终为 00:00:00) |
格式等于“日期和时间”
如果格式选择了“日期和时间”,那么“时区调整”将有如下2个选项
| 选项 | 时区调整 | 描述 |
|---|---|---|
| 1 | 用户当地时间 | 默认值,根据用户时区进行转换 |
| 2 | 时区无关 | 不进行时区转换 |
TIP
字段创建完成后,“格式” 和 “时区调整” 在一定条件下仍可修改(详见下文)
基本概念3:已创建字段“格式”和“时区调整”还能修改吗?
经实际验证,结论如下:
| “格式” & “时区调整” | “格式”是否可以修改? | “时区调整”是否可以修改? |
| 仅日期 & 用户当地时间 | ✔ | ✔ |
| 仅日期 & 时区无关 | ✔ | ❌ |
| 仅日期 & 仅日期 | ❌ | ❌ |
| 日期和时间 & 用户当地时间 | ✔ | ✔ |
| 日期和时间 & 时区无关 | ✔ | ❌ |
基本概念4:“时区调整”该如何选择?
用户当地时间
这是默认选项。字段值会根据每个用户个人设置中的时区进行动态转换。例如,一个销售订单的“创建时间”在伦敦的用户看来是 2024-01-01 12:00:00,而在纽约的用户看来则会显示为 2024-01-01 07:00:00(如果该时间点对应的是同一UTC时刻)。这个选项适用于绝大多数需要记录事件发生时刻,并期望在不同地区用户视角下呈现为当地时间场景,例如会议时间、任务截止日期、操作日志等
时区无关
选择此选项后,字段值将不进行任何时区转换。无论查看者身处哪个时区,他们在界面上看到的日期和时间值都是完全相同的。数据库存储的值是什么,用户看到的就是什么。这适用于那些本身就不包含时区信息,或不应受时区影响的场景。例如,酒店的入住登记时间(通常以酒店当地时间为准)、健身房的打卡记录(以打卡地当地时间为准)、或者一个全球统一发布的公告的“发布日期和时间”
仅日期
此选项仅在字段“格式”为“仅日期”时可用。选择后,字段将仅存储日期部分,时间部分固定为 00:00:00(UTC),并且不进行任何时区转换。所有用户,无论身处何地,都将看到完全相同的日期。适用于生日、结婚纪念日、节日等只关心日期,不关心具体时间和时区的场景
实例
接下来,我将在 Invoice 实体新建如下5个“日期和时间”字段,然后对它们进行赋值,接着到数据库上看存储情况
TIP
- 目前我所在的时区是
(GMT+08:00) Beijing, Chongqing, Hong Kong, Urumqi - 用户 Test06 使用的的时区是
(GMT+04:00) Baku
接下来是具体的“实验”步骤
Step 1. 新建日期和时间字段
字段清单如下
| # | 名称 | 字段名称 | 格式 | 时区调整 |
|---|---|---|---|---|
| 1 | D - User Local | gdh_d_userlocal | 仅日期 | 用户当地时间 |
| 2 | D - TZ independent | gdh_d_tz_independent | 仅日期 | 时区无关 |
| 3 | D - D | gdh_d_d | 仅日期 | 仅日期 |
| 4 | DT - User Local | gdh_dt_userlocal | 日期和时间 | 用户当地时间 |
| 5 | DT - TZ independent | gdh_dt_tz_independent | 日期和时间 | 时区无关 |
Step 2. 在表单中填写日期和时间字段
在 (GMT+08:00) Beijing, Chongqing, Hong Kong, Urumqi 时区下,对字段进行赋值

| # | 名称 | 字段名称 | 填写内容 |
|---|---|---|---|
| 1 | D - User Local | gdh_d_userlocal | 2025-03-10 |
| 2 | D - TZ independent | gdh_d_tz_independent | 2025-03-12 |
| 3 | D - D | gdh_d_d | 2025-03-14 |
| 4 | DT - User Local | gdh_dt_userlocal | 2025-03-10 14:30 |
| 5 | DT - TZ independent | gdh_dt_tz_independent | 2025-03-24 09:30 |
Step 3. 数据库查看数据
SELECT gdh_d_userlocal AS 'D - User Local',
gdh_d_tz_independent AS 'D - TZ independent',
gdh_d_d AS 'D - D',
gdh_dt_userlocal AS 'DT - User Local',
gdh_dt_tz_independent AS 'DT - TZ independent'
FROM gdh_invoice
WHERE gdh_no = 'SAS-00000001';

| # | 名称 | 字段名称 | 填写内容 | 数据库存储的值 |
|---|---|---|---|---|
| 1 | D - User Local | gdh_d_userlocal | 2025-03-10 | 2025-03-09 16:00:00.000 |
| 2 | D - TZ independent | gdh_d_tz_independent | 2025-03-12 | 2025-03-12 00:00:00.000 |
| 3 | D - D | gdh_d_d | 2025-03-14 | 2025-03-14 00:00:00.000 |
| 4 | DT - User Local | gdh_dt_userlocal | 2025-03-10 14:30 | 2025-03-10 06:30:00.000 |
| 5 | DT - TZ independent | gdh_dt_tz_independent | 2025-03-24 09:30 | 2025-03-24 09:30:00.000 |
问题:为什么D-User Local在数据库中存储的是2025-03-09 16:00:00.000 ?
前面说过,我是在(GMT+08:00) Beijing, Chongqing, Hong Kong, Urumqi时区下填写 2025-03-10,Dataverse 是以UTC时区存储所有日期时间数据,且 “仅日期 + 用户当地时间” 格式的字段会先将用户填写的本地日期转换为 UTC 时间,再存储到数据库
为了转换为 UTC 时间存储,计算逻辑:
= 2025-03-10 00:00:00 (GMT+08:00) - 8 小时
= 2025-03-09 16:00:00 (UTC)
Step 4. 使用 Test06 查看数据
TIP
用户 Test06 使用的的时区是 (GMT+04:00) Baku

| # | 名称 | 字段名称 | 数据库存储的值 | 看到的值 |
|---|---|---|---|---|
| 1 | D - User Local | gdh_d_userlocal | 2025-03-09 16:00:00.000 | 2025-03-09 |
| 2 | D - TZ independent | gdh_d_tz_independent | 2025-03-12 00:00:00.000 | 2025-03-12 |
| 3 | D - D | gdh_d_d | 2025-03-14 00:00:00.000 | 2025-03-14 |
| 4 | DT - User Local | gdh_dt_userlocal | 2025-03-10 06:30:00.000 | 2025-03-10 10:30 |
| 5 | DT - TZ independent | gdh_dt_tz_independent | 2025-03-24 09:30:00.000 | 2025-03-24 09:30 |
问题:为什么 Test06 在表单上看到的 DT - User Local 是 2025-03-10 10:30:00.000 ?
Test06(GMT+04:00 巴库时区)看到 DT - User Local 是 2025-03-10 10:30,本质是 Dataverse 从 UTC 存储值反向转换为用户本地时区时间的规则导致,Test06 所在时区是 GMT+04:00(Baku,比 UTC 快 4 小时),系统会将 UTC 存储值转换为其本地时间:
Test06 看到的时间 = UTC 存储值 + 4 小时
= 2025-03-10 06:30:00 + 4 小时
= 2025-03-10 10:30:00
问题:为什么 D - User Local 是 2025-03-09 ?
D - User Local 是“仅日期+用户当地时间”,数据库存储的 UTC 是 2025-03-09 16:00,转换为巴库时间:
2025-03-09 16:00(UTC) + 4 小时 = 2025-03-09 20:00(巴库时间)
由于是“仅日期”格式,表单只显示日期部分 2025-03-09
Step 5. Client API 获取日期时间字段值的结果解析
(1) “用户当地时间”字段(D/DT - User Local)的取值逻辑

Xrm.Page.getAttribute("gdh_d_userlocal").getValue();
// Mon Mar 10 2025 00:00:00 GMT+0800 (中国标准时间)
Xrm.Page.getAttribute("gdh_d_userlocal").getValue().toUTCString();
// 'Sun, 09 Mar 2025 16:00:00 GMT'
Xrm.Page.getAttribute("gdh_dt_userlocal").getValue();
// Mon Mar 10 2025 14:30:00 GMT+0800 (中国标准时间)
Xrm.Page.getAttribute("gdh_dt_userlocal").getValue().toUTCString();
// 'Mon, 10 Mar 2025 06:30:00 GMT'
- Client API 中,
getAttribute("字段名").getValue()返回的是 JavaScript Date 对象,返回的是 - 对于“用户当地时间”类型的字段,这个
Date对象的底层存储是UTC时间(和数据库一致) - 调用
toUTCString()会直接输出该Date对象的UTC字符串,因此和数据库值完全匹配
(2) “时区无关”字段(D/DT - TZ independent)的取值逻辑
Xrm.Page.getAttribute("gdh_d_tz_independent").getValue();
// Wed Mar 12 2025 00:00:00 GMT+0800 (中国标准时间)
Xrm.Page.getAttribute("gdh_d_tz_independent").getValue().toString();
// 'Wed Mar 12 2025 00:00:00 GMT+0800 (中国标准时间)'
Xrm.Page.getAttribute("gdh_dt_tz_independent").getValue();
// Mon Mar 24 2025 09:30:00 GMT+0800 (中国标准时间)
Xrm.Page.getAttribute("gdh_dt_tz_independent").getValue().toString();
// 'Mon Mar 24 2025 09:30:00 GMT+0800 (中国标准时间)'
我所在浏览器的时区是 GMT+08,因此 toString() 输出的是 GMT+0800 对应的时间(和数据库存储的数值完全一致,只是时区标识变成了浏览器时区)
Step 6. Web API 获取日期时间字段的解析
var req = new XMLHttpRequest();
var apiVersion = "v9.1";
var entityName = "gdh_invoices";
var recordId = "98F83878-1E35-EF11-8409-0017FA0671FA";
var selectFields = [
"gdh_d_d",
"gdh_d_tz_independent",
"gdh_d_userlocal",
"gdh_dt_tz_independent",
"gdh_dt_userlocal",
].join(",");
var baseUrl = Xrm.Page.context.getClientUrl();
var apiPath = `/api/data/${apiVersion}/${entityName}(${recordId})`;
var queryParams = `?$select=${selectFields}`;
var fullUrl = baseUrl + apiPath + queryParams;
req.open("GET", fullUrl, true);
req.setRequestHeader("OData-MaxVersion", "4.0");
req.setRequestHeader("OData-Version", "4.0");
req.setRequestHeader("Accept", "application/json");
req.setRequestHeader("Content-Type", "application/json; charset=utf-8");
req.setRequestHeader("Prefer", 'odata.include-annotations="*"');
req.onreadystatechange = function () {
if (this.readyState === 4) {
req.onreadystatechange = null;
if (this.status === 200) {
var result = JSON.parse(this.response);
var gdh_d_d = result["gdh_d_d"];
var gdh_d_tz_independent = result["gdh_d_tz_independent"];
var gdh_d_userlocal = result["gdh_d_userlocal"];
var gdh_dt_tz_independent = result["gdh_dt_tz_independent"];
var gdh_dt_userlocal = result["gdh_dt_userlocal"];
} else {
Xrm.Utility.alertDialog(this.statusText);
}
}
};
req.send();
{
"@odata.context": "https://sample.com/api/data/v9.1/$metadata#gdh_invoices(gdh_d_d,gdh_d_tz_independent,gdh_d_userlocal,gdh_dt_tz_independent,gdh_dt_userlocal)/$entity",
"@odata.etag": "W/\"9300793\"",
"gdh_invoiceid": "98f83878-1e35-ef11-8409-0017fa0671fa",
"gdh_dt_userlocal@OData.Community.Display.V1.FormattedValue": "2025/03/10 14:30",
"gdh_dt_userlocal": "2025-03-10T06:30:00Z",
"gdh_d_d@OData.Community.Display.V1.FormattedValue": "2025/03/14",
"gdh_d_d": "2025-03-14",
"gdh_d_userlocal@OData.Community.Display.V1.FormattedValue": "2025/03/10",
"gdh_d_userlocal": "2025-03-09T16:00:00Z",
"gdh_d_tz_independent@OData.Community.Display.V1.FormattedValue": "2025/03/12",
"gdh_d_tz_independent": "2025-03-12T00:00:00Z",
"gdh_dt_tz_independent@OData.Community.Display.V1.FormattedValue": "2025/03/24 09:30",
"gdh_dt_tz_independent": "2025-03-24T09:30:00Z"
}
(补充说明)ISO 8601 中 T/Z 的含义
| 字符 | 定义 | Web API中的实际作用 |
|---|---|---|
| T | 分隔日期和时间 | 固定格式分隔符,Web API 所有带时间的日期字段返回值都会包含 T (如 2025-03-10T06:30:00Z) |
| Z | 代表 UTC 时区(Zulu 时间) | 明确标识该时间是 UTC 时间,无偏移量; Web API中所有用户当地时间/时区无关的时间字段都会带Z |
(补充说明)Web API 与 Client API 的取值对比
| 特性 | Web API | Client API |
|---|---|---|
| 返回格式 | ISO 8601 字符串(带 T/Z) | JavaScript Date 对象 |
| 时区处理 | 仅返回 UTC 原始值(无转换) | 用户当地时间返回 UTC Date 对象, 时区无关返回浏览器时区 Date 对象 |
| 仅日期字段 | D-D 无 T/Z,其他带 T/Z | 均返回 Date 对象(含时间) |
| 适用场景 | 跨系统集成(如对接外部服务) | 表单内前端逻辑处理 |
补充说明
避坑提醒:仅日期字段的查询注意事项
“仅限日期” 类型的日期和时间字段,不允许使用下面的查询运算符,当这些运算符用于查询时,会引发无效运算符异常错误
- X 分钟以前
- X 小时以前
- 过去 X 小时
- 接下来 X 小时
例如:对 “D-D” 列进行筛选,筛选条件如下:

执行后会报错:2147779605The operator is not valid or it is not supported.

该错误码对应 Dataverse 官方文档中的 DMQE_INVALID_OPERATOR 错误,描述为:
The specified operator is not valid for the attribute type, or the operator is not supported for the query type.(指定的运算符对该属性类型无效,或该运算符不支持此查询类型。)
如何设置用户的时区?
个人设置
右上角的设置按钮 —> 个性化设置 —> General Tab —> 选择时区
后台设置
请参考我的另一篇文章《XrmToolBox设置用户默认时区》