Skip to content
Donghai's Blog
Go back

Dynamics 365日期和时间字段

本文整理了 Dynamics 365Dataverse 中日期和时间(Date and Time)字段的基础概念、配置选项以及在不同使用场景下的实际表现

Table of contents

Open Table of contents

概述

由于 Dataverse 统一以 UTC 时区存储日期时间数据,而 (1)界面显示、(2)Client API 与 (3)Web API 又根据字段配置和用户时区产生不同的行为,这类字段常常成为以下问题的重灾区:

本文通过字段配置说明 + 实际实验 + 数据库存储 / API 返回对比的方式,一次性理清这些容易混淆的点

基本概念

DataverseUTC 时区存储所有日期和时间值,当应用显示值或处理用户输入时,会根据字段的“格式”和“时区调整”选项,结合当前用户的时区设置进行动态转换

基本概念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. 新建日期和时间字段

字段清单如下

#名称字段名称格式时区调整
1D - User Localgdh_d_userlocal仅日期用户当地时间
2D - TZ independentgdh_d_tz_independent仅日期时区无关
3D - Dgdh_d_d仅日期仅日期
4DT - User Localgdh_dt_userlocal日期和时间用户当地时间
5DT - TZ independentgdh_dt_tz_independent日期和时间时区无关

Step 2. 在表单中填写日期和时间字段

(GMT+08:00) Beijing, Chongqing, Hong Kong, Urumqi 时区下,对字段进行赋值

对日期和时间字段进行赋值

#名称字段名称填写内容
1D - User Localgdh_d_userlocal2025-03-10
2D - TZ independentgdh_d_tz_independent2025-03-12
3D - Dgdh_d_d2025-03-14
4DT - User Localgdh_dt_userlocal2025-03-10 14:30
5DT - TZ independentgdh_dt_tz_independent2025-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';

在数据库查看数据

#名称字段名称填写内容数据库存储的值
1D - User Localgdh_d_userlocal2025-03-102025-03-09 16:00:00.000
2D - TZ independentgdh_d_tz_independent2025-03-122025-03-12 00:00:00.000
3D - Dgdh_d_d2025-03-142025-03-14 00:00:00.000
4DT - User Localgdh_dt_userlocal2025-03-10 14:302025-03-10 06:30:00.000
5DT - TZ independentgdh_dt_tz_independent2025-03-24 09:302025-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

使用 Test06 查看数据

#名称字段名称数据库存储的值看到的值
1D - User Localgdh_d_userlocal2025-03-09 16:00:00.0002025-03-09
2D - TZ independentgdh_d_tz_independent2025-03-12 00:00:00.0002025-03-12
3D - Dgdh_d_d2025-03-14 00:00:00.0002025-03-14
4DT - User Localgdh_dt_userlocal2025-03-10 06:30:00.0002025-03-10 10:30
5DT - TZ independentgdh_dt_tz_independent2025-03-24 09:30:00.0002025-03-24 09:30

问题:为什么 Test06 在表单上看到的 DT - User Local 是 2025-03-10 10:30:00.000

Test06(GMT+04:00 巴库时区)看到 DT - User Local2025-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 Local2025-03-09

D - User Local 是“仅日期+用户当地时间”,数据库存储的 UTC2025-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)的取值逻辑

js获取gdh_d_userlocal和gdh_dt_userlocal

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'

(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 APIClient API
返回格式ISO 8601 字符串(带 T/Z)JavaScript Date 对象
时区处理仅返回 UTC 原始值(无转换)用户当地时间返回 UTC Date 对象,
时区无关返回浏览器时区 Date 对象
仅日期字段D-D 无 T/Z,其他带 T/Z均返回 Date 对象(含时间)
适用场景跨系统集成(如对接外部服务)表单内前端逻辑处理

补充说明

避坑提醒:仅日期字段的查询注意事项

“仅限日期” 类型的日期和时间字段,不允许使用下面的查询运算符,当这些运算符用于查询时,会引发无效运算符异常错误

例如:对 “D-D” 列进行筛选,筛选条件如下:

对“D-D”列设置筛选条件

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

出现异常: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设置用户默认时区》


Share this post on:

Previous Post
AstroPaper 5.0(译)
Next Post
解决XrmToolBox启动时报NuGet程序集加载失败
BlogsClub Meo Forever Blog