将UTC / GMT时间转换为当地时间


301

我们正在为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);            

根据此日期网站

UTC / GMT偏移

标准时区:UTC / GMT +12小时
夏时制:+1小时
当前时区偏移:UTC / GMT +13小时

我们如何调整加班时间?可以通过编程方式完成,还是在PC上进行某种设置?


2
Z时间是指UTC,而不是GMT。两者最多相差0.9秒。
mc0e

Answers:


374

对于诸如的字符串2012-09-19 01:27:30.000DateTime.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();

这将为您提供所需的结果。


19
只是指定类型的另一种方式:DateTime convertedTime = new DateTime(DateTime.Parse(dateStr).Ticks), DateTimeKind.Utc);
Brad 2010年

是ToLocalTime()吗?@Brad-您的父母不匹配。
TrueWill 2011年

2
此解决方案是否考虑了夏令时?当我尝试时,我要休息一个小时。
鲍勃·霍恩

7
改变的步骤KindDateTimeUnspecifiedUTC是不必要的。Unspecified假定是UTC出于以下目的ToLocalTimemsdn.microsoft.com/en-us/library/…–
CJ7

16
@ CJ7:是的,但是明确表示对可能需要维护代码的其他开发人员特别有用。
瑞安

121

如果您使用的是.NET 3.5,我会考虑使用System.TimeZoneInfo类。请参阅http://msdn.microsoft.com/zh-cn/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。这只是不必要的复杂性。请参阅我的答案以获取更多详细信息。
德鲁·诺阿克斯

11
如果有人需要,这里是我发现的TimeZoneInfo.FindSystemTimeZoneById时区的列表- codeproject.com/Messages/3867850/...
nikib3ro

辉煌!感谢Dan的这篇文章。我一直在寻找此修复程序3天。
凯文·摩尔

58
TimeZone.CurrentTimeZone.ToLocalTime(date);

8
仅当系统知道要转换的日期为UTC时,此方法才有效。请看我的回答。
德鲁·诺阿克斯

1
但是UTC是默认设置吗?因此,它适用于“未指定”,如CJ7的回答。
NickG 2013年

25

DateTime对象默认情况下具有的Kindof Unspecified,出于的目的,ToLocalTime假定为UTC

因此,要获取Unspecified DateTime对象的本地时间,您只需要执行以下操作:

convertedDate.ToLocalTime();

改变的步骤KindDateTimeUnspecifiedUTC是不必要的。Unspecified假定UTC用于以下目的ToLocalTimehttp : //msdn.microsoft.com/zh-cn/library/system.datetime.tolocaltime.aspx


6
反之亦然:convertedDate.FromLocalTime();将转换为UTC
R. Schreurs

16

我知道这是一个比较老的问题,但是我遇到了类似的情况,我想分享一下我为以后的搜索者(可能包括我自己)发现的内容:

DateTime.Parse()可能很棘手-请参阅此处例如,。

如果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 MS
......是的,对结果Parse(),而别人都在毫秒


14

我只想添加一般的注意事项。

如果您要做的只是从计算机的内部时钟获取当前时间,以在显示器或报告上显示日期/时间,那么一切就很好了。但是,如果您要保存日期/时间信息以供以后参考或正在计算日期/时间,请当心!

假设您确定一艘游轮于2007年12月20日世界标准时间15:00到达檀香山。您想知道那是什么当地时间。
1.可能至少涉及三个“本地人”。“本地”可能意味着檀香山,或者可能意味着您的计算机所在的位置,或者可能意味着您的客户所在的位置。
2.如果使用内置函数进行转换,则可能是错误的。这是因为(可能)夏令时在您的计算机上当前有效,但在12月没有生效。但是Windows不知道这一点...它所具有的只是一个标志,用于确定当前是否正在实施夏令时。如果目前有效,那么即使是12月的日期,它也会很高兴地增加一个小时。
3。在各个政治部门中,夏令时的实现方式有所不同(或根本没有实现)。不要仅仅因为您的国家/地区在特定日期发生变化,就不要以为其他国家也会如此。


6
实际上,#2并不完全正确。实际上,在每个时区中都有关于DST的规则,您的计算机将知道该信息是否已安装(和更新)。对于许多区域,这些规则是固定的。其他实现“动态DST”。巴西是我的宠儿。总而言之,如果假设您的本地时间在12月之间未更改为法律,则您的计算机管理员可以算出当地时间是12月DST。
Roger Willcocks

即使您不住在巴西,DST也是“动态的”,因为政客可以随时更改它(就像几年前在美国所做的那样)。由于大多数软件都是为将来使用而编写的,因此必须意识到,没有任何实际,可预测甚至是理论的方法来了解什么DST规则将生效。您可以亲近,但放弃完美就可以避免沮丧。
DaveWalley 2014年


5

不要忘记您是否已经有一个DateTime对象,并且不确定它是UTC还是Local,直接在该对象上使用方法就很容易了:

DateTime convertedDate = DateTime.Parse(date);
DateTime localDate = convertedDate.ToLocalTime();

我们如何调整加班时间?

除非指定,否则.net将使用本地pc设置。我将阅读:http : //msdn.microsoft.com/zh-cn/library/system.globalization.daylighttime.aspx

从外观上看,代码可能类似于:

DaylightTime daylight = TimeZone.CurrentTimeZone.GetDaylightChanges( year );

并如上所述,仔细检查服务器的时区设置。网上有文章介绍如何安全地影响IIS中的更改。


系统会为您处理这种复杂性,前提是您告诉系统日期是什么“种类”(本地/ utc /未指定)。
德鲁·诺阿克斯

2

回答达娜的建议:

现在,代码示例如下所示:

string date = "Web service date"..ToString("R", ci);
DateTime convertedDate = DateTime.Parse(date);            
DateTime dt = TimeZone.CurrentTimeZone.ToLocalTime(convertedDate);

原始日期是08年8月20日;那种是UTC。

“ convertedDate”和“ dt”都相同:

21/08/08 10:00:26; 那种是当地的


请参阅我的答案以对此进行解释。
德鲁·诺阿克斯

1

我有一个问题,它是通过网络(从Web服务到客户端)推送的数据集,由于DataColumn的DateType字段设置为local,因此它将自动更改。如果跨过数据集,请确保检查DateType是什么。

如果您不想更改它,请将其设置为Unspecified


1

我遇到了这个问题,因为我在通过Twitter API(状态上的created_at字段)返回的UTC日期上遇到了问题;我需要将它们转换为DateTime。此页面上的答案中的所有答案/代码示例均不足以阻止我收到“未将字符串识别为有效的DateTime”错误(但这是我在SO上找到正确答案的最接近的错误)

万一这对其他人有帮助,请在此处发布此链接-在此博客文章中找到了我需要的答案:http : //www.wduffy.co.uk/blog/parsing-dates-when-aspnets-datetimeparse-doesnt-work/ -基本上使用带格式字符串的DateTime.ParseExact而不是DateTime.Parse

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.