前言

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

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

  • 不同用户看到的日期不一致
  • 表单显示正确,但数据库或接口返回“少了 / 多了 8 小时”
  • Client API / Web API 返回的时间与预期不符

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

基本概念

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

新建「日期和时间」字段

在实体新建 日期和时间(Date and Time) 字段时,可以选择以下以下格式:仅日期或日期和时间。

格式 描述
仅日期 存储时间为 00:00:00(UTC),界面仅显示日期
日期和时间 同时存储并显示日期与时间

选择日期和时间字段的 “格式”

时区调整选项

在「高级选项」中,可以配置 时区调整。
可选项会随字段格式发生变化:

格式:仅日期

选项 时区调整 描述
1 用户当地时间 默认值,根据用户时区进行转换
2 时区无关 不进行时区转换
3 仅日期 不存储时间部分(始终为 00:00:00)

格式:日期和时间

选项 时区调整 描述
1 用户当地时间 默认值,根据用户时区进行转换
2 时区无关 不进行时区转换

💡 字段创建完成后,“格式” 和 “时区调整” 在一定条件下仍可修改(详见下文)。

已创建字段还能修改吗?

经实际验证,结论如下:

当前配置 格式可改 时区调整可改
仅日期 - 用户当地时间
仅日期 - 时区无关
仅日期 - 仅日期
日期和时间 - 用户当地时间
日期和时间 - 时区无关

时区调整该如何选择?

选项 描述
时区无关 当不需要 “时区” 信息时,例如酒店入住登记时间、发票付款时间等,使用该选项,选择该选项后,所有时区的用户将看到相同的日期和时间值
仅限日期 不关注一天的 “时间” 或 “时区” 时,例如生日或纪念日等,使用该选项,选择该选项后,所有时区的用户将看到完全相同的日期值。

💡 如果未来可能需要时间部分,优先选择「仅日期 - 时区无关」,一旦选择「用户当地时间」,不同时区用户可能看到不同日期

小实验:字段配置、存储、展示

我将在 Invoice 实体新添如下 5 个 “日期和时间” 字段,然后对它们进行赋值,接着到 DB 上看看存储情况。

  • 目前我使用的的时区是 (GMT+08:00) Beijing, Chongqing, Hong Kong, Urumqi
  • 用户 Test06 使用的的时区是 (GMT+04:00) Baku

(1)新建字段

5 个“日期和时间”字段如下:

# 名称 字段名称 格式 时区调整
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 日期和时间 时区无关

5 个 “日期和时间” 字段

(2)在表单中填写“日期和时间”字段

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

字段 填写值
D - User Local 2025-03-10
D - TZ independent 2025-03-12
D - D 2025-03-14
DT - User Local 2025-03-10 14:30
DT - TZ independent 2025-03-24 09:30

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

(3)在数据库查看数据

在数据库查看数据

结果如下:

字段 填写值 数据库存储的值
D - User Local 2025-03-10 2025-03-09 16:00:00.000
D - TZ independent 2025-03-12 2025-03-12 00:00:00.000
D - D 2025-03-14 2025-03-14 00:00:00.000
DT - User Local 2025-03-10 14:30 2025-03-10 06:30:00.000
DT - TZ independent 2025-03-24 09:30 2025-03-24 09:30:00.000

❓ 为什么 D - User Local 在 DB 中存储的是 2025-03-09 16:00:00.000 ?

我在 (GMT+08:00) Beijing, Chongqing, Hong Kong, Urumqi 时区下,对 D - User Local 进行赋值,

填写的时间是 2025-03-10

但是在数据库中,时间被存储为 UTC 时区,在东八区到 UTC 之间有 8 小时的时差

所以在本地填写的 2025-03-10 日期被转换到 UTC 时区时,

就变成了 2025-03-09 16:00:00.000

DT - User Local 同理。

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';

(4)使用 Test06 查看数据

注:用户 Test06 使用的的时区是 (GMT+04:00) Baku

使用 Test06 查看数据

❓ 为什么 用户 Test06 在表单上看到的 D - User Local 是 2025-03-10 10:30:00.000 ?

D - User Local 在数据库中存储的时间是 2025-03-10 06:30:00.000 (UTC 时区),

当这个时间转换到用户 Test06 所在的时区:“(GMT+04:00) Baku”,

时间就变成了 2025-03-10 10:30:00.000( UTC + 4),

所以用户 Test06 在表单上看到的 D - User Local 是 2025-03-10 10:30:00.000

(4)使用 Client API 获取

a. 获取 D - User LocalDT - User Local

名称 字段名称 格式 时区调整 GMT+08:00 时区下填写
D - User Local gdh_d_userlocal 仅日期 用户当地时间 2025-03-10
DT - User Local gdh_dt_userlocal 日期和时间 用户当地时间 2025-03-10 14:30
// 'Sun, 09 Mar 2025 16:00:00 GMT'
Xrm.Page.getAttribute("gdh_d_userlocal").getValue().toUTCString();

// 'Sun, 09 Mar 2025 16:00:00 GMT'
Xrm.Page.getAttribute("gdh_d_userlocal").getValue().toUTCString();

b.对于 “时区无关”,返回的是浏览器的时区

获取 D-TZ independentDT-TZ independent

名称 字段名称 格式 时区调整
D-TZ independent gdh_d_tz_independent 仅日期 时区无关
DT-TZ independent gdh_dt_tz_independent 日期和时间 时区无关
// 'Wed Mar 12 2025 00:00:00 GMT+0800 (中国标准时间)'
Xrm.Page.getAttribute("gdh_d_tz_independent").getValue().toString();

// 'Mon Mar 24 2025 09:30:00 GMT+0800 (中国标准时间)'
Xrm.Page.getAttribute("gdh_dt_tz_independent").getValue().toString();

💡JavaScript 日期值受浏览器的时区(来自设备操作系统设置)的影响,所以需要注意:

  1. 对于 “用户当地时间” 的字段,Client API 获取到的结果为 “UTC” 值,应使用 Date.getUTCDate()Date.getUTCHours(),而不是 Date.getDate()
  2. 如果要获取用户看到的时间,应用 getTimeZoneOffsetMinutes。 不要使用 Date.getDate()Date.getHours() 等,因为它们显示的是浏览器时区
  3. 对于 “时区无关”、“仅限日期” 的字段,应该使用 Date.getDate()Date.getHours() 等,不要使用 Date.getUTCDate()Date.getUTCHours(),因为本身就不需要进行时区的转换

(5)使用 Web API 获取

(GMT+08:00) Beijing, Chongqing, Hong Kong, Urumqi 时区下,对字段进行赋值,并通过 Web API 获取:

字段 填写值 数据库存储的值 通过 Web API 获取得出
D-User Local 2025-03-10 2025-03-09 16:00:00.000 2025-03-09T16:00:00Z
D-TZ independent 2025-03-12 2025-03-12 00:00:00.000 2025-03-12T00:00:00Z
D-D 2025-03-14 2025-03-14 00:00:00.000 2025-03-14
DT-User Local 2025-03-10 14:30 2025-03-10 06:30:00.000 2025-03-10T06:30:00Z
DT-TZ independent 2025-03-24 09:30 2025-03-24 09:30:00.000 2025-03-24T09:30:00Z

可以看出,通过 Web API 获取的原始值,和数据库存储的一样。


❓ 关于 T 与 Z

“2025-03-10T06:30:00Z” 中的 “T”和 “Z” 分别是什么意思?

  • T 是 ISO 8601 日期时间格式中的一个特殊字符,用于分隔日期和时间部分, 例如 2025-03-10T14:30:00 表示 2025 年 3 月 10 日 14 时 30 分 0 秒
  • Z 是 ISO 8601 日期时间格式中的一个特殊字符,代表 “Zulu time” 或者说 UTC (Coordinated Universal Time) 时区。它表示该时间是以 UTC 时区表示的,没有时区偏移量。例如 2025-03-10T14:30:00Z 表示 2025 年 3 月 10 日 14 时 30 分 0 秒 (UTC 时区)。
var req = new XMLHttpRequest();
req.open(
  "GET",
  Xrm.Page.context.getClientUrl() +
    "/api/data/v9.1/gdh_invoices(98F83878-1E35-EF11-8409-0017FA0671FA)?$select=gdh_d_d,gdh_d_tz_independent,gdh_d_userlocal,gdh_dt_tz_independent,gdh_dt_userlocal",
  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();

查询运算符不支持?

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

  • X 分钟以前
  • X 小时以前
  • 过去 X 小时
  • 接下来 X 小时

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

<condition attribute="gdh_d_d" operator="last-x-hours" value="5"/>

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

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

出现异常:2147779605The operator is not valid or it is not supported.

如何设置用户的时区?

右上角的设置按钮 –> 个性化设置 –> General Tab –> 选择时区

设置用户的时区

参考

  1. 使用 Power Apps 解决方案资源管理器创建和编辑 Microsoft Dataverse 的列
  2. Column Data types

如果本文对你有所帮助,可以请我喝杯咖啡

(完)