指定日期时间的时区而不更改值 [英] Specify timezone of datetime without changing value
问题描述
我想知道如何在不实际更改值的情况下更改DateTime对象的时区.这是背景...
I'm wondering how to go about changing the timezone of a DateTime object without actually changing the value. Here is the background...
我在AppHarbor上托管了一个ASP.NET MVC站点,该服务器的时间设置为UTC.当我从网站提交包含日期时间值的表单时,例如2013年9月17日4:00,它将以该值上载到服务器.但是,当我这样做时:
I have an ASP.NET MVC site hosted on AppHarbor and the server has its time set to UTC. When I submit a form from my site containing a datetime value, say 9/17/2013 4:00am, it comes up to the server with that value. However then when I do:
public ActionResult Save(Entity entity)
{
entity.Date = entity.Date.ToUniversalTime();
EntityService.Save(entity);
}
...由于服务器已经处于UTC时间,因此错误地将其保持为同一时间(凌晨4点).因此,在转换为UTC并保存到数据库后,数据库值是2013-09-17 04:00:00.000,实际上它应该是2013-09-17 08:00:00.000,因为浏览器处于EST上.我希望提交表单并将这些值传递给控制器操作后,它将把DateTime的时区设置为浏览器的(EST)而不是服务器主机的(UTC),但是那没有发生.
...it incorrectly keeps it as the same time (4am) because the server is already on UTC time. So after converting to UTC and saving to the database, the database value is 2013-09-17 04:00:00.000, when really it should be 2013-09-17 08:00:00.000, as the browser is on EST. I was hoping after form submission and passing the values to the controller action, it would set the DateTime's timezone to the browser's (EST) rather than the server host's (UTC), however that didn't happen.
无论如何,现在我陷入了一个DateTime对象,该对象包含正确的日期/时间值但错误的时区,因此无法正确地将其转换为UTC.我存储了每个用户的时区,因此,如果有一种方法可以设置DateTime对象的时区而不实际更改值(我不能执行TimeZoneInfo.ConvertTime,因为它将更改值),那么我可以正确的日期/时间值和正确的时区,那么我可以放心地做date.ToUniversalTime.话虽如此,我只看到了如何更改DateTime(而不是TimeZone)的KIND,而不更改其实际值.
In any case, now I'm stuck with a DateTime object that contains the correct date/time value but the wrong time zone, so I can't correctly convert it to UTC. I store the time zone of each user, so if there was a way to set the time zone for the DateTime object without actually changing the value (I can't do TimeZoneInfo.ConvertTime because it'll change the value) then I could have the correct date/time value AND the correct time zone, then I could safely do date.ToUniversalTime. That being said, I've only seen how to change the KIND of a DateTime, not the TimeZone, without changing its actual value.
有什么想法吗?
推荐答案
实际上,它确实在执行您要求的操作.从服务器的角度来看,您向它传递了一个未指定"的DateTime
.换句话说,只是年份,月份等,而没有任何上下文.如果查看.Kind
属性,您会发现它确实是DateTimeKind.Unspecified
.
Actually, it is doing exactly what you asked it to do. From the point of view of the server, you passed it an "unspecified" DateTime
. In other words, just year, month day, etc., withoutout any context. If you look at the .Kind
property, you will see that it is indeed DateTimeKind.Unspecified
.
在未指定类型的DateTime
上调用.ToUniversalTime()
时,.NET将假定运行代码的计算机的本地时区的上下文.您可以在此处的文档中详细了解.由于您的服务器设置为UTC,因此无论输入类型如何,这三种类型都会产生相同的结果.基本上,这是无人操作.
When you call .ToUniversalTime()
on an unspecified kind of DateTime
, .NET will assume the context of the local time zone of the computer that the code is running on. You can read more about this in the documentation here. Since your server is set to UTC then regardless of the input kind, all three kinds will yield the same result. Basically, it's a no-op.
现在,您说您想要时间来反映用户的时区.不幸的是,这是服务器所没有的信息.没有神奇的HTTP标头可以包含时区详细信息.
Now, you said that you wanted the time to reflect the user's time zone. Unfortunately, that is information that the server doesn't have. There's no magic HTTP header that carries time zone details.
尽管有多种方法可以达到这种效果,但是您有一些选择.
There are ways to achieve this effect though, and you have some options.
JavaScript方法
这可能是最简单的选项,但是它确实需要JavaScript.
This is probably the easiest option, but it does require JavaScript.
- 从用户那里获取本地日期和时间输入,并将其解析为JavaScript
Date
类.- 为便于解析,您可以考虑从 moment.js 库中使用
moment
.
- Take the local date and time input from your user and parse it into a JavaScript
Date
class.- For easier parsing, you might consider using a
moment
instead, from the moment.js library.
- 这在JavaScript中相对容易,因此我将为您省去细节.
- 您可以传递许多格式,但是首选方式是使用ISO8601时间戳.示例:
2013-09-17T08:00:00.000Z
使用Windows时区的.NET方法
如果您不打算调用JavaScript,则需要询问用户的时区.在较大的应用程序中(例如在用户的个人资料页面上),此方法可以很好地工作.
If you aren't going to invoke JavaScript, then you will need to ask the user for their time zone. This can work well in a larger application, such as on the user's profile page.
- 使用
TimeZoneInfo.GetSystemTimeZones
进行构建下拉列表.- 对于列表中的每个
TimeZoneInfo
项目,将.Id
用作值,并将.DisplayName
用作文本.
- Use
TimeZoneInfo.GetSystemTimeZones
to build a drop-down list.- For each
TimeZoneInfo
item in the list, use the.Id
for the value, and the.DisplayName
for the text.
然后,当您要转换时间时,可以使用此值.
Then you can use this value when you want to convert the time.
TimeZoneInfo tz = TimeZoneInfo.FindSystemTimeZoneById(yourUsersTimeZone); DateTime utc = TimeZoneInfo.ConvertTimeToUtc(theInputDatetime, tz);
- For each
-
这将与Windows时区一起使用,Windows时区是Microsoft创建的,并且存在一些缺点.您可以在时区标签Wiki 中了解它们的一些不足.
This will work with Windows time zones, which are a Microsoft creation, and have some drawbacks. You can read about a few of their deficiencies in the timezone tag wiki.
使用IANA时区的.NET方法
如果要使用更标准的IANA时区,例如
America/New_York
或Europe/London
,则可以使用If you want to use the more standard IANA time zones, such as
America/New_York
orEurope/London
, then you can use a library like Noda Time. It offers a much better API for working with date and time than the built-in framework. There's a bit of a learning curve, but if you're doing anything complicated it is well worth the effort. As an example: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中,规则会因浏览器而异,因此不要期望结果一致.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.根据要收集的数据类型,您可能会认为完全可以接受这种假设.但在许多情况下,不适合进行假设.在这种情况下,您可能需要警告您的用户输入时间无效,或者询问他们这是两个不明确的时间中的哪个.
Depending on what kind of data you're collecting, you may decide it's perfectly acceptable to make this kind of assumption. But in many cases it's not appropriate to assume. In that case, you may need to either alert your user that the input time is invalid, or ask them which of two ambiguous times they meant.
在.Net中,您可以使用
TimeZoneInfo.IsAmbiguousTime
.In .Net, you can check for this with
TimeZoneInfo.IsInvalidTime
andTimeZoneInfo.IsAmbiguousTime
.有关夏时制工作原理的示例,请参见此处.在弹簧向前"过渡中,过渡期间的时间无效.在后备"过渡中,过渡期间的时间是模棱两可的-也就是说,它可能发生在过渡之前或之后.
For an example of how daylight saving time works, see here. In the "spring-forward" transition, a time during the transition is invalid. In the "fall-back" transition, a time during the transition is ambiguous - that is, it could have happened either before or after the transition.
这篇关于指定日期时间的时区而不更改值的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!
- 对于列表中的每个
- For easier parsing, you might consider using a
- 为便于解析,您可以考虑从 moment.js 库中使用