我有一个网站,托管在不同的时区,而不是用户使用的应用程序。除此之外,用户还可以使用特定的时区。我想知道其他SO用户和应用程序是如何做到这一点的?最明显的部分是,在数据库中,日期/时间存储在UTC中。在服务器上时,所有日期/时间都应使用UTC处理。然而,我看到了三个我试图克服的问题:
以协调世界时为单位获取当前时间的
DateTime.UtcNow
).可轻松解决
DateTime
上有一个自定义的扩展方法(见下文)。主要的缺点是在视图中使用datetime的每个位置都必须调用扩展方法!这也会增加使用像JsonResult
这样的东西的难度。您不能再轻松地调用Json(myEnumerable)
,它必须是Json(myEnumerable.Select(transformAllDates))
。也许AutoMapper可以在这种情况下提供帮助?
ModelBinder
.下面是我想在视图中使用的扩展:
public static class DateTimeExtensions
{
public static DateTime UtcToLocal(this DateTime source,
TimeZoneInfo localTimeZone)
{
return TimeZoneInfo.ConvertTimeFromUtc(source, localTimeZone);
}
public static DateTime LocalToUtc(this DateTime source,
TimeZoneInfo localTimeZone)
{
source = DateTime.SpecifyKind(source, DateTimeKind.Unspecified);
return TimeZoneInfo.ConvertTimeToUtc(source, localTimeZone);
}
}
我认为,考虑到现在许多应用程序都是基于云的,其中服务器的本地时间可能与预期时区有很大不同,因此处理时区将是一件很常见的事情。
这个问题以前有没有被优雅地解决过?我是不是漏掉了什么?我们非常欣赏你的想法和想法。
编辑:为了澄清一些困惑,我想添加更多细节。现在的问题不是如何在数据库中存储UTC时间,而是从UTC->Local and Local->UTC的过程。正如@Max Zerbini指出的那样,将UTC->本地代码放在视图中显然是明智的,但使用DateTimeExtensions
真的是答案吗?当从用户获取输入时,接受日期作为用户的本地时间(因为这是JS将使用的),然后使用ModelBinder
将其转换为UTC,这有意义吗?用户的时区存储在数据库中,很容易检索。
发布于 2011-09-28 20:58:06
这不是一个建议,它更多地分享了一种范式,但我所见过的在web应用程序中处理时区信息的最激进的方式(不是ASP.NET MVC独有的)是这样的:
DateTime.UtcNow
.DateTime.UtcNow
.到目前为止,相当标准的票价,但这是事情变得“有趣”的地方。
<time>
元素,他们永远不会在ViewModel中直接呈现datetimes。它被实现为类似于Html.Time(Model.when)
的HtmlHelper
扩展。它将呈现<time datetime='[utctime]' data-date-format='[datetimeformat]'></time>
。然后,他们将使用javascript将UTC时间转换为客户端本地时间。该脚本将查找所有数据元素,并使用date-format
<time>
属性来格式化日期并填充元素的内容。
这样,他们就不必跟踪、存储或管理客户的时区。服务器并不关心客户端所在的时区,也不必进行任何时区转换。它只是简单地输出UTC,并让客户端将其转换为合理的东西。这在浏览器中很容易,因为它知道自己所在的时区。如果客户端更改了他/她的时区,web应用程序将自动更新。它们唯一存储的是用户区域设置的日期时间格式字符串。
我不是说这是最好的方法,但这是一种我以前从未见过的不同的方法。也许你会从中收集到一些有趣的想法。
发布于 2013-05-10 09:24:18
经过几次反馈后,这是我的最终解决方案,我认为是干净和简单的,涵盖了夏令时的问题。
1-我们在模型级别处理转换。因此,在Model类中,我们写道:
public class Quote
{
...
public DateTime DateCreated
{
get { return CRM.Global.ToLocalTime(_DateCreated); }
set { _DateCreated = value.ToUniversalTime(); }
}
private DateTime _DateCreated { get; set; }
...
}
2-在全局帮助器中,我们将自定义函数设置为"ToLocalTime":
public static DateTime ToLocalTime(DateTime utcDate)
{
var localTimeZoneId = "China Standard Time";
var localTimeZone = TimeZoneInfo.FindSystemTimeZoneById(localTimeZoneId);
var localTime = TimeZoneInfo.ConvertTimeFromUtc(utcDate, localTimeZone);
return localTime;
}
3-我们可以进一步改进这一点,将时区id保存在每个用户配置文件中,以便我们可以从User类中检索,而不是使用常量“中国标准时间”:
public class Contact
{
...
public string TimeZone { get; set; }
...
}
4-在这里,我们可以从下拉框中获取要显示给用户选择的时区列表:
public class ListHelper
{
public IEnumerable<SelectListItem> GetTimeZoneList()
{
var list = from tz in TimeZoneInfo.GetSystemTimeZones()
select new SelectListItem { Value = tz.Id, Text = tz.DisplayName };
return list;
}
}
所以,现在上午9点25分在中国,网站托管在美国,日期保存在UTC数据库中,这是最终结果:
5/9/2013 6:25:58 PM (Server - in USA)
5/10/2013 1:25:58 AM (Database - Converted UTC)
5/10/2013 9:25:58 AM (Local - in China)
编辑
感谢Matt Johnson指出了原始解决方案的弱点,很抱歉删除了原始帖子,但在获取正确的代码显示格式时遇到了问题……原来编辑器在混合“bulles”和"pre code“时有问题,所以我删除了这些bulles,这是可以的。
发布于 2011-09-28 11:20:58
在sf4answers上的events section中,用户可以输入事件的地址、开始日期和可选的结束日期。这些时间将转换为SQL server中的datetimeoffset
,用于说明与UTC的偏移量。
这与您面临的问题相同(尽管您使用的是不同的方法,因为您使用的是DateTime.UtcNow
);您有一个位置,并且需要将时间从一个时区转换为另一个时区。
我主要做了两件事,这对我很有效。首先,始终使用DateTimeOffset
structure。它说明了UTC的偏移量,如果你能从你的客户那里获得这些信息,它会让你的生活更容易一些。
其次,在执行转换时,假设您知道客户端所在的位置/时区,您可以使用public info time zone database将一个时间从协调世界时转换到另一个时区(或者,如果您愿意,可以在两个时区之间进行三角转换)。tz数据库(有时称为Olson database)的伟大之处在于,它可以说明历史上时区的变化;获取偏移量是想要获取偏移量的日期的函数(只需查看Energy Policy Act of 2005 that changed the dates when daylight savings time goes into effect in the US)。
有了数据库,您就可以使用ZoneInfo (tz database / Olson database) .NET API了。注意,这里没有二进制发行版,您必须下载latest version并自己编译它。
在撰写本文时,它目前正在解析最新数据分发中的所有文件(我实际上是在2011年9月25日对ftp://elsie.nci.nih.gov/pub/tzdata2011k.tar.gz文件运行它的;在2017年3月,您可以通过https://iana.org/time-zones或从ftp://fpt.iana.org/tz/releases/tzdata2017a.tar.gz获得它)。
因此,在sf4answers上,获取地址后,将其地理编码为经纬度组合,然后发送到第三方web服务,以获取与tz数据库中的条目相对应的时区。在此基础上,将开始和结束时间转换为具有适当协调时间偏移的DateTimeOffset
实例,然后将其存储在数据库中。
至于在SO和网站上处理它,这取决于观众和你试图展示的内容。如果您注意到了,大多数社交网站(以及sf4answers上的events部分)以相对时间显示事件,或者,如果使用绝对值,则通常是协调世界时。
但是,如果您的受众期望的是本地时间,那么使用DateTimeOffset
和一个将时区转换为的扩展方法将是很好的;SQL数据类型datetimeoffset
将转换为.NET DateTimeOffset
,然后您可以获得使用GetUniversalTime
method的通用时间。在此基础上,您只需使用ZoneInfo
类上的方法将UTC转换为本地时间(您需要做一些工作才能将其转换为DateTimeOffset
,但这已经足够简单了)。
在哪里进行转换?这是你必须在某个地方付出的代价,而且没有“最佳”的方法。不过,我还是选择了视图,将时区偏移量作为视图模型的一部分呈现给视图。这样,如果视图的需求发生变化,您就不必更改视图模型来适应这种变化。您的JsonResult
将只包含一个带有IEnumerable
和偏移量的模型。
在输入端,使用模型绑定器?我会说绝对不可能。您不能保证所有的日期(现在或将来)都必须以这种方式转换,它应该是您的控制器的一个显式函数来执行此操作。同样,如果需求发生变化,您不必调整一个或多个ModelBinder
实例来调整您的业务逻辑;而且它是业务逻辑,这意味着它应该在控制器中。
https://stackoverflow.com/questions/7577389
复制相似问题