我想知道如何在不实际更改值的情况下更改 DateTime 对象的时区。这是背景...
我在 AppHarbor 上托管了一个 ASP.NET MVC 站点,并且服务器的时间设置为 UTC。当我从我的站点提交包含日期时间值的表单时,比如 2013 年 9 月 17 日凌晨 4:00,它会以该值发送到服务器。但是,当我这样做时:
public ActionResult Save(Entity entity)
{
entity.Date = entity.Date.ToUniversalTime();
EntityService.Save(entity);
}
...它错误地将其保留为同一时间(凌晨 4 点),因为服务器已经在 UTC 时间。所以在转换为 UTC 并保存到数据库后,数据库值为 2013-09-17 04:00:00.000,实际上它应该是 2013-09-17 08:00:00.000,因为浏览器是在 EST 上。我希望在提交表单并将值传递给 Controller 操作后,它会将 DateTime 的时区设置为浏览器的 (EST) 而不是服务器主机的 (UTC),但这并没有发生。
无论如何,现在我被困在一个包含正确日期/时间值但错误时区的 DateTime 对象上,所以我无法正确地将它转换为 UTC。我存储每个用户的时区,所以如果有一种方法可以在不实际更改值的情况下为 DateTime 对象设置时区(我不能执行 TimeZoneInfo.ConvertTime 因为它会更改值)那么我可以有正确的日期/时间值和正确的时区,然后我可以安全地执行 date.ToUniversalTime。话虽如此,我只看到了如何更改 DateTime 的 KIND 而不是 TimeZone,而没有更改其实际值。
有任何想法吗?
最佳答案
实际上,它正在做你要求它做的事情。从服务器的角度来看,您向它传递了一个“未指定的”DateTime
.换句话说,只是年、月日等,没有任何上下文。如果你看 .Kind
属性,你会看到它确实是 DateTimeKind.Unspecified
.
当您拨打 .ToUniversalTime()
在未指定类型的 DateTime
上, .NET 将假定运行代码的计算机的本地时区上下文。您可以阅读有关此内容的更多信息 in the documentation here .由于您的服务器设置为 UTC,因此无论输入类型如何,所有三种类型都会产生相同的结果。基本上,这是一个无操作。
现在,您说您希望时间反射(reflect)用户的时区。不幸的是,这是服务器没有的信息。没有携带时区详细信息的神奇 HTTP header 。
不过,有一些方法可以实现这种效果,并且您有一些选择。
JavaScript 方法
这可能是最简单的选择,但它确实需要 JavaScript。
Date
类(class)。moment
相反,来自 moment.js图书馆。 Date
获取 UTC 日期和时间或 moment
,并将其传递给您的服务器。2013-09-17T08:00:00.000Z
Z
在末尾。这表明时间是 UTC。 Date
中。或 moment
使用 JavaScript,然后发出本地日期和时间。 使用 Windows 时区的 .NET 方法
如果您不打算调用 JavaScript,则需要向用户询问他们的时区。这在更大的应用程序中可以很好地工作,例如在用户的个人资料页面上。
TimeZoneInfo.GetSystemTimeZones
建立一个下拉列表。TimeZoneInfo
列表中的项目,使用 .Id
值,以及 .DisplayName
为文本。 TimeZoneInfo tz = TimeZoneInfo.FindSystemTimeZoneById(yourUsersTimeZone);
DateTime utc = TimeZoneInfo.ConvertTimeToUtc(theInputDatetime, tz);
.NET 方法使用 IANA 时区
如果要使用更标准的 IANA 时区,例如
America/New_York
或 Europe/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();
关于夏令时
无论您采用这三种方法中的哪一种,您都必须处理夏令时造成的问题。这些示例中的每一个都显示了一种“宽松”的方法,如果您指定的本地时间不明确或无效,则会遵循某些规则,因此您仍然可以获得一些有效的时间。当我拨打
AtLeniently
时,您可以使用 Noda Time 方法直接看到这一点。 .但它也发生在其他人身上——这只是隐含的。在 JavaScript 中,规则可能因浏览器而异,因此不要期望结果一致。根据您收集的数据类型,您可能会认为做出这种假设是完全可以接受的。但在许多情况下,假设是不合适的。在这种情况下,您可能需要提醒用户输入时间无效,或者询问他们的意思是两个不明确的时间中的哪一个。
在 .Net 中,您可以使用
TimeZoneInfo.IsInvalidTime
来检查这一点。和 TimeZoneInfo.IsAmbiguousTime
.有关夏令时工作原理的示例,see here .在“spring-forward”转换中,转换期间的时间是无效的。在“回退”转换中,转换期间的时间是不明确的——也就是说,它可能发生在转换之前或之后。
关于c# - 指定日期时间的时区而不更改值,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18840081/