我将用户的时区存储为会话中的小数。例如,如果用户在EST时区,我就有
UserTimeZone = -5.00;
数据库中的数据以UTC格式存储,因此我想计算该用户当天的开始和结束时间,以便当用户想要特定日期的数据时,记录会按时区调整。
这就是我正在做的事情:
DateTime StartDate = DateTime.Now.ToUniversalTime();
StartDate = StartDate.AddHours((double)UserTimeZone);
StartDate = StartDate.Date;
StartDate = StartDate.AddHours((double)UserTimeZone);
DateTime EndDate = StartDate.AddHours(24);
我遇到的问题是,这并不考虑夏令时,所以即使认为EST时间比UTC晚了5个小时,目前由于夏令时的变化,它实际上落后于UTC 4小时。
您有什么建议?感谢。
答案 0 :(得分:9)
要进行此类计算,您需要使用TimeZoneInfo
和DateTimeOffset
类。
首先,我们需要为本地时间和用户的本地时间获取TimeZoneInfo
实例:
var localTimezone = TimeZoneInfo.Local;
var userTimezone = TimeZoneInfo.FindSystemTimeZoneById("Eastern Standard Time");
这里需要注意的是,你需要知道用户本地时区的id(你目前的偏移量是不够的)。您可以获取系统使用TimeZoneInfo.GetSystemTimeZones
识别的所有TimeZoneInfo
个实例的列表,然后您需要一种方法将用户提供的时区与其中一个匹配。
对于这个例子,我有硬编码的EST。
然后你需要在当地时区的今天午夜(一天开始)获得DateTimeOffset
个实例:
var todayDate = DateTime.Today;
var todayLocal = new DateTimeOffset(todayDate,
localTimezone.GetUtcOffset(todayDate));
鉴于此,您可以在用户的时区中计算代表“今天”午夜的DateTimeOffset
实例(基于您当地时间)。 请注意,根据时区的不同,这可能实际上是用户时区的未来!
var todayUser = TimeZoneInfo.ConvertTime(todayLocal, userTimezone);
最后,您可以为这两个日期创建时间戳:
var epochStart = DateTime.Parse("01/01/1970 00:00:00");
var todayLocalTs = (todayLocal.Ticks - epochStart.Ticks)/TimeSpan.TicksPerSecond;
var todayUserTs = (todayUser.Ticks - epochStart.Ticks) / TimeSpan.TicksPerSecond;
答案 1 :(得分:3)
正如BrokenGlass所提到的,一个简单的偏移量不足以确定白天的处理时间,因为每个区域中的不同国家可能会以不同的方式处理夏令时。 C#TimeZone
类更具体,并且支持夏令时(查看MSDN的详细信息)。遗憾的是,没有简单的方法可以从浏览器中获取相关时区,但this post上有一些关于如何允许用户选择时区的建议。
如果您想在没有用户帮助的情况下尝试计算时区,可以采用以下几种方法(通常围绕获取浏览器的首选语言,然后将其映射到国家/地区......),一些示例是here和here。
答案 2 :(得分:3)
您需要使用JavaScript从用户的浏览器中收集必要的信息 - 对于此部分,请参阅http://www.onlineaspect.com/2007/06/08/auto-detect-a-time-zone-with-javascript/
如果您有这些信息,可以设置UserTimeZone
(顺便说一下,这不应该是int
,因为时区有几小时!)以容纳当前时区,包括DST ......
答案 3 :(得分:2)
你不应该这样做。您应该只使用.net中内置的TimeZoneInfo。
例如:
TimeZoneInfo.ConvertTimeToUtc();
TimeZoneInfo.ConvertTimeFromUtc();
由于您似乎无法查找API参数,因此请转到:
http://msdn.microsoft.com/en-us/library/system.timezoneinfo.converttimefromutc.aspx
答案 4 :(得分:2)
正确答案是每个人都告诉你的事情 - 在框架中使用TimeZone API。但在.NET 3.5之前,TimeZoneInfo API并不存在。如果你真的不想使用APIS,或者你使用的是.NET 3.5之前的东西,你可以在注册表中找到所有的时区信息
HKLM/Software/Microsoft/Windows NT/CurrentVersion/Timezones
http://www.michaelbrumm.com/simpletimezone.html有一组类可以直接读取注册表数据并进行所需的所有时区计算 - 并对DST进行调整。这是很好的代码(我们已经可靠地使用了多年)和源代码,所以你可以看到它们实际上在做什么。
答案 5 :(得分:2)
我建议在用户设置中添加时区并存储。时区根据一年中的时间有不同的时间。您可以使用TimeZoneInfo.GetSystemTimeZones方法提供用户可供选择的时区列表。您可以将任何日期存储为UTC并将其(使用TimeZoneInfo.ConvertTime方法)转换为显示时的用户时间,并在保存时将其转换回UTC。这将允许用户随时更改其时区而不会导致问题。如果你遵循这种格式,你不应该遇到任何问题。
预先警告,如果您不将日期存储为UTC并按上述建议进行转换,则可能会遇到问题。在某些时区,从夏令时变为标准时,某些日期不存在某些时间。 TimeZoneInfo类对这些不存在的时间不起作用。
答案 6 :(得分:2)
接受的答案并不认为有时区在午夜就有DST前进过渡,因此当天的开始可能不是00:00
而是01:00
。伊朗和巴西(部分地区)就是很好的例子。
此外,某些时区可能会有后退过渡,这两个过渡点会产生两个可能的午夜时间点。
考虑以下功能,它适用于两种情况:
static DateTimeOffset GetStartOfDay(DateTime dt, TimeZoneInfo tz)
{
// Work in the time zone provided
if (dt.Kind != DateTimeKind.Unspecified)
{
dt = TimeZoneInfo.ConvertTime(dt, tz);
}
// Start with assuming midnight
var d = dt.Date;
// Check for the time being invalid and handle if so
if (tz.IsInvalidTime(d))
{
// the gap is *usually* 1hr, but not always, so calculate it
var gap = tz.GetUtcOffset(dt.AddDays(1)) - tz.GetUtcOffset(dt.AddDays(-1));
// advance forward by the amount of the gap
d = d.Add(gap);
}
// Also check for the time being ambiguous, such as in a fall-back transition.
// We want the *first* occurrence, which will have a *larger* offset
var offset = tz.IsAmbiguousTime(d)
? tz.GetAmbiguousTimeOffsets(d).OrderByDescending(x => x).First()
: tz.GetUtcOffset(d);
// Now we know when the date starts precisely
return new DateTimeOffset(d, offset);
}
答案 7 :(得分:0)
您需要时区档案http://www.twinsun.com/tz/tz-link.htm
Record all you time in UTC/GMT.
日光节约不一致,各国改变其DLS规则 因此,您的应用程序将始终需要最新的Tz文件。
您的用户在注册时应选择Time-Zone name
,而不是时间偏移。因为两个区域可以具有相同的时间偏移(有时使用DLS)。
Tz Name
你知道了
- 国家/地区用户所属的内容;
- 什么是his time offset
;
- 什么是his DLS offset
,
add these both
让用户获得当前时间。
已知问题:如果您没有历史DLS详细信息,则历史记录中的时间无法转换为实际时间,因为这些可能在该历史时间内无效。