前言

本文主要记录在 Dynamics CRM 中使用日期和时间字段的基础知识。

基本概念

(1)Dataverse 以 UTC 时区存储所有日期和时间值。 当应用显示值或处理用户输入的值时,Dataverse 和模型驱动应用可以使用 “格式” 选项根据用户的时区进行调整。

(2)在实体新建 日期和时间(Date and Time) 字段时,可以选择以下 “格式”

格式描述
仅日期“仅日期” 显示的日期和时间值。 时间值在系统中存储为 12:00 AM (00:00:00)
日期和时间日期和时间值
选择日期和时间字段的 “格式”

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

(3)在实体新建 日期和时间(Date and Time) 字段时,在 “高级选项” 中可以选择以下 “时区调整”。此外,“时区调整” 的可选选项又根据 “格式” 变化,具体如下:

  • 格式 选择 仅日期 时,“时区调整” 可以选择:
时区调整描述
用户当地时间默认值。根据用户的时区调整值
时区无关显示无时区转换的值
仅日期无时区转换。 与时区无关不同,不存储时间部分(时间值在系统中存储为 12:00 AM (00:00:00))
  • 格式 选择 日期和时间 时,“时区调整” 可以选择:
时区调整描述
用户当地时间默认值。根据用户的时区调整值
时区无关显示无时区转换的值

(4)新建后的 “日期和时间” 字段,“格式” 和 “时区调整” 仍然可以在解决方案中修改。

“格式”和“时区调整”可以再更改吗?

已创建 的 “日期和时间” 字段,它的 “格式” 和 “时区调整” 是否还可以修改?经验证,结果如下:

“格式” & “时区调整”“格式”“时区调整”
仅日期 - 用户当地时间可改可改
仅日期 - 时区无关可改不可改
仅日期 - 仅日期不可改不可改
日期和时间 - 用户当地时间可改可改
日期和时间 - 时区无关可改不可改

“时区调整”该如何选?

“时区调整” 选项描述
时区无关当不需要 “时区” 信息时,例如酒店入住登记时间、发票付款时间等,使用该选项,选择该选项后,所有时区的用户将看到相同的日期和时间值
仅限日期不关注一天的 “时间” 或 “时区” 时,例如生日或纪念日等,使用该选项,选择该选项后,所有时区的用户将看到完全相同的日期值。
笔记
  1. “仅日期-时区无关” 和 “仅日期-仅日期” 是一样的,如果不确定 “日期和时间” 字段未来是否需要时间部分(时分秒),应该选择 “仅日期-时区无关”

  2. 因为系统默认的是 “用户当地时间” ,如果我们需要选择 “仅日期” 或是 “时区无关” 时,需要谨慎些(起码明确需求如此),否则不同时区的用户会因为 ±8 小时,导致看到不同的日期

小实验

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

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

(1)新建字段

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

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

5 个 “日期和时间” 字段

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

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

字段填写值
D - User Local2025-03-10
D - TZ independent2025-03-12
D - D2025-03-14
DT - User Local2025-03-10 14:30
DT - TZ independent2025-03-24 09:30
对日期和时间字段进行赋值

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

(3)在数据库查看数据

在数据库查看数据

在数据库查看数据

结果如下:

字段填写值数据库存储的值
D - User Local2025-03-102025-03-09 16:00:00.000
D - TZ independent2025-03-122025-03-12 00:00:00.000
D - D2025-03-142025-03-14 00:00:00.000
DT - User Local2025-03-10 14:302025-03-10 06:30:00.000
DT - TZ independent2025-03-24 09:302025-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 同理。

Sql 代码
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 查看数据

为什么 用户 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 Localgdh_d_userlocal仅日期用户当地时间2025-03-10
DT - User Localgdh_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 independentgdh_d_tz_independent仅日期时区无关
DT - TZ independentgdh_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 Local2025-03-102025-03-09 16:00:00.0002025-03-09T16:00:00Z
D - TZ independent2025-03-122025-03-12 00:00:00.0002025-03-12T00:00:00Z
D - D2025-03-142025-03-14 00:00:00.0002025-03-14
DT - User Local2025-03-10 14:302025-03-10 06:30:00.0002025-03-10T06:30:00Z
DT - TZ independent2025-03-24 09:302025-03-24 09:30:00.0002025-03-24T09:30:00Z

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

笔记

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

  • T

    • “T” 是 ISO 8601 日期时间格式中的一个特殊字符,用于分隔日期和时间部分
    • 例如 2025-03-10T14:30:00 表示 2025 年 3 月 10 日 14 时 30 分 0 秒
  • Z

    • “Z” 是 ISO 8601 日期时间格式中的一个特殊字符,代表 “Zulu time” 或者说 UTC (Coordinated Universal Time) 时区。
    • 它表示该时间是以 UTC 时区表示的,没有时区偏移量。
    • 例如 2025-03-10T14:30:00Z 表示 2025 年 3 月 10 日 14 时 30 分 0 秒 (UTC 时区)。
Web API 代码
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”列设置筛选条件

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

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

出现异常: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