如果你有世界各地的用户和办公室,则必须在多个时区中正确表示日期和时间值。 使用 DateTimeAttributeMetadata(DateTimeAttributeMetadata 实体类型或 DateTimeAttributeMetadata 类)来定义和管理 Microsoft Dataverse 中DateTime类型的列。 使用属性 DateTimeBehavior (对于用于 .NET 的 SDK,请参阅 DateTimeBehavior 属性)定义是存储日期和时间值与时区信息还是不使用时区信息。 使用 DateTimeAttributeMetadata.Format 属性 指定这些列的显示格式。
还可以使用 Dataverse 中的自定义区域来定义日期和时间列的行为和格式。 有关详细信息,请参阅 “日期和时间”列的行为和格式。
注释
Dataverse 中的所有日期和时间列都支持早在上午 1/1/1753 12:00 的值。
如果“仅日期”或“日期时间”字段位于解决方案中,则仅当作为发布者时,才能更改现有托管字段的行为。 若要对这些字段进行更改,必须升级添加“仅日期”或“日期时间”列的解决方案。 有关详细信息,请参阅 升级或更新解决方案。
指定日期和时间列的功能
DateTimeBehavior 使用 (DateTimeBehavior 复杂类型或 DateTimeBehavior 类)指定 DateTimeAttributeMetadata 实体类型的值。
DateTimeBehavior 属性将其完全禁用。 以下是 DateTimeBehavior 的成员。 每个成员返回一个字符串,其值与成员名称相同:
| 成员名称和值 | Description |
|---|---|
UserLocal |
- 在系统中将日期和时间值存储为 UTC 值。 - 检索操作返回 UTC 值。 - 更新作将 UTC 值转换为当前用户的时区值,然后按原样或等效的 UTC 值存储更新值,具体取决于为更新指定的值的类型(DateTimeKind)。 如果指定的值为 UTC 类型,则它按原样存储。 否则,将存储 UTC 等效值。 - 获取格式化值时,根据用户的时区和区域设置,将值从 UTC 转换为用户的当前时区。 对于 Web API,该列会以 DateTimeOffset 形式公开。 - 此行为用于类似 CreatedOn 且 ModifiedOn无法更改的系统列。 将此行为用于想要将日期和时间值与时区信息一起存储到的自定义列。 |
DateOnly |
- 存储没有时间值的实际日期值。 - 检索格式化值将显示日期值。 - 对于 Web API,该列以日期的形式公开。 - 将此行为用于存储生日和周年纪念日(其中不需要时间信息)的自定义列。 |
TimeZoneIndependent |
- 将实际日期和时间值存储在系统中,而不考虑用户时区。 - 对于检索和更新操作,不执行时区转换,无论用户时区如何,都会在系统中分别返回和更新实际日期和时间值。 - 检索格式化值根据当前用户的时区和区域设置指定的格式显示日期和时间值(没有任何时区转换)。 对于 Web API,该列将被公开为 DateTimeOffset。 - 将此行为用于存储信息(例如酒店签入和签出时间)的列。 |
以下示例代码演示如何为新的日期时间列设置 UserLocal 行为:
/// <summary>
/// Create a new DateTime column for the Account table with UserLocal behavior
/// </summary>
/// <param name="service">Authenticated IOrganizationService instance</param>
static void CreateUserLocalDateTimeColumn(IOrganizationService service) {
int _languageCode = 1033; //English
DateTimeAttributeMetadata dtAttribute = new()
{
SchemaName = "new_SampleDateTimeAttribute",
DisplayName = new Label("Sample Date Time Attribute", _languageCode),
RequiredLevel = new AttributeRequiredLevelManagedProperty(AttributeRequiredLevel.None),
Description = new Label("Created by SDK Sample", _languageCode),
DateTimeBehavior = DateTimeBehavior.UserLocal,
Format = Microsoft.Xrm.Sdk.Metadata.DateTimeFormat.DateAndTime,
ImeMode = ImeMode.Disabled
};
CreateAttributeRequest request = new()
{
EntityName = Account.EntityLogicalName,
Attribute = dtAttribute
};
service.Execute(request);
}
在示例代码中,还可以通过直接指定字符串值来设置 DateTimeBehavior 属性的值:DateTimeBehavior = "UserLocal"
如果在创建日期和时间列时未指定该行为,则默认情况下会使用 UserLocal 该行为创建该列。
重要
- 创建一个行为设置为
DateOnly或TimeZoneIndependent的日期和时间列后,无法更改该列的行为。 有关详细信息,请参阅 更改 DateTime 列的行为。 - 在旧版本的 Dynamics 365 for Outlook 客户端脱机模式中编辑时,具有
DateOnly或TimeZoneIndependent行为的日期和时间列将被视为具有UserLocal行为。 存在此限制,因为客户端不了解新行为,并且不会以UserLocal不同的方式对待它们。 升级时不会将日期和时间列转换为新行为。 为了避免此限制,请在定制器采用新的功能之前,将所有 Dataverse 客户端升级到最新版本。 联机时,编辑具有新行为的列的数据可以正常工作。
指定日期和时间列的格式
使用Format属性来指定列的日期/时间显示格式,而不管系统如何存储它。 使用 DateTimeFormat 枚举(DateTimeFormat 枚举类型 或 DateTimeFormat 枚举)指定显示格式: DateAndTime 或 DateOnly。
如果将DateTimeAttributeMetadata.DateTimeBehavior属性设置为DateOnly,则无法将DateTimeAttributeMetadata.Format属性的值设置为DateAndTime。
DateOnly 行为不支持的日期和时间查询运算符
DateOnly 行为不支持与时间相关的查询运算符。 除了此处列出的特定于时间的查询运算符之外,所有其他查询运算符均受支持。
- X 分钟以前
- X 小时以前
- 过去 X 小时
- 接下来 X 小时
详细信息: Datetime 数据运算符
使用 OData API 提交用户本地日期和时间值
在 Microsoft Power Platform 中,当用户通过 UI 在用户特定的时区中提交日期和时间时,自动计算会将数据设置为正确的日期和时间。 它执行分析,以根据列和 UI 设置将提交的任何日期更改为相应的 UTC 值。 使用 Web API 提交日期和时间值时,计算不会发生,从而导致无法解释的数据展示。 例如,如果你位于太平洋时区,并且提交 2021 年 4 月 4 日 12:00,则会发生以下情况:
- 原文:2021/4/12:00 提交者位于太平洋时区。
- 通过 UI 提交并检索为用户本地时间: 2021/4/4 12:00
- 通过 API 提交并以用户本地时间检索:2021/4/4 04:00
通过 UI 提交
UI 将值设置为用户本地,列设置为用户本地。
- 原始值: 2021/4/12:00 太平洋时区。
- 计算为 UTC 并在 Dataverse 中存储的值: 2021/4/4 12:00 + 8:00 = 2021T20:00:00Z。 由于 PST 与 UTC 相差 -8:00,因此在存储的值上加上 +8。
- 太平洋时区的用户在 UI 中显示的值: 2021/4/4 12:00。 UI 将-8:00 UTC的偏移量计算应用于2021年4月4日T20:00:00Z,以确保值的正确性。
通过 API 提交
UI 将值设置为用户本地,列设置为用户本地。
- 原始值: 4/4/2021T12:00:00 或 4/4/2021T12:00:00Z——未提供偏移量或 UTC 指示符。 提交者位于太平洋时区。
- 计算为 UTC 并在 Dataverse 中存储的值: 从 OData API 提交时不进行 UI 计算,所以该值被存储为 2021-04-04T12:00:00Z。
- 太平洋时区的用户在 UI 中显示的值: 2021/4/4 4:00。 UI 对 Dataverse 中的值应用 -8:00 UTC 偏移量计算。
若要在对用户本地列使用 API 调用输入数据时防止此问题,请计算提交数据的用户的偏移量并应用偏移量。
使用前面的示例: 2021/4/2021 12:00 需要通过 API 提交为 4/4/2021T12:00:00-08:00。 原始时间和日期包括对当前用户时区的偏移量计算。 或者,提交者可以在提交之前执行计算,并提交 2021年4月4日T20:00:00Z。
如果选择包括偏移量计算,请不要包含 ZUTC 指示器,因为 Dataverse 不接受它。
更改日期和时间列的行为
如果在 Dataverse 实例中具有系统定制器角色,并且 DateTimeAttributeMetadata.CanChangeDateTimeBehavior 日期和时间列的托管属性设置为 True,则可以更新日期和时间列以更改其行为。
注意
在更改日期和时间列的行为之前,请查看该列的所有依赖项,例如业务规则、工作流以及计算列或汇总列,以确保更改行为时没有问题。 系统定制器可以使用托管属性限制修改现有日期和时间列 DateTimeAttributeMetadata.CanChangeDateTimeBehavior 的行为。
更改日期和时间列的行为后,至少要打开每个依赖于该日期和时间列的业务规则、工作流、计算列和汇总列记录,查看信息,并保存记录以确保使用最新的列行为和值。
更改计算列或汇总列的数据和时间行为后,打开计算列或汇总列定义编辑器,并保存列定义以确保该列在行为更改后仍然有效。 系统定制器可以通过在 Dataverse 的自定义区域中选择 字段类型 旁边的 编辑 来打开计算列或汇总列的列定义编辑器。 有关详细信息,请参阅 定义计算列以自动执行计算 和 定义聚合值的汇总列。
默认情况下,开箱即用表和自定义表的
CreatedOn和ModifiedOn列的行为设置为UserLocal,同时,DateTimeAttributeMetadata.CanChangeDateTimeBehavior托管属性设置为False,这意味着无法更改这些列的行为。 尽管用户可以更改自定义表中这些列的DateTimeAttributeMetadata.CanChangeDateTimeBehavior托管属性的值,但用户仍无法更改列的行为。对于新的自定义日期和时间列,托管
DateTimeAttributeMetadata.CanChangeDateTimeBehavior属性设置为True。 此设置意味着你可以将自定义日期和时间列的行为从UserLocal更改为DateOnly或TimeZoneIndependent;不允许进行其他行为转换。对于属于 Dataverse 组织的自定义日期和时间列,除非列或父表不可自定义,否则托管
DateTimeAttributeMetadata.CanChangeDateTimeBehavior属性将设置为True。注释
当您将列的
DateTimeAttributeMetadata.DateTimeBehavior属性从UserLocal更改为DateOnly时,请确保也将DateTimeAttributeMetadata.Format属性从DateAndTime更改为DateOnly。 否则,会发生异常。Dataverse 中的以下开箱即用的日期和时间列默认设置为
DateOnly,且托管属性DateTimeAttributeMetadata.CanChangeDateTimeBehavior设置为False,这意味着无法更改这些列的行为:日期和时间列 父表 anniversary联系人 birthdate联系人 duedateInvoice estimatedclosedate潜在顾客 actualclosedate机会 estimatedclosedate机会 finaldecisiondate机会 validfromdate产品 validtodate产品 closedon报价单 expireson报价单 这些列的行为被设置为
UserLocal,并且DateTimeAttributeMetadata.CanChangeDateTimeBehavior管理的属性被设置为True,你只能将这些列的行为更改为DateOnly。 不允许进行其他行为转换。
更新列的行为后,必须发布有关更改的自定义配置,以使其生效。 更新日期和时间列的行为可确保更改列行为 后 输入或更新的所有值都按照新行为存储在系统中。 此更改不会影响数据库中已存储的值,并且它们将继续存储为 UTC 值。 但是,使用 SDK 检索现有值或在 UI 中查看现有值时,根据列的新行为显示现有值。 例如,如果将帐户自定义列的行为从UserLocal更改为DateOnly,并使用 SDK 检索现有帐户记录,则日期和时间显示为<日期>,后跟时间为 12 AM (00:00:00)。 同样,对于从 UserLocal 到 TimeZoneIndependent 的行为变化,数据库中的实际值按原样显示,没有进行任何时区转换。
以下示例代码演示如何更新日期和时间列的行为:
/// <summary>
/// Update the behavior of a DateTime column
/// </summary>
/// <param name="service">Authenticated IOrganizationService instance</param>
static void UpdateBehaviorOfDateTimeColumn(IOrganizationService service) {
// Retrieve the attribute to update its behavior and format
RetrieveAttributeRequest retrieveColumnRequest = new()
{
EntityLogicalName = Account.EntityLogicalName,
LogicalName = "new_sampledatetimeattribute",
RetrieveAsIfPublished = false
};
// Execute the request
RetrieveAttributeResponse attributeResponse =
(RetrieveAttributeResponse)service.Execute(retrieveColumnRequest);
// Modify the values of the retrieved attribute
DateTimeAttributeMetadata retrievedAttributeMetadata =
(DateTimeAttributeMetadata)attributeResponse.AttributeMetadata;
retrievedAttributeMetadata.DateTimeBehavior = DateTimeBehavior.DateOnly;
retrievedAttributeMetadata.Format = Microsoft.Xrm.Sdk.Metadata.DateTimeFormat.DateOnly;
// Update the attribute with the modified value
UpdateAttributeRequest updateRequest = new()
{
Attribute = retrievedAttributeMetadata,
EntityName = Account.EntityLogicalName,
MergeLabels = false
};
service.Execute(updateRequest);
// Publish customizations to the account
PublishXmlRequest pxReq = new()
{
ParameterXml = "<importexportxml><entities><entity>account</entity></entities></importexportxml>"
};
service.Execute(pxReq);
}
转换数据库中的现有日期和时间值的行为。
当您更新日期和时间列以将其行为从 UserLocal 更改为 DateOnly 或 TimeZoneIndependent 时,数据库中现有的列值不会自动转换。 行为更改仅影响更改行为 后 在列中输入或更新的值。 系统中的现有日期和时间值保持 UTC。 通过 SDK 或 UI 检索这些值时,Dataverse 会根据新行为显示这些值,如上一部分所述。 对于行为从UserLocal更改为DateOnly的列,可以使用ConvertDateAndTimeBehavior消息将数据库中的现有 UTC 值转换为适当的DateOnly值,以避免出现任何数据异常。
使用此消息可以指定转换规则(如果使用 SDK for .NET,请参阅 ConvertDateAndTimeBehaviorRequest.ConversionRule 属性),以选择要用于将值从 UTC 转换为 DateOnly 的时区。 可以指定下列转换规则之一:
-
SpecificTimeZone:根据指定的 Dataverse 时区代码,将 UTC 值转换为 DateOnly 值。 在这种情况下,还需要为 ConvertDateAndTimeBehaviorRequest.TimeZoneCode 属性指定值。 -
CreatedByTimeZone:将 UTC 值转换为创建记录的用户在 UI 中看到的 DateOnly 值。 -
OwnerTimeZone:将 UTC 值转换为拥有记录的用户在 UI 中看到的 DateOnly 值。 -
LastUpdatedByTimeZone:将 UTC 值转换为用户在 UI 中看到的上次更新记录的 DateOnly 值。
使用 DateTimeBehaviorConversionRule 类 的四个成员之一指定 ConversionRule 属性的有效值。
注释
必须在 Dataverse 实例中具有系统管理员角色才能执行 ConvertDateAndTimeBehaviorRequest 类。
当您执行 ConvertDateAndTimeBehavior 时(如果您正在使用适用于 .NET 的 SDK,请参阅 ConvertDateAndTimeBehaviorRequest 消息),您会创建一个系统作业(异步操作)来运行转换请求。
ConvertDateAndTimeBehaviorResponse.JobId消息响应中的列显示转换请求创建的系统作业的 ID。 系统作业完成后,检查作业详细信息 (AsyncOperation.Message) 来查看转换详细信息或错误(如果有)。
注释
将多个列的转换分组为单个转换作业,并且一次只运行一个转换作业,以确保转换过程中没有冲突并达到最佳系统性能。
使用 ConvertDateAndTimeBehavior 消息时,请考虑以下几点:
- 避免在执行消息期间对 Dataverse 中的解决方案进行重大更改,例如导入解决方案或删除列或父表。 异常行为可能会发生。 但是,不会发生数据丢失。
- 由于执行消息,系统中完成的更新不会运行工作流和插件。
- 执行消息后在系统中完成的更新不会更改列的“上次修改时间”值。 但是,更新会被审核,以帮助管理员确定转换的时间以及列的原始值和更改后的值。
以下示例代码展示如何使用消息:
/// <summary>
/// Demonstrates use of the ConvertDateAndTimeBehavior message
/// </summary>
/// <param name="service">Authenticated IOrganizationService instance</param>
static void ConvertDateAndTimeBehavior(IOrganizationService service)
{
ConvertDateAndTimeBehaviorRequest request = new()
{
Attributes = new EntityAttributeCollection()
{
new KeyValuePair<string, StringCollection>("account", new StringCollection()
{ "new_sampledatetimeattribute" })
},
ConversionRule = DateTimeBehaviorConversionRule.SpecificTimeZone.Value,
TimeZoneCode = 190, // Time zone code for India Standard Time (IST) in Dataverse
AutoConvert = false // Conversion must be done using ConversionRule
};
// Execute the request
var response = (ConvertDateAndTimeBehaviorResponse)service.Execute(request);
Console.WriteLine($"Asynchronous Job ID: {response.JobId}");
}
Web 客户端处理时区转换的方式不同于统一接口
Web 客户端 在 2019 年已弃用。 如果要将 客户端脚本 迁移到其后续的统一接口,请注意两个客户端如何处理用户本地列的时区转换的差异。
在 Web 客户端中,服务器处理时区转换。 如果用户在 Web 客户端窗体中输入 2018-10-15 07:30 ,客户端 API Xrm.Page.getAttribute(<column name>).getValue() 将 2018-10-15 07:30返回。 在保存该值时,此值将发送到服务器,在服务器上进行时区调整。
在 Unified Client 中,客户端处理时区转换。 如果 UTC+8 时区的用户以统一客户端形式输入 2018-10-15 07:30 ,则客户端 API formContext.getAttribute(<column name>).getValue() 返回调整后的值 2018-10-14T23:30:00Z。 服务器接受值,并且不执行进一步的时区调整。
若要考虑这种差异,可以:
- 使用 Component Framework UserSettings.getTimeZoneOffsetMinutes 方法 或 Xrm.Utility.getGlobalContext().userSettings.getTimeZoneOffsetMinutes 方法 获取用户的时区偏移量,并修改脚本以考虑该偏移量。
- 将列行为从
UserLocal更改为TimeZoneIndependent,从而不会调整输入的时间。 只有在列不涉及时区时才能进行此更改。
另请参阅
“日期和时间”列的行为和格式
解决模型驱动应用中的日期和时间问题
列概述
ConvertDateAndTimeBehaviorRequest 类
DateTimeAttributeMetadata 类
博客:提交日期和时间数据的方式是否重要?