当前时间函数的本地、固定和 UTC 变体

在 Excel 和 Power BI 等工具中使用 Power Query 时,正确处理日期和时间值至关重要,尤其是在数据转换依赖于当前时间时。 Power Query 提供各种功能来检索当前日期和时间:

本文探讨这些函数之间的区别,并阐明何时和为什么使用每个函数。 此外,它还突出显示了一个关键但经常被忽视的细节。 即使使用标记为“Local”的函数,Power Query Online 也始终返回 UTC 时间。了解这些细微差别有助于避免意外结果,尤其是在生成时间敏感报表或自动执行 Power BI 服务或 Power Apps 等应用中的数据更新时。

函数之间的差异

每个当前时间函数都有重要的差异。 这些函数因时区意识、波动性(在同一查询中多次调用值时是否更改)以及它们在不同环境中的行为方式(桌面与联机)而异。 下表包含每个函数的明细。

功能 退货 波动 桌面行为 联机行为 典型用例
DateTime.LocalNow 用于表示当前本地时间的datetime 动态 —— 在查询评估期间,每次调用时返回一个新值。 返回本地计算机时间 返回 UTC 时间 快速的本地时间戳,无需时区上下文
DateTimeZone.LocalNow 一个 datetimezone 值,表示具有时区偏移量的当前本地时间 动态 —— 在查询评估期间,每次调用时返回一个新值。 返回带偏移量的本地时间 返回带时区偏移的 +00:00 UTC 时间 具有时区感知的本地时间
DateTime.FixedLocalNow 一个 datetime 值,该值表示在查询评估期间首次调用时的本地时间 已修复 - 在整个单个查询计算中返回相同的值 首次调用时捕获本地时间 捕获第一次调用该功能时的 UTC 时间 不带时区的本地时间快照
DateTimeZone.FixedLocalNow 一个 datetimezone 值,表示在查询评估期间首次调用时具有偏移量的本地时间 已修复 - 在整个单个查询计算中返回相同的值 在第一次调用时捕获具有偏移量的本地时间 第一次调用时记录具有 +00:00 偏移的 UTC 时间 具有时区的本地时间快照
DateTimeZone.UtcNow 表示datetimezone UTC 当前时间的值 动态 —— 在查询评估期间,每次调用时返回一个新值。 返回当前 UTC 时间 返回当前 UTC 时间 动态场景的一致 UTC 时间戳
DateTimeZone.FixedUtcNow 一个 datetimezone 值,表示在查询评估期间首次调用的 UTC 时间 已修复 - 在整个单个查询计算中返回相同的值 捕获第一次调用该功能时的 UTC 时间 捕获第一次调用该功能时的 UTC 时间 修正了用于日志记录或审核的 UTC 时间戳

在 Power Query M 中,在本地时间和基于 UTC 的日期和时间函数之间进行选择是影响查询的一致性、准确性和可移植性的关键设计决策。 当您的逻辑依赖于本地系统时间时,函数DateTime.LocalNowDateTime.FixedLocalNow非常有用,例如用于筛选“今天”发生的记录或为面向用户的报告生成时间戳。 这些函数反映执行查询的环境的时区,使其适用于本地上下文定义良好的 Power Query Desktop 方案。

但是,在分布式或基于云的环境中(如 Power Query Online)中,这些相同的函数返回 UTC 时间,而不是用户的实际本地时间。 如果逻辑假定本地时间上下文,这种差异可能会导致细微的不一致。 相反,DateTimeZone.UtcNowDateTimeZone.FixedUtcNow提供了一个时区中性的参考点,该参考点在各种环境中保持一致,并且不受夏令时或区域设置的影响。 对于涉及数据集成、日志记录、审核或任何必须在不同查询位置和时间表现一致的逻辑而言,这些基于UTC的函数是首选。

LocalNow 和 FixedLocalNow 函数之间的差异

Power Query M 提供四个函数用于检索当前本地时间:

  • DateTime.LocalNow 每次计算表达式时返回当前本地 datetime 值。
  • DateTime.FixedLocalNow 在每次查询评估中返回一次本地 datetime,用于充当快照。
  • DateTimeZone.LocalNow 每次计算表达式时返回当前本地 datetimezone 值。
  • DateTimeZone.FixedLocalNow 每个查询评估返回本地 datetimezone 一次,充当快照

为了演示差异,以下示例生成包含多行的表。 每行通过使用延迟来捕获一个DateTime.LocalNow新值,以确保时间戳不同,而每个捕获的DateTime.FixedLocalNow值在所有行中保持不变。

注释

本文中示例输出中的所有日期和时间取决于函数的运行时间。 输出中显示的日期和时间仅用于演示目的。

let
    // Create a table with LocalNow and FixedLocalNow columns 
    TableWithTimes = Table.FromList(
        {1..5},
        each {
            _,
            Function.InvokeAfter(() => DateTime.LocalNow(), #duration(0, 0, 0, 0.2)),
            Function.InvokeAfter(() => DateTime.FixedLocalNow(), #duration(0, 0, 0, 0.2))
        },
        {"Index", "LocalNow", "FixedLocalNow"}
    ),

    // Format both datetime columns
    FormatLocalNow = Table.TransformColumns(TableWithTimes, 
        {{"LocalNow", each DateTime.ToText(_, "yyyy-MM-ddThh:mm:ss.fff")}}),
    FormatFixedNow = Table.TransformColumns(FormatLocalNow, 
        {{"FixedLocalNow", each DateTime.ToText(_, "yyyy-MM-ddThh:mm:ss.fff")}}),

    // Change the table types
    FinalTable =  Table.TransformColumnTypes(FormatFixedNow, {{"Index", Int64.Type}, 
        {"LocalNow", type text}, {"FixedLocalNow", type text}})

in
    FinalTable

此示例的输出为:

使用 DateTime.LocalNow 动态日期和时间以及 DateTime.FixedLocalNow 固定日期和时间创建的表的屏幕截图。

如果查看输出,你可能会注意到,即使 DateTime.LocalNow 函数首先出现在代码中,DateTime.FixedLocalNow 返回的值显示的时间比 DateTime.LocalTime 的时间更早。 即使 DateTime.LocalNow 表构造中首先列出,Power Query M 中的计算顺序也不保证遵循表中字段的顺序。 相反,Power Query 使用惰性评估模型。 使用此模型意味着仅在需要时评估字段,并且引擎确定计算顺序,而不是代码中的顺序。 在这种情况下,首先计算DateTime.FixedLocalNow函数,因此第一次返回此函数的时间发生在DateTime.LocalNow的第一次返回时间之前。

以下示例演示如何使用 DateTimeZone.LocalNowDateTimeZone.FixedLocalNow生成类似的结果。

let
    // Create a table with LocalNow and FixedLocalNow columns 
    TableWithTimes = Table.FromList(
        {1..5},
        each {
            _,
            Function.InvokeAfter(() => DateTimeZone.LocalNow(), #duration(0, 0, 0, 0.2)),
            Function.InvokeAfter(() => DateTimeZone.FixedLocalNow(), #duration(0, 0, 0, 0.2))
        },
        {"Index", "LocalNow", "FixedLocalNow"}
    ),

    // Format both datetimezone columns
    FormatLocalNow = Table.TransformColumns(TableWithTimes, 
        {{"LocalNow", each DateTimeZone.ToText(_, "yyyy-MM-ddThh:mm:ss.fff:zzz")}}),
    FormatFixedNow = Table.TransformColumns(FormatLocalNow, 
        {{"FixedLocalNow", each DateTimeZone.ToText(_, "yyyy-MM-ddThh:mm:ss.fff:zzz")}}),

    //  Change the table types
    FinalTable =  Table.TransformColumnTypes(FormatFixedNow, 
        {{"Index", Int64.Type}, {"LocalNow", type text}, {"FixedLocalNow", type text}})
in
    FinalTable

Power Query Desktop 中此示例的输出为:

使用 DateTimeZone.LocalNow 动态日期和时间以及 DateTimeZone.FixedLocalNow 固定日期和时间创建的表的屏幕截图。

注释

如果在 Power Query Online 中运行此示例,则返回的时间始终为 UTC 时间,返回值的时区部分始终 +00:00为 。

UtcNow 和 FixedUtcNow 函数之间的差异

Power Query M 提供两个用于检索当前 UTC 时间的函数:

  • DateTimeZone.UtcNow 每次计算表达式时返回当前 UTC datetimezone
  • DateTimeZone.FixedUtcNow 在每次查询评估中返回一次本地 datetimezone,用于充当快照。

这两个函数之间的差异与LocalNowFixedLocalNow函数相似。 但是,无论函数是在 Power Query Desktop 还是 Power Query Online 中运行,返回值始终作为 UTC 时间返回。 以下示例演示了这两个函数之间的差异。

let
    // Create a table with UtcNow and FixedUtcNow columns 
    TableWithTimes = Table.FromList(
        {1..5},
        each {
            _,
            Function.InvokeAfter(() => DateTimeZone.UtcNow(), #duration(0, 0, 0, 0.2)),
            Function.InvokeAfter(() => DateTimeZone.FixedUtcNow(), #duration(0, 0, 0, 0.2))
        },
        {"Index", "UtcNow", "FixedUtcNow"}
    ),

    // Format both datetimezone columns
    FormatLocalNow = Table.TransformColumns(TableWithTimes, 
        {{"UtcNow", each DateTimeZone.ToText(_, "yyyy-MM-ddThh:mm:ss.fff:zzz")}}),
    FormatFixedNow = Table.TransformColumns(FormatLocalNow, 
        {{"FixedUtcNow", each DateTimeZone.ToText(_, "yyyy-MM-ddThh:mm:ss.fff:zzz")}}),

    //  Change the table types
    FinalTable =  Table.TransformColumnTypes(FormatFixedNow, 
        {{"Index", Int64.Type}, {"UtcNow", type text}, {"FixedUtcNow", type text}})
in
    FinalTable

Power Query Desktop 和 Power Query Online 中此示例的输出为:

使用 DateTimeZone.UtcNow 动态日期和时间以及 DateTimeZone.FixedUtcNow 固定日期和时间创建的表的屏幕截图。

对其他函数的影响

其他依赖于当前日期和时间的 Power Query M 函数也可能受到 Power Query Desktop 或 Power Query Online 上返回本地时间的方式的影响。 例如,如果使用函数将 DateTimeZone.ToLocal UTC 时间转换为本地时间,它仍返回 Power Query Online 上的 UTC 时间。

另一个示例是可以使用当前系统时间作为参数的任何函数。 这些函数包括Date.MonthDate.DayOfYearDateTime.IsInCurrentYear 或任何其他DateTimeZone.ZoneHours可计算当前日期和时间的函数。

在所有这些函数中,如果逻辑取决于某个值是否在当前日期、小时、月或年份内,则结果在环境之间可能有所不同。 如果查询在边界附近运行(例如,就在午夜之前或午夜之后、新月的开始或新年)时,环境之间的这些差异尤其明显。 如果不同环境之间的一致性至关重要,请使用 DateTimeZone.UtcNowDateTimeZone.FixedUtcNow 函数检索日期和时间。

最佳做法和建议

在 Power Query 中选择正确的时间函数取决于特定的用例、运行查询的环境(桌面与联机),以及是否需要动态时间戳或固定时间戳。 下面是一些最佳做法,可帮助指导你的决策:

  • 明确时区:当时区上下文重要时,请使用 DateTimeZone 函数而不是 DateTime 函数。 使用 DateTimeZone.UtcNowDateTimeZone.FixedUtcNow 实现环境之间的一致性,尤其是在基于云的解决方案(如 Power BI 服务)中。
  • 对可重复的结果使用固定函数:如果希望时间戳在查询评估中保持常量,请使用固定变体(例如 DateTimeZone.FixedUtcNow)。 此方法特别适用于记录、审核或捕获数据摄入的时间。
  • 避免在 Power Query Online 中使用本地函数:像DateTime.LocalNowDateTimeZone.LocalNow这样的函数会在基于云的解决方案中(例如 Power BI 服务)返回 UTC 时间,可能会导致关于时间混淆或错误假设。 如果需要服务中的实际本地时间,请考虑使用已知偏移量手动调整 UTC(尽管此调整可能很脆弱,例如,由于夏令时或区域设置)。
  • 在桌面和联机环境中进行测试:如果逻辑依赖于当前时间,请始终在 Power Query Desktop 和 Power Query Online 中测试查询。 此测试有助于尽早捕获差异,尤其是针对计划的刷新方案。
  • 记录时间逻辑:明确注释或记录使用特定时间函数的原因,尤其是在使用时区处理的解决方法时。 此信息可帮助未来的协作者了解逻辑背后的意图。
  • 将 UTC 用于计划工作流:对于计划的刷新或自动化管道,UTC 是最安全且最可预测的选择。 它避免了夏令时或区域时区班次导致的歧义。
  • 根据需要缓存时间值:如果需要跨查询中的多个步骤使用相同的时间戳,请使用固定函数将其分配给查询顶部的变量。 此变量可确保整个转换逻辑的一致性。