将 UTC/GMT 时间转换为本地时间
Posted
技术标签:
【中文标题】将 UTC/GMT 时间转换为本地时间【英文标题】:Convert UTC/GMT time to local time 【发布时间】:2010-09-15 20:21:42 【问题描述】:我们正在为 Web 服务客户端开发 C# 应用程序。这将在 Windows XP PC 上运行。
Web 服务返回的字段之一是 DateTime 字段。服务器返回 GMT 格式的字段,即末尾带有“Z”。
但是,我们发现 .NET 似乎做了某种隐式转换,而且时间总是 12 小时。
以下代码示例在一定程度上解决了这个问题,因为 12 小时的差异已经消失,但它没有考虑到新西兰夏令时。
CultureInfo ci = new CultureInfo("en-NZ");
string date = "Web service date".ToString("R", ci);
DateTime convertedDate = DateTime.Parse(date);
根据this date site:
UTC/GMT 偏移量
标准时区:UTC/GMT +12 小时 夏令时:+1 小时 当前时区偏移:UTC/GMT +13 小时
我们如何调整额外的时间?这可以通过编程方式完成,还是 PC 上的某种设置?
【问题讨论】:
Z
时间指的是 UTC,而不是 GMT。两者最多相差 0.9 秒。
【参考方案1】:
对于2012-09-19 01:27:30.000
等字符串,DateTime.Parse
无法判断日期和时间来自哪个时区。
DateTime
有一个 Kind 属性,它可以有以下三个时区选项之一:
注意 如果您希望表示除 UTC 或您当地时区以外的日期/时间,那么您应该使用DateTimeOffset
。
所以对于您问题中的代码:
DateTime convertedDate = DateTime.Parse(dateStr);
var kind = convertedDate.Kind; // will equal DateTimeKind.Unspecified
你说你知道它是什么种类,所以告诉它。
DateTime convertedDate = DateTime.SpecifyKind(
DateTime.Parse(dateStr),
DateTimeKind.Utc);
var kind = convertedDate.Kind; // will equal DateTimeKind.Utc
现在,一旦系统知道它的 UTC 时间,您就可以拨打ToLocalTime
:
DateTime dt = convertedDate.ToLocalTime();
这将为您提供所需的结果。
【讨论】:
只是另一种指定种类的方法:DateTime convertedTime = new DateTime(DateTime.Parse(dateStr).Ticks), DateTimeKind.Utc);
不是 ToLocalTime() 吗? @Brad - 你的括号不匹配。
此解决方案是否考虑夏令时?当我尝试它时,我会休息一个小时。
不需要将DateTime
的Kind
从Unspecified
更改为UTC
的步骤。对于ToLocalTime
,Unspecified
被假定为UTC
:msdn.microsoft.com/en-us/library/…
@CJ7:是的,但是明确对可能需要维护代码的其他开发人员特别有帮助。【参考方案2】:
如果您使用的是 .NET 3.5,我会考虑使用 System.TimeZoneInfo 类。见http://msdn.microsoft.com/en-us/library/system.timezoneinfo.aspx。这应该正确考虑夏令时的变化。
// Coordinated Universal Time string from
// DateTime.Now.ToUniversalTime().ToString("u");
string date = "2009-02-25 16:13:00Z";
// Local .NET timeZone.
DateTime localDateTime = DateTime.Parse(date);
DateTime utcDateTime = localDateTime.ToUniversalTime();
// ID from:
// "HKEY_LOCAL_MACHINE\Software\Microsoft\Windows NT\CurrentVersion\Time Zone"
// See http://msdn.microsoft.com/en-us/library/system.timezoneinfo.id.aspx
string nzTimeZoneKey = "New Zealand Standard Time";
TimeZoneInfo nzTimeZone = TimeZoneInfo.FindSystemTimeZoneById(nzTimeZoneKey);
DateTime nzDateTime = TimeZoneInfo.ConvertTimeFromUtc(utcDateTime, nzTimeZone);
【讨论】:
如果您在自己的时区(本例中为 en-NZ)中工作,则无需处理 TimeZoneInfo。这只是不必要的复杂性。请参阅我的答案以了解更多详细信息。 如果有人需要,这里是我为 TimeZoneInfo.FindSystemTimeZoneById - codeproject.com/Messages/3867850/…找到的时区列表 太棒了!感谢这个职位丹。我一直在寻找此修复程序 3 天。【参考方案3】:TimeZone.CurrentTimeZone.ToLocalTime(date);
【讨论】:
这仅在系统知道要转换的日期是 UTC 时才有效。请看我的回答。 但是 UTC 不是默认的吗?因此,它适用于 CJ7 的答案中的“未指定”。【参考方案4】:DateTime
对象默认具有 Unspecified
的 Kind
,对于 ToLocalTime
的目的,假定为 UTC
。
要获取Unspecified
DateTime
对象的当地时间,您只需要这样做:
convertedDate.ToLocalTime();
无需将DateTime
的Kind
从Unspecified
更改为UTC
的步骤。对于ToLocalTime
,Unspecified
被假定为UTC
:http://msdn.microsoft.com/en-us/library/system.datetime.tolocaltime.aspx
【讨论】:
反之亦然:convertedDate.FromLocalTime();
将转换为 UTC
。【参考方案5】:
我知道这是一个较老的问题,但我遇到了类似的情况,我想为未来的搜索者分享我的发现,可能包括我自己:)。
DateTime.Parse()
可能很棘手——例如,请参阅here。
如果 DateTime
来自 Web 服务或其他具有已知格式的来源,您可能需要考虑类似
DateTime.ParseExact(dateString,
"MM/dd/yyyy HH:mm:ss",
CultureInfo.InvariantCulture,
DateTimeStyles.AssumeUniversal | DateTimeStyles.AdjustToUniversal)
或者,甚至更好,
DateTime.TryParseExact(...)
AssumeUniversal
标志告诉解析器日期/时间已经是 UTC; AssumeUniversal
和 AdjustToUniversal
的组合告诉它不要将结果转换为“本地”时间,默认情况下它会尝试这样做。 (无论如何,我个人尝试在业务/应用程序/服务层中专门处理 UTC。但是绕过转换为本地时间也加快了速度——在我的测试中提高了 50% 或更多,见下文。)
这是我们之前所做的:
DateTime.Parse(dateString, new CultureInfo("en-US"))
我们对应用进行了分析,发现 DateTime.Parse 占 CPU 使用率的很大一部分。 (顺便说一下,CultureInfo
构造函数不是对 CPU 使用的重要贡献。)
所以我设置了一个控制台应用程序,以各种方式解析日期/时间字符串 10000 次。底线:Parse()
10 秒ParseExact()
(转换为本地)20-45 毫秒ParseExact()
(不转换为本地)10-15 毫秒
... 是的,Parse()
的结果以 秒 为单位,而其他结果以 毫秒 为单位。
【讨论】:
【参考方案6】:我只想补充一点注意事项。
如果您所做的只是从计算机的内部时钟获取当前时间以在显示屏或报告上显示日期/时间,那么一切都很好。但是,如果您要保存日期/时间信息以供以后参考,或者要计算日期/时间,请小心!
假设您确定一艘游轮于 2007 年 12 月 20 日 15:00 UTC 抵达檀香山。而且您想知道那是当地时间。1. 可能至少涉及三个“当地人”。本地可能表示檀香山,也可能表示您的计算机所在的位置,也可能表示您的客户所在的位置。2. 如果您使用内置函数执行转换,它可能是错误的。这是因为夏令时(可能)当前在您的计算机上生效,但在 12 月没有生效。但是 Windows 不知道这一点……它只有一个标志来确定夏令时当前是否有效。如果它目前有效,那么即使是在 12 月的日期,它也会很高兴地增加一个小时。3. 夏令时在不同的政治分区中实施不同(或根本不实施) .不要以为您的国家/地区在特定日期发生变化,其他国家/地区也会发生变化。
【讨论】:
其实,#2 并不完全正确。事实上,每个时区都有关于 DST 的规则,您的计算机将知道信息是否已安装(和更新)。对于许多区域,这些规则是固定的。其他人实施“动态 DST”。巴西是我最讨厌的。总而言之,假设从现在到那时没有任何变化成为法律,您的计算机可以计算出您的当地时间是否是 12 月的 DST。 即使您不住在巴西,DST 也是“动态的”,因为政客可以随时更改它(就像几年前在美国所做的那样)。由于大多数软件都是为将来使用而编写的,因此重要的是要意识到没有实际的、可预测的甚至理论上的方法来了解 DST 规则将生效。你可以接近,但放弃完美可以避免一些挫折。【参考方案7】:@TimeZoneInfo.ConvertTimeFromUtc(timeUtc, TimeZoneInfo.Local)
【讨论】:
【参考方案8】:不要忘记如果您已经有一个 DateTime 对象并且不确定它是 UTC 还是 Local,直接使用对象上的方法很容易:
DateTime convertedDate = DateTime.Parse(date);
DateTime localDate = convertedDate.ToLocalTime();
我们如何调整额外的时间?
除非指定 .net 将使用本地电脑设置。我会阅读:http://msdn.microsoft.com/en-us/library/system.globalization.daylighttime.aspx
从外观上看,代码可能类似于:
DaylightTime daylight = TimeZone.CurrentTimeZone.GetDaylightChanges( year );
如上所述,请仔细检查您的服务器所在的时区设置。网上有文章介绍如何安全地影响 IIS 中的更改。
【讨论】:
系统将为您处理这种复杂性,前提是您告诉系统您的日期是什么“种类”(本地/UTC/未指定)。【参考方案9】:回应 Dana 的建议:
代码示例现在看起来像:
string date = "Web service date"..ToString("R", ci);
DateTime convertedDate = DateTime.Parse(date);
DateTime dt = TimeZone.CurrentTimeZone.ToLocalTime(convertedDate);
原来的日期是 2008 年 8 月 20 日;那种是UTC。
“convertedDate”和“dt”都是一样的:
21/08/08 10:00:26;那种是本地的
【讨论】:
请参阅我的回答以获得对此的解释。【参考方案10】:我遇到了一个问题,它位于通过网络推送的数据集中(Web 服务到客户端),它会自动更改,因为 DataColumn 的 DateType 字段设置为本地。如果您推送数据集,请确保检查 DateType 是什么。
如果您不想更改,请将其设置为未指定
【讨论】:
【参考方案11】:我遇到了这个问题,因为我对通过 twitter API 返回的 UTC 日期有疑问(状态上的 created_at 字段);我需要将它们转换为 DateTime。此页面上的答案中的所有答案/代码示例都不足以阻止我收到“字符串未被识别为有效的日期时间”错误(但这是我在 SO 上找到正确答案的最接近的答案)
在此处发布此链接以防对其他人有帮助 - 我需要的答案在此博客文章中找到:http://www.wduffy.co.uk/blog/parsing-dates-when-aspnets-datetimeparse-doesnt-work/ - 基本上使用 DateTime.ParseExact 和格式字符串而不是 DateTime.Parse
【讨论】:
【参考方案12】:此代码块使用通用时间来转换当前 DateTime 对象,然后将其转换回本地 DateTime。非常适合我,希望对您有所帮助!
CreatedDate.ToUniversalTime().ToLocalTime();
【讨论】:
以上是关于将 UTC/GMT 时间转换为本地时间的主要内容,如果未能解决你的问题,请参考以下文章