为什么DateTime.ToLocalTime()没有考虑夏令时?

时间:2015-04-10 09:05:29

标签: c# .net datetime

我有一个UTC时间字符串(我从数据库中获取,因此我无法更改格式)是使用DateTime.UtcNow.ToString("s")创建的。我希望展示一些面向用户的内容,例如" 10:00 AM"。我在哪里(在英格兰),时钟最近向前推进,以下方法是一小时出发:

var timenowstring = DateTime.UtcNow.ToString("s");
var dateutc = DateTime.Parse(timenowstring).ToShortTimeString();
var datelocal = DateTime.Parse(timenowstring).ToLocalTime().ToShortTimeString();

Console.WriteLine("Utc time string: " + dateutc);
Console.WriteLine("Local time string: " + datelocal);

印刷" 9:02 AM"实际上它的时间是10:02 AM。

以下是http://csharppad.com/上重新发布的屏幕截图:

utc and local show the same time - see system clock in lower right, which is correct bigger image

CSharpPad gist

我做错了什么以及获取DateTime对象的最简单方法是什么?当我调用.ToShortTimeString()时,它会返回正确的时间?

NB the docs on ToLocalTime()说:

  

转换还会考虑适用于当前DateTime对象所代表的时间的夏令时规则。

3 个答案:

答案 0 :(得分:7)

你说:

  

我有一个UTC时间字符串(我从数据库中获取,因此我无法更改格式)是使用DateTime.UtcNow.ToString("s")创建的。

马上,你有一个问题。数据库中的日期(通常)不存储为字符串。它们存储在具有特定数据类型的字段中。在SQL Server中(例如),您可能正在使用datetimedatetime2字段。这些不是字符串。当您将它们检索到.NET代码中时,它们会直接转换为DateTime类型。如果你把它当作一个字符串来处理,你就错了。

例如,您的数据访问代码可能正在执行以下操作:

DateTime dt = Convert.ToDateTime(dataReader["myDateTimeField"].ToString());

这很常见,而且完全错误。你应该这样做:

DateTime dt = (DateTime) dataReader["myDateTimeField"];

或者如果字段可以为空:

DateTime? dt = dataReader["myDateTimeField"] as DateTime;

正确加载值而不是将其解析为字符串后,其余的都可以正常工作。 DateTime属性的DateTimeKind.Unspecified值为Kind,当您在其上调用ToLocalTime时,它会假定您要将未指定的值视为UTC。 (See the chart on MSDN。)

关于您发布的代码,虽然它有点混乱(不必要地浏览字符串),但它实际上可以正常工作 - 假设您在 时区中运行它。在ToLocalTime方法中," local"表示代码恰好在哪里运行的本地时区设置。 对于csharppad.com,时区恰好是UTC 。它无法知道你想要使用英格兰的时区规则。

CSharpPad Time Zone

如果您打算在服务器上运行代码,那么您根本不应该使用ToLocalTime - 因为服务器的时区可能无关紧要。相反,您可以使用TimeZoneInfo转换时间:

// this uses the time zone for England
TimeZoneInfo tz = TimeZoneInfo.FindSystemTimeZoneById("GMT Standard Time");
DateTime englandDatetime = TimeZoneInfo.ConvertTimeFromUtc(utcDateTime, tz);

或者,您可以使用开源Noda Time库,如下所示:

DateTimeZone tz = DateTimeZoneProviders.Tzdb["Europe/London"];
DateTime englandDateTime = Instant.FromDateTimeUtc(utcDateTime)
                                  .InZone(tz)
                                  .ToDateTimeUnspecified();

答案 1 :(得分:0)

当我在本地运行代码时,它会给出预期的结果(本地时间对应于我的机器本地时间)。 虽然当我在http://csharppad.com上运行它时,时间是相同的。 Matt Johnson的答案中提到了原因:服务器与本地计算机有不同的时区。

答案 2 :(得分:0)

对于其他认为DateTime.ToLocalTime忽略夏时制时间的人(就像我一样),要意识到的是,您要转换的日期是是否考虑夏时制的基础。

示例:将UTC日期时间'1/1/2020'转换为我的本地(美国中部),我得到:'12 / 31/2019 18:00:00',这是正确的。当我在四月运行此程序并且夏令时生效时,我期望的是UTC-5:00而不是UTC-6:00。但是1月是CST,而不是CDT,因此UTC-6:00是正确的。

您的问题可能是“用户错误”,因为这是我的。