如何在 ASP.NET 中使用时区?
Posted
技术标签:
【中文标题】如何在 ASP.NET 中使用时区?【英文标题】:How to work with time zones in ASP.NET? 【发布时间】:2010-10-24 09:30:36 【问题描述】:我正在开发一个“在线提醒系统”项目(ASP.NET 2.0 (C#) / SQL Server 2005)
因为这是一项提醒服务,它会在特定日期向用户发送邮件。但问题是用户不是来自特定的国家,他们来自世界各地,来自不同的时区。现在,当我注册时,我正在询问用户时区,就像 Windows 在安装时询问我们的时区一样。
但是我没有得到如果用户选择 (+5.30) 或某个时区,那么如何在我的 asp.net 应用程序中处理这个时区。如何根据时区工作。
请建议是否有更好的方法来处理此应用程序中的时区??
谢谢
【问题讨论】:
这个问题可能很旧并且有一个公认的答案,但它相当广泛,答案只解决了问题的一个方面。答案也不正确,因为推荐的解决方案不包括夏令时,并且使用TimeZoneInfo
- 这在 .NET 2.0 中不可用(这是这个问题的要求)。我建议关闭或删除此问题。
【参考方案1】:
有两个步骤:
使用 javascript 在客户端检测不同的时区:
var dt = new Date();
var diffInMinutes = -dt.getTimezoneOffset();
然后在服务器端,C# 代码根据上面检测到的时区偏移量将服务器时间转换为客户端时间:
------------------------;
string queryStr = Request.QueryString["diffInMinutes"];
int diffInMinutes = 0;
if (Int32.TryParse(queryStr, out diffInMinutes))
clientTime = serverTime.ToUniversalTime().AddMinutes(diffInMinutes);
【讨论】:
为我工作。好的例子。问题不在于将日期存储为 UTC,正如您所展示的那样,如果需要,已经有一个“ToUniveralTime()”方法可以转换为 UTC。真正的问题是获取客户端所在的本地时区偏移量。此示例也适用于将 json 对象传递给控制器的 MVC。【参考方案2】:我建议始终在服务器端使用 UTC (GMT) 时间(在代码隐藏、数据库等中),并将时间从 UTC 转换为 本地时间以进行显示 仅限。这意味着所有时间操作 - 包括在数据库中节省时间、执行计算等 - 都应该使用 UTC 完成。
问题是:您的代码隐藏如何知道客户端浏览器的时区?假设用户在表单中输入一些日期/时间值(例如 12/30/2009 14:30)并将其提交给服务器。假设用户提交的是本地时间,那么服务器如何知道如何将这个值转换为UTC?
应用程序可以要求用户指定时区(并将其保存在持久性 cookie 或数据库中),但这需要用户付出额外的努力,并且您的应用程序需要为此实现逻辑和屏幕。如果应用能够自动确定客户的时区,那就更好了。
我已经在 JavaScript 的 getTimezoneOffset 函数的帮助下解决了这个问题,这是唯一可以告诉服务器客户端本地时间与 GMT 时间差的 API。由于这是一个客户端 API,我执行了以下操作:在服务器端检查保存时间偏移值的自定义会话 cookie,如果它不可用,则重新加载页面(仅在 GET 期间,而不是 POST 调用期间)添加了一些 JavaScript 逻辑来生成时间偏移并将其保存在 cookie 中。从客户端来看,这几乎是透明的(在会话期间我在 GET 上重新加载页面)。在 cookie 中获得偏移量后,我将根据时间转换方向(UTC 到本地时间,或本地时间到 UTC)将其应用于时间管理功能。
这听起来可能有点复杂,但确实如此,但在我编写了辅助函数之后,将这个功能集成到站点中只需在 Page_Load(需要时间的页面)中进行 单次调用转换),并在向浏览器发送和从浏览器检索时间值时使用时间转换例程。这是一个如何使用它的示例:
using My.Utilities.Web;
...
// Derive the form class from BaseForm instead of Page.
public class WebForm1: BaseForm
...
private void Page_Load(object sender, System.EventArgs e)
// If we only want to load the page to generate the time
// zone offset cookie, we do not need to do anything else.
if (InitializeLocalTime())
return;
// Assume that txtStartDate is a TextBox control.
if (!IsPostback)
// To display a date-time value, convert it from GMT (UTC)
// to local time.
DateTime startDate = GetStartDateFromDB(...);
txtStartDate.Text = FormatLocalDate(startDate);
...
else
// To save a date-time value, convert it from local
// time to GMT (UTC).
DateTime tempDate = DateTime.Parse(txtStartDate.Text);
DateTime startDate = ConvertLocalTimeToUtc(tempDate);
SaveStartDateInDB(startDate, ...);
...
...
如果您需要更多详细信息,请查看 It’s About Time: Localizing Time in ASP.NET Applications 文章(抱歉,我没有发布者网站上文章的直接链接,因为 asp.netPRO 仅对付费订阅者进行访问;这里有指向PDF 副本,不过)。我希望我可以发布文章中的示例,但我不想侵犯版权;然而,这里有一个project to build a helper library,它具有所有必要的功能和文档(忽略你不需要的东西)。
更新:文章已由新发布者here在线发布,带有示例项目。
【讨论】:
我不同意您的应用应自动确定用户时区的说法。用户可以在他们的操作系统中设置他们想要的任何时区/日期/时间并绕过您的业务规则。永远不要相信用户输入。 我不明白布鲁诺的观点。首先,没有与时区相关的业务规则。这是可用性的问题。我们可以使用 GMT,但对用户来说不方便,所以我们使用本地时间。如果用户更改系统时钟(或他们用来指定时区的任何方式),谁在乎?我认为这里没有问题。我也没有关注从您的第一个陈述到第二个陈述的过渡。您不喜欢“自动”部分,还是根本不喜欢使用本地时间的想法(如果不是自动完成,用户必须能够以某种方式更改它,对吧)? +1。 @BrunoSalvino,您建议如何传递时区数据?我能想到的另一种方法是提供明确的用户输入。后者比这里提出的自动解决方案更不值得信赖,因为用户更容易出错。从用户的角度来看,这将需要时区输入,这对用户不友好,尤其是当页面甚至不需要日期时间信息,但仍然必须保留某些用户交互的日期和时间(比如用户上传一些文件或进行金融交易)。【参考方案3】:问题是与 UTC 的偏移量在一年中的不同时间会有所不同——每个时区都有自己的规则。 (我在开发会议室调度应用程序时学到了这一点。)
看起来这里有内置支持:http://msdn.microsoft.com/en-us/library/system.timezoneinfo.converttime.aspx
我自己没有尝试过,但它似乎可以保证正确的转换,考虑到夏令时。
如果没有,这是我用过的(昂贵的)商业工具:http://www.worldtimeserver.com/time_zone_guide/
【讨论】:
【参考方案4】:到目前为止,所有答案的问题在于他们没有考虑到 Prashant 试图实现的目标。如果他的系统用户在夏令时更改前一天的偏移量为+12,并为第二天设置了提醒,则他应该触发提醒时的偏移量将改为+13。
这就是为什么您只能将当前偏移量用于当前正在发生的事情。尽管我同意其他所有人的观点,即所有时间服务器端(可能仅用于显示的除外)都应存储在 UTC 中。
【讨论】:
【参考方案5】:如果您使用的是框架 2.0 或更高版本,您可能需要考虑使用 DateTimeOffset 结构而不是 DateTime。
DateTimeOffset 表示相对于 UTC 时间的时间点,因此在这种情况下应该更容易使用。
【讨论】:
【参考方案6】:首先要确定您的数据位于哪个时区。我建议确保您存储的任何 DateTime 都以 UTC 时间存储(使用 DateTime.ToUniversalTime()
获取它)。
当您要为用户存储提醒时,您将需要当前的 UTC 时间,添加或删除用户的时区差异,并将该新时间转换回 UTC;这就是您要存储在数据库中的内容。
然后,当您要检查要发送的提醒时,您只需根据 UTC 时间在数据库中查找要发送的提醒;基本上得到时间戳早于DateTime.Now.ToUniversalTime()
的所有提醒。
更新包含一些实现细节:
您可以从TimeZoneInfo.GetSystemTimeZones()
方法中获取时区列表;您可以使用它们为用户显示时区列表。如果您存储所选时区的 Id
属性,您可以从中创建一个 TimeZoneInfo 类实例,并计算给定本地日期/时间值的 UTC 时间:
TimeZoneInfo tzi = TimeZoneInfo.FindSystemTimeZoneById("<the time zone id>");
// May 7, 08:04:00
DateTime userDateTime = new DateTime(2009, 5, 7, 8, 4, 0);
DateTime utcDateTime = userDateTime.Subtract(tzi.BaseUtcOffset);
【讨论】:
好的,你的想法听起来不错,但我觉得那里有问题。您说“您将需要当前的 UTC 时间,添加或删除用户的时区差异,并将该新时间转换回 UTC;”但现在当我运行我的进程时,它将发送提醒。我位于印度时区 (IST),所以我应该什么时候运行提醒,以便提醒将在正确的时间发送给用户。 至少您需要将时间存储为“服务器参考”时间;我通常更喜欢 UTC,但在你的情况下,我猜 IST 也可以。如果您使用 IST,您将不需要存储 UTC 时间(因为您可以轻松地从 DateTime 类中获取该时间。用户的本地时间也是如此;从技术上讲,您只需要用户的时区;那么您可以从 IST 到 UTC 到本地时间。不过将它放在数据库中可能会很方便。 @Prashant:很抱歉没有回复您:答案是 BaseUtcOffset 应该总是从本地时间中减去,因为它已签名;在 UTC 之前的时区(例如 IST),BaseTimeOffset 返回一个正值的 TimeSpan,但对于 UTC 之后的时区,例如美国东部标准时间,它返回一个负的时间跨度;因此,如果 UTC 为 12:00,并且您的时区偏移为 -1 小时(因此本地时间为 11:00),您将获得 11:00 - (-1) 小时 => 11:00 + 1 小时 => 12:00。 请注意,由于潜在的夏令时问题,您应该使用DateTime.UtcNow()
而不是DateTime.Now().ToUniversalTime()
- 请参阅msdn.microsoft.com/en-us/library/ms973825.aspx#datetime_topic8
@Matt - 这篇文章可能很旧,但它是完全错误的。您不应手动减去基本偏移量。您应该改用 TimeZoneInfo.ConvertTime
从用户的时间转到 UTC。如果您只想知道任意时间的偏移量,您可以使用GetUtcOffset
。另请注意,当提出此问题时,TimeZoneInfo
在 .net 2.0 中不可用。最后 - 你真的应该在一个新问题中问这种事情,而不是对一个 5 年的旧帖子发表评论。【参考方案7】:
基本上,您需要做的就是将偏移量(小时+分钟)添加到用户输入的本地时间。添加偏移量基本上会为您提供 UTC(基本上是 GMT)时区的 DateTime。
通常最容易将所有时间标准化为 UTC,这样您的应用程序逻辑就不必处理偏移量。
这个页面有几个很好的例子:http://msdn.microsoft.com/en-us/library/bb546099.aspx
【讨论】:
以上是关于如何在 ASP.NET 中使用时区?的主要内容,如果未能解决你的问题,请参考以下文章
如何使用客户端的时区将 asp.net 日期时间反序列化为 JSON 日期时间