在 Excel 和 Power BI 等工具中使用 Power Query 时,正确处理日期和时间值至关重要,尤其是在数据转换依赖于当前时间时。 Power Query 提供各种功能来检索当前日期和时间:
- DateTime.LocalNow
- DateTimeZone.LocalNow
- DateTime.FixedLocalNow
- DateTimeZone.FixedLocalNow
- DateTimeZone.UtcNow
- DateTimeZone.FixedUtcNow。
本文探讨这些函数之间的区别,并阐明何时和为什么使用每个函数。 此外,它还突出显示了一个关键但经常被忽视的细节。 即使使用标记为“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.LocalNow和DateTime.FixedLocalNow非常有用,例如用于筛选“今天”发生的记录或为面向用户的报告生成时间戳。 这些函数反映执行查询的环境的时区,使其适用于本地上下文定义良好的 Power Query Desktop 方案。
但是,在分布式或基于云的环境中(如 Power Query Online)中,这些相同的函数返回 UTC 时间,而不是用户的实际本地时间。 如果逻辑假定本地时间上下文,这种差异可能会导致细微的不一致。 相反,DateTimeZone.UtcNow和DateTimeZone.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.LocalTime 的时间更早。 即使 DateTime.LocalNow 表构造中首先列出,Power Query M 中的计算顺序也不保证遵循表中字段的顺序。 相反,Power Query 使用惰性评估模型。 使用此模型意味着仅在需要时评估字段,并且引擎确定计算顺序,而不是代码中的顺序。 在这种情况下,首先计算DateTime.FixedLocalNow函数,因此第一次返回此函数的时间发生在DateTime.LocalNow的第一次返回时间之前。
以下示例演示如何使用 DateTimeZone.LocalNow 和 DateTimeZone.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 中此示例的输出为:
注释
如果在 Power Query Online 中运行此示例,则返回的时间始终为 UTC 时间,返回值的时区部分始终 +00:00为 。
UtcNow 和 FixedUtcNow 函数之间的差异
Power Query M 提供两个用于检索当前 UTC 时间的函数:
-
DateTimeZone.UtcNow每次计算表达式时返回当前 UTCdatetimezone。 -
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 中此示例的输出为:
对其他函数的影响
其他依赖于当前日期和时间的 Power Query M 函数也可能受到 Power Query Desktop 或 Power Query Online 上返回本地时间的方式的影响。 例如,如果使用函数将 DateTimeZone.ToLocal UTC 时间转换为本地时间,它仍返回 Power Query Online 上的 UTC 时间。
另一个示例是可以使用当前系统时间作为参数的任何函数。 这些函数包括Date.Month、Date.DayOfYearDateTime.IsInCurrentYear 或任何其他DateTimeZone.ZoneHours可计算当前日期和时间的函数。
在所有这些函数中,如果逻辑取决于某个值是否在当前日期、小时、月或年份内,则结果在环境之间可能有所不同。 如果查询在边界附近运行(例如,就在午夜之前或午夜之后、新月的开始或新年)时,环境之间的这些差异尤其明显。 如果不同环境之间的一致性至关重要,请使用 DateTimeZone.UtcNow 或 DateTimeZone.FixedUtcNow 函数检索日期和时间。
最佳做法和建议
在 Power Query 中选择正确的时间函数取决于特定的用例、运行查询的环境(桌面与联机),以及是否需要动态时间戳或固定时间戳。 下面是一些最佳做法,可帮助指导你的决策:
-
明确时区:当时区上下文重要时,请使用 DateTimeZone 函数而不是 DateTime 函数。 使用
DateTimeZone.UtcNow或DateTimeZone.FixedUtcNow实现环境之间的一致性,尤其是在基于云的解决方案(如 Power BI 服务)中。 -
对可重复的结果使用固定函数:如果希望时间戳在查询评估中保持常量,请使用固定变体(例如
DateTimeZone.FixedUtcNow)。 此方法特别适用于记录、审核或捕获数据摄入的时间。 -
避免在 Power Query Online 中使用本地函数:像
DateTime.LocalNow和DateTimeZone.LocalNow这样的函数会在基于云的解决方案中(例如 Power BI 服务)返回 UTC 时间,可能会导致关于时间混淆或错误假设。 如果需要服务中的实际本地时间,请考虑使用已知偏移量手动调整 UTC(尽管此调整可能很脆弱,例如,由于夏令时或区域设置)。 - 在桌面和联机环境中进行测试:如果逻辑依赖于当前时间,请始终在 Power Query Desktop 和 Power Query Online 中测试查询。 此测试有助于尽早捕获差异,尤其是针对计划的刷新方案。
- 记录时间逻辑:明确注释或记录使用特定时间函数的原因,尤其是在使用时区处理的解决方法时。 此信息可帮助未来的协作者了解逻辑背后的意图。
- 将 UTC 用于计划工作流:对于计划的刷新或自动化管道,UTC 是最安全且最可预测的选择。 它避免了夏令时或区域时区班次导致的歧义。
- 根据需要缓存时间值:如果需要跨查询中的多个步骤使用相同的时间戳,请使用固定函数将其分配给查询顶部的变量。 此变量可确保整个转换逻辑的一致性。