32

我正在开发一个“在线提醒系统”项目(ASP.NET 2.0 (C#) / SQL Server 2005)

因为这是一项提醒服务,它将在特定日期向用户发送邮件。但问题是用户不是来自特定的国家,他们来自世界各地,来自不同的时区。现在,当我注册时,我正在询问用户时区,就像 Windows 在安装时询问我们的时区一样。

但是我没有得到如果用户选择(+5.30)或某个时区,那么如何在我的 asp.net 应用程序中处理这个时区。如何根据时区工作。

请建议是否有更好的方法来处理此应用程序中的时区?

谢谢

4

7 回答 7

29

首先要确定您的数据位于哪个时区。我建议确保您存储的任何 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);
于 2009-05-07T05:24:15.603 回答
19

我建议始终在服务器端使用 UTC (GMT) 时间(在代码隐藏、数据库等中),并将时间从 UTC 转换为本地时间,仅用于显示目的。这意味着所有时间操作——包括在数据库中节省时间、执行计算等——都应该使用 UTC 完成。

问题是:您的代码隐藏如何知道客户端浏览器的时区?假设用户在表单中输入一些日期/时间值(例如12/30/2009 14:30)并将其提交给服务器。假设用户提交的是本地时间,那么服务器如何知道如何将此值转换为 UTC?

应用程序可以要求用户指定时区(并将其保存在持久性 cookie 或数据库中),但这需要用户付出额外的努力,并且您的应用程序需要为此实现逻辑和屏幕。如果应用程序可以自动确定客户端的时区,那就更好了。

我已经在 J​​avaScript 的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 副本的链接)。我希望我可以发布文章中的示例,但我不想侵犯版权;然而,这里有一个项目来构建一个具有所有必要功能和文档的辅助库(忽略你不需要的东西)。

更新:文章已由新发布者在线发布,其中包含示例项目。

于 2009-05-07T07:54:40.513 回答
3

到目前为止,所有答案的问题在于他们没有考虑到 Prashant 试图实现的目标。如果他的系统用户在夏令时更改前一天的偏移量为+12,并为第二天设置了提醒,则他应该触发提醒时的偏移量将改为+13。

这就是为什么您只能将当前偏移量用于现在正在发生的事情。尽管我确实同意其他所有人的观点,即所有时间服务器端(可能仅用于显示的除外)都应存储在 UTC 中。

于 2009-05-12T02:26:06.953 回答
2

如果您使用的是框架 2.0 或更高版本,您可能希望查看使用DateTimeOffset结构而不是 DateTime。

DateTimeOffset 表示相对于 UTC 时间的时间点,因此在这种情况下应该更容易使用。

于 2009-05-07T06:36:53.087 回答
1

有2个步骤:

  • 使用 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);
}
于 2015-04-14T14:00:10.433 回答
0

基本上,您需要做的就是将偏移量(小时+分钟)添加到用户输入的本地时间。添加偏移量基本上会在 UTC(基本上是 GMT)时区为您提供 DateTime。

通常最容易将所有时间标准化为 UTC,这样您的应用程序逻辑就不必处理偏移量。

这个页面有几个很好的例子:http: //msdn.microsoft.com/en-us/library/bb546099.aspx

于 2009-05-07T05:25:50.543 回答
0

问题是与 UTC 的偏移量在一年中的不同时间会有所不同——每个时区都有自己的规则。(我在开发会议室调度应用程序时学到了这一点。)

看起来这里有内置支持:http: //msdn.microsoft.com/en-us/library/system.timezoneinfo.converttime.aspx

自己没有尝试过,但它似乎可以保证正确的转换,考虑到夏令时。

如果没有,这是我使用过的(昂贵的)商业工具:http ://www.worldtimeserver.com/time_zone_guide/

于 2009-05-12T05:16:18.080 回答