为什么 UtcDateTime 函数没有将偏移量添加到 UTC 日期?

Why UtcDateTime function is not adding the offset to the UTC date?

对于瞬时日期时间跟踪,我使用的是 DateTimeOffset 数据类型。以下函数将用户对应的TimeZone ID偏移量添加到DateTimeOffset

的UTC DateTime 属性

根据 documentationUtcDateTime 将对 DateTimeOffset 执行时区转换和类型转换。下面的代码没有。为什么转换没有发生?

添加TimeSpan偏移量的函数,

public static DateTimeOffset GetUtcDateTime (DateTime sourceDateTime, string timeZoneId) {
 TimeZoneInfo timeZone = TimeZoneInfo.FindSystemTimeZoneById (timeZoneId);
 TimeSpan offset = timeZone.GetUtcOffset (sourceDateTime);
 DateTimeOffset utcTime = new DateTimeOffset (sourceDateTime, offset);
 return utcTime;
 }

这里是我尝试转换的地方,

DateTimeOffset utcDate = (DateTime.UtcNow);
DateTime fromUtc = utcDate.DateTime;
DateTimeOffset UtcDate = StaticHandlers.GetUtcDateTime (fromUtc, "America/Los_Angeles");
Console.WriteLine ("UTC now is {0} and UTC Date LA is {1} and UtcDateTime LA is {2}", utcDate, UtcDate, utcDate.UtcDateTime);

输出是,

UTC now is 5/8/18 6:43:37 AM +00:00 and and UTC Date LA is 5/8/18 6:43:37 AM -07:00 UtcDateTime LA is 5/8/18 6:43:37 AM

更新,

我想保留 UTC 和用户偏移量以用于跟踪目的。 DST 在这种情况下很重要。下面的例子展示了我在说什么。

DateTime currentDateTime = DateTime.Now;
DateTime beforeDST_LA = new DateTime (2018, 3, 11, 0, 0, 0);
DateTime afterDST_LA = new DateTime (2018, 3, 12, 0, 0, 0);
TimeSpan offsetCurrent = tzi.GetUtcOffset (currentDateTime);
TimeSpan offsetBeforeDST = tzi.GetUtcOffset (beforeDST_LA);
TimeSpan offsetAfterDST = tzi.GetUtcOffset (afterDST_LA);
Console.WriteLine ("Current offset is {0} before DST is {1} and After DST is {2}", offsetCurrent, offsetBeforeDST, offsetAfterDST);

Current offset is -07:00:00 before DST is -08:00:00 and After DST is -07:00:00

首先,我不会调用您的函数 GetUtcDateTime,因为它不是那样做的。它试图在特定时间为特定时区获取 DateTimeOffset,因此将其命名为 GetDateTimeOffset.

您的代码中缺少的主要概念是 DateTime 具有 .Kind 属性,它设置了 DateTimeKind 值。您的代码中有几个地方考虑​​了这种类型:

  • GetUtcOffset 将在确定偏移量之前将 UtcLocal 类型转换为提供的区域。

  • 如果提供偏移量,
  • new DateTimeOffset(构造函数)将在类型和偏移量冲突时出错。

  • 当您将 DateTime 分配给 DateTimeOffset 时,隐式转换正在计算种类。

  • 当您从 DateTimeOffset 调用 .DateTime 时,种类将 始终Unspecified - 无论偏移.

如果考虑到所有这些,您会发现在调用 GetUtcOffset 之前需要自己检查类型。如果它是 而不是 Unspecified 那么您需要在获取偏移量之前将其转换为指定的时区。

public static DateTimeOffset GetDateTimeOffset(DateTime sourceDateTime, string timeZoneId)
{
    TimeZoneInfo timeZone = TimeZoneInfo.FindSystemTimeZoneById(timeZoneId);

    // here, is where you need to convert
    if (sourceDateTime.Kind != DateTimeKind.Unspecified)
        sourceDateTime = TimeZoneInfo.ConvertTime(sourceDateTime, timeZone);

    TimeSpan offset = timeZone.GetUtcOffset(sourceDateTime);
    return new DateTimeOffset(sourceDateTime, offset);
}

现在已经处理完了,转到下一组问题,也就是您所说的问题。

DateTimeOffset utcDate = (DateTime.UtcNow);
DateTime fromUtc = utcDate.DateTime;

在第 1 行中,从 DateTimeDateTimeOffset 的隐式转换将偏移量设置为 00:00 - 因为 DateTime.UtcNow 具有 .Kind == DateTimeKind.Utc.

在第 2 行中,调用 .DateTime 属性 设置 fromUtc.Kind == DateTimeKind.Unspecified。本质上,你已经剥离了那种。

所以不用这个,只需将 DateTime.UtcNow 直接传递给函数即可。这种类型将持续存在,并且一切正常 - 现在 Kind 已被识别并且转换在函数内部发生。

综上所述,如果您的原始值都是 DateTimeOffset(例如,DateTimeOffset.UtcNow),那么您根本不需要该功能。只需直接用 DateTimeOffset 调用 TimeZoneInfo.ConvertTime