问题

我们遇到了一个问题,一个开发人员创建了下面的代码,并且它可以在他的 DEV 环境中工作。但是,当它签入 QA 时,代码会与下面的错误消息断开:

myRecord.UTCStartTime = TimeZoneInfo.ConvertTimeToUtc(myRecord.StartTime, myTimeZone);

由于提供的 DateTime,无法完成转换 没有正确设置 Kind 属性。例如,当 Kind 属性是 DateTimekind。本地,源时区必须是 时区信息,本地信息。

在我的 DEV 环境中,上面的代码生成与 QA 服务器相同的错误。我应用了下面的修改来解决这个问题:

DateTime utcStart = DateTime.SpecifyKind(myRecord.StartTime, DateTimeKind.Unspecified);
myRecord.UTCStartTime = TimeZoneInfo.ConvertTimeToUtc(utcStart, myTimeZone);

为什么第一个代码示例在 Dev1的环境中工作,但在我的 DEV 环境和我们的 QA 服务器上中断?

49865 次浏览

这取决于 myRecord.StartTime是如何产生的。

  • 如果你从 DateTime.Now得到它,那么它将有一个 Local类型。
  • 如果你得到它从 DateTime.UtcNow那么它将有一个 Utc的类型。
  • 如果你得到它从 new DateTime(2013,5,1)那么它将有一个 Unspecified的类型。

这也取决于你从哪里得到的 myTimeZone。例如:

  • TimeZoneInfo.Local
  • TimeZoneInfo.Utc
  • TimeZoneInfo.FindSystemTimeZoneById("Central Standard Time")

TimeZoneInfo.ConvertTimeToUtc函数只有在区域与您提供的区域相匹配时才会运行。如果两者都是本地的,或者两者都是 UTC 的,那么它将工作。如果您给它一个特定的区域,那么类型应该是未指定的。这种行为是 记录在 MSDN 上

您可以很容易地一致地重现异常:

var tz = TimeZoneInfo.FindSystemTimeZoneById("Fiji Standard Time");
var utc = TimeZoneInfo.ConvertTimeToUtc(DateTime.Now, tz);

假设你不住在斐济,每次都会出错。你基本上是说,“转换我的本地时间,在一些其他区域,UTC”-这是没有意义的。

它可能在您的开发环境中工作,因为您正在为 myTimeZone测试的值恰好是开发人员的本地区域。

关于你的变化-当然,你可以强迫这种未指定,这改变了什么意思,你正在做的这样它有意义。但你确定这是你想要的吗?提前日期的 .Kind是多少?如果它还没有 Unspecified,那么它就带有某种意图。您可能应该回到这个数据的源,并确保它是您所期望的。

如果所有这些听起来疯狂、疯狂、沮丧和怪异,那是因为 DateTime对象散发着恶臭。下面是一些补充阅读材料:

你可以考虑改用 NodaTime,它的 API 可以防止你犯这些常见的错误。

在这个示例中,我已经将本地时区转换为未指定的类型,因此使用“ DateTime”可以很好地工作。SpecifyKind ()”方法

指定类型(utc,日期时间类型,未指定) ;

此方法创建一个新的 DateTime 对象,该对象具有与指定的 DateTime 相同的刻度数,但被指定为未指定类型的 DateTimeKind。

Public 静态 DateTime ConvertLocalDate (DateTime utc) {

        string id = ConfigurationManager.AppSettings["Timezone"].ToString();
TimeZoneInfo cstZone = TimeZoneInfo.FindSystemTimeZoneById(id);
utc = DateTime.SpecifyKind(utc,DateTimeKind.Unspecified);
DateTime cstTime = TimeZoneInfo.ConvertTimeFromUtc(utc, cstZone);
return cstTime;
}

我找到了一个非常简单的解决方案 Https://kiranpatils.wordpress.com/2011/01/09/the-conversion-could-not-be-completed-because-the-supplied-datetime-did-not-have-the-kind-property-set-correctly-for-example-when-the-kind-property-is-datetimekind-local-the-source-time-zone-must/

这似乎只有在使用日期时才会发生。现在。我像下面这样更新我的代码,现在它又可以工作了:)

DateTime currentTime = new DateTime (DateTime.Now. Ticks,DateTimekind. 未指定) ;

C #


public static DateTime IndianDateTime(DateTime currentTime)
{
DateTime cstTime = TimeZoneInfo.ConvertTimeBySystemTimeZoneId(currentTime, TimeZoneInfo.Local.Id, "India Standard Time");
return cstTime;
}

在 VB 中


Public Shared Function IndianDateTime(ByVal currentTime As DateTime) As DateTime
Dim cstTime As DateTime = TimeZoneInfo.ConvertTimeBySystemTimeZoneId(currentTime, TimeZoneInfo.Local.Id, "India Standard Time")
Return cstTime
End Function

基本上和之前的答案一样,但是要精简一下:

DateTime.NowDateTime.Today将 Kind 设置为 Local,因此将其更改为 Unspecified

在处理日期的方法中:

// Incoming date has DateTimeKind.Local
var localDateTime = DateTime.Now;


// Set to unspecified
var unspecifiedDateTime = DateTime.SpecifyKind(localDateTime, DateTimeKind.Unspecified);

我只是在部署时才收到这个,然后意识到一个库正在将 DateTime.Now而不是 new DateTime(...)传递给一个方法。

这个被接受的答案很好地解释了原因,但是我还是要补充一点:

当序列化 DateTime 对象时,一些序列化器会弄乱 DateTime 属性的 Kind。我们在使用 ProtoBuf 和 MessagePack 序列化器在 Redis 中缓存 POCO 对象时遇到过这个问题。

所以,如果你知道是这种情况,那么强制你的日期时间变量类型为“未指定”是完全可以的,如下所示:

myDateTime = DateTime.SpecifyKind(myDateTime, DateTimeKind.Unspecified);