【问题标题】:Why UtcDateTime function is not adding the offset to the UTC date?为什么 UtcDateTime 函数没有将偏移量添加到 UTC 日期?
【发布时间】:2018-10-18 01:04:43
【问题描述】:

对于即时日期时间跟踪,我使用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 现在是 2018 年 5 月 8 日上午 6:43:37 +00:00,UTC 日期洛杉矶是 5/8/18 6:43:37 AM -07:00 UtcDateTime LA 是 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);

在 DST 为 -08:00:00 之前和 DST 之后的当前偏移量为 -07:00:00 -07:00:00

【问题讨论】:

  • 输出看起来正确。 2018 年 5 月 8 日上午 6:43:37 UTC 在任何地方都是 2018 年 5 月 8 日上午 6:43:37 UTC,即使在洛杉矶也是如此。当然,本地时区会与 UTC 有偏差
  • 所以输出的 UtcDateTime LA 部分不应该回到 7 小时?
  • 正确。 UTC 的历史基准是伦敦时间(没有夏令时)。所以在世界各地都是一样的。它从来没有时区问题,因为它完全忽略了它们。这就是我们喜欢 IT 的原因 ;-)。只有当地时间有偏移量。
  • 所以基本上我在这里做的是错误的,我应该将 DateTime.Now 与偏移量一起存储吗?
  • 那我这里怎么转换呢?我的意思是在上面的例子中显示落后 7 小时的时间?

标签: c# .net datetime timezone datetimeoffset


【解决方案1】:

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

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

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

  • new DateTimeOffset(构造函数)如果种类和偏移量冲突,如果您提供偏移量,则会出错。

  • 当您将 DateTime 分配给 DateTimeOffset 时,隐式转换正在评估类型。

  • 当您从DateTimeOffset 调用.DateTime 时,类型将始终Unspecified - 无论偏移量如何。

如果您考虑到所有这些,您会意识到在致电GetUtcOffset 之前您需要自己检查一下。如果它是 not 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

【讨论】:

  • 非常感谢您的详尽回复。 TimeZoneInfo.ConvertTime 是我最好的选择
猜你喜欢
  • 1970-01-01
  • 2012-08-02
  • 1970-01-01
  • 1970-01-01
  • 2014-02-04
  • 1970-01-01
  • 1970-01-01
  • 2017-08-01
相关资源
最近更新 更多