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

use*_*973 5 .net c# datetime

我有一个UTC时间字符串(我从数据库中获取,因此我无法更改格式)DateTime.UtcNow.ToString("s").我想展示一些面向用户的内容,如"上午10点".我在哪里(在英格兰),时钟最近出现了,下面的方法是一小时出来:

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);
Run Code Online (Sandbox Code Playgroud)

两者都打印"上午9:02"实际上是10:02 AM.

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

utc和local同时显示 - 请参见右下方的系统时钟,这是正确的 更大的形象

CSharpPad要点

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

注意ToLocalTime()上的文档说:

转换还考虑了适用于当前DateTime对象所表示的时间的夏令时规则.

Mat*_*int 7

你说:

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

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

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

DateTime dt = Convert.ToDateTime(dataReader["myDateTimeField"].ToString());
Run Code Online (Sandbox Code Playgroud)

这是非常普遍的,完全错误的.你应该这样做:

DateTime dt = (DateTime) dataReader["myDateTimeField"];
Run Code Online (Sandbox Code Playgroud)

或者如果字段可以为空:

DateTime? dt = dataReader["myDateTimeField"] as DateTime;
Run Code Online (Sandbox Code Playgroud)

一旦正确加载值而不是将其解析为字符串,其余的都可以正常工作.该DateTime值将具有DateTimeKind.UnspecifiedKind属性,当您调用ToLocalTime它时,它将假定您希望将未指定的值视为UTC.(参见MSDN上的图表.)

关于你发布的代码,虽然它有点乱(不必要地通过字符串),它实际上会工作得很好 - 假设你在你的时区运行它.在该ToLocalTime方法中,"本地"表示代码恰好在哪里运行的机器的本地时区设置. 对于csharppad.com,时区恰好是UTC.它无法知道你想要使用英格兰的时区规则.

CSharpPad时区

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

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

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

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