TimeZoneInfo.ConvertTimeToUtc问题

81

我们遇到了一个问题,其中一位开发人员编写了以下代码,并且在他的 DEV 环境中运行正常。但是当代码提交到 QA 后,出现以下错误信息导致代码无法运行:

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

转换无法完成,因为提供的 DateTime 对象未正确设置 Kind 属性。例如,当 Kind 属性为 DateTimeKind.Local 时,源时区必须为 TimeZoneInfo.Local。

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

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

第一个代码示例为什么在DEV1的环境中可以正常工作,但在我的DEV环境和我们的QA服务器上会出现错误?

7个回答

123

这取决于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 将防止你犯这些常见的错误。


1
你可以使用 DateTime.Ticks 来解决本地时间和 UTC 时间不兼容的问题。就像这样,当我从表中读取一个 UTC 时间时:var timeUtc = new DateTime(r.GetDateTime(r.GetOrdinal("TimeUtc")).Ticks, DateTimeKind.Utc); - Daniel Smolka
这只是一种更复杂的设置类型的方式。您可以使用 DateTime.SpecifyKind 来获得相同的结果。 - Matt Johnson-Pint
1
我已经使用NodaTime很长时间了,以至于多年来都没有使用TZI进行时区转换。 DateTimeKind.Local 旨在表示“本地机器”的本地时间,而不是“某个时区”的本地时间,这是非常愚蠢的设计决策。关于 DateTime 和时区的设计决策已经过时了,这在.NET 1.0之后是相当罕见的。 - rianjs

13

在这个例子中,我已经使用了“DateTime.SpecifyKind()”方法将本地时区转换为未指定类型,所以它对我来说运行得很好。

DateTime.SpecifyKind(utc,DateTimeKind.Unspecified);

这个方法创建一个新的DateTime对象,它具有与指定DateTime相同的滴答数,但被指定为未指定类型的DateTimeKind。

public static 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;
    }

4

基本上前面的回答已经说得很清楚了,但是为了简化问题:

DateTime.Now 或者 DateTime.Today 会将时区设置成 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(...)


3
我在这里找到了一个非常简单的解决方案,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.Now时出现。我按照以下方式更新我的代码,现在它可以正常工作:DateTime currentTime = new DateTime(DateTime.Now.Ticks, DateTimeKind.Unspecified);。

0

已经有一个答案解释得很好了,但是为了完整性,我也想再补充一点:

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

因此,如果你知道这种情况,那么强制将 datetime 变量的 kind 设为 "Unspecified" 就没有任何问题,就像这样:

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

0
我遇到了同样的问题,但我试图将我的本地时间转换为其他的UTC时间,然后我意识到我使用了错误的方法TimeZoneInfo.ConvertTimeToUtc,我真正需要的是TimeZoneInfo.ConvertTime从UTC时间转换。

-1

在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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接