指定datetime的时区而不更改值

Jus*_*tin 10 c# asp.net-mvc timezone datetime appharbor

我想知道如何在不实际更改值的情况下更改DateTime对象的时区.这是背景......

我在AppHarbor上托管了一个ASP.NET MVC站点,服务器的时间设置为UTC.当我从我的网站提交包含日期时间值的表单时,比如9/17/2013凌晨4:00,它会转到具有该值的服务器.然而,当我这样做时:

public ActionResult Save(Entity entity)
{
    entity.Date = entity.Date.ToUniversalTime();
    EntityService.Save(entity);
}
Run Code Online (Sandbox Code Playgroud)

...它错误地将其保持为同一时间(凌晨4点),因为服务器已经处于UTC时间.因此,在转换为UTC并保存到数据库后,数据库值为2013-09-17 04:00:00.000,实际应该是2013-09-17 08:00:00.000,因为浏览器在EST上.我希望在表单提交并将值传递给控制器​​操作后,它会将DateTime的时区设置为浏览器(EST)而不是服务器主机(UTC),但这并没有发生.

在任何情况下,现在我都遇到了一个包含正确日期/时间值但时区错误的DateTime对象,所以我无法正确地将其转换为UTC.我存储每个用户的时区,所以如果有办法设置DateTime对象的时区而不实际更改值(我不能做TimeZoneInfo.ConvertTime因为它会改变值)然后我可以有正确的日期/时间值和正确的时区,然后我可以安全地做date.ToUniversalTime.话虽这么说,我只看到如何在不改变其实际值的情况下更改DateTime的KIND而不是TimeZone.

有任何想法吗?

Mat*_*int 13

实际上,它完全按照你的要求去做.从服务器的角度来看,您传递了一个"未指定" DateTime.换句话说,只是年,月,日等,没有任何背景.如果你看看这个.Kind房产,你会发现确实如此DateTimeKind.Unspecified.

当您调用.ToUniversalTime()未指定的类型时DateTime,.NET将假定运行代码的计算机的本地时区的上下文.您可以在此处的文档中阅读更多相关信息.由于您的服务器设置为UTC,因此无论输入类型如何,这三种类型都将产生相同的结果.基本上,这是一个无操作.

现在,您说您希望有时间反映用户的时区.不幸的是,这是服务器没有的信息.没有神奇的HTTP标头带有时区细节.

有一些方法可以实现这种效果,你有一些选择.

JavaScript方法

这可能是最简单的选择,但它确实需要JavaScript.

  • 从用户那里获取本地日期和时间输入并将其解析为JavaScript Date类.
  • Date或获取UTC日期和时间moment,并将其传递给您的服务器.
    • 这在JavaScript中相对容易,所以我会为您省去详细信息.
    • 您可以通过多种格式,但首选方式是ISO8601时间戳.例: 2013-09-17T08:00:00.000Z
  • 不要忘记Z最后.这表示时间是UTC.
  • 由于它以UTC身​​份传递给您,因此您可以在不进行任何转换的情况下存储它.
  • 检索它时,再次将UTC传递给浏览器,将其加载到一个Date或一个momentJavaScript中,然后发出本地日期和时间.
  • 如果采取这种方法,我强烈建议你试试moment.js.它可以在没有它的情况下完成,但这可能更复杂且容易出错.

使用Windows时区的.NET方法

如果您不打算调用JavaScript,那么您需要询问用户他们的时区.这可以在更大的应用程序中很好地工作,例如在用户的配置文件页面上.

  • 使用TimeZoneInfo.GetSystemTimeZones建立一个下拉列表.
    • 对于TimeZoneInfo列表中的每个项目,使用.Id值作为值,.DisplayName使用文本.
  • 然后,当您想要转换时间时,可以使用此值.

    TimeZoneInfo tz = TimeZoneInfo.FindSystemTimeZoneById(yourUsersTimeZone);
    DateTime utc = TimeZoneInfo.ConvertTimeToUtc(theInputDatetime, tz);
    
    Run Code Online (Sandbox Code Playgroud)
  • 这适用于Windows时区,这是Microsoft创建的,并且有一些缺点.您可以在时区标签wiki中了解它们的一些缺陷.

使用IANA时区的.NET方法

如果您想使用更标准的IANA时区,例如America/New_YorkEurope/London,那么您可以使用像Noda Time这样的库.与内置框架相比,它提供了更好的API来处理日期和时间.有一点学习曲线,但如果你做了任何复杂的事情,那值得付出努力.举个例子:

DateTimeZone tz = DateTimeZoneProviders.Tzdb["America/New_York"];
var pattern = LocalDateTimePattern.CreateWithInvariantCulture("yyyy-MM-dd HH:mm:ss");
LocalDateTime dt = pattern.Parse("2013-09-17 04:00:00").Value;
ZonedDateTime zdt = tz.AtLeniently(dt);
Instant utc = zdt.ToInstant();
Run Code Online (Sandbox Code Playgroud)


关于夏令时

Regardless of which of these three approaches you take, you will have to deal with the problems created by Daylight Saving Time. Each of these samples shows a "lenient" approach, where if the local time you specify is ambiguous or invalid, that some rule is followed so you still get some valid moment in time. You can see this directly with the Noda Time approach when I called AtLeniently. But it occurs with the other ones also - it's just implicit. In JavaScript, the rules can vary per browser, so don't expect consistent results.

根据您收集的数据类型,您可能会认为做出这种假设是完全可以接受的.但在许多情况下,假设是不合适的.在这种情况下,您可能需要提醒用户输入时间无效,或者询问他们意味着两个不明确的时间.

在.Net中,您可以使用TimeZoneInfo.IsInvalidTime和来检查TimeZoneInfo.IsAmbiguousTime.

有关夏令时如何工作的示例,请参见此处.在"前进"转换中,转换期间的时间无效.在"后退"过渡期间,过渡期间的时间是模糊的 - 也就是说,它可能在过渡之前或之后发生.