determining the beginning of a day with timezones AND daylight saving
我在会话中将用户的时区存储为小数。 例如,如果用户在EST时区,我就有
1 | UserTimeZone = -5.00; |
数据库中的数据以UTC格式存储,因此我想计算该用户当天的开始和结束时间,以便当用户想要特定日期的数据时,记录会按时区进行调整。
这就是我正在做的事情:
1 2 3 4 5 6 7 | 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小时。
你有什么建议吗? 谢谢。
要进行此类计算,您需要使用
首先,我们需要为本地时间和用户的本地时间获取
1 2 | var localTimezone = TimeZoneInfo.Local; var userTimezone = TimeZoneInfo.FindSystemTimeZoneById("Eastern Standard Time"); |
这里需要注意的是,你需要知道用户本地时区的id(你目前的偏移量是不够的)。您可以获取系统使用
对于这个例子,我有硬编码的EST。
然后你需要在当地时区得到今天午夜(一天开始)的
1 2 3 | var todayDate = DateTime.Today; var todayLocal = new DateTimeOffset(todayDate, localTimezone.GetUtcOffset(todayDate)); |
鉴于此,您可以在用户的??时区中计算代表"今天"午夜的
1 | var todayUser = TimeZoneInfo.ConvertTime(todayLocal, userTimezone); |
最后,您可以为这两个日期创建时间戳,如下所示:
1 2 3 | 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; |
您需要使用JavaScript从用户的浏览器中收集必要的信息 - 有关此部分,请参阅http://www.onlineaspect.com/2007/06/08/auto-detect-a-time-zone-with-javascript/
当你有这个信息时,你可以设置
正如BrokenGlass所提到的,简单的偏移量不足以确定白天的处理时间,因为每个区域中的不同国家可能会以不同的方式处理夏令时。 C#
如果你想在没有用户帮助的情况下尝试计算时区,有几种方法可以做到这一点(通常围绕获取浏览器的首选语言,然后将其映射到一个国家......),这里有一些例子和这里。
接受的答案并不认为有时区在午夜就有DST前进过渡,因此当天的开始可能不是
此外,某些时区可能具有后退过渡,这两个时间段提供两个可能的午夜时间点。
考虑以下功能,它适用于两种情况:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | 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); } |
我建议在用户设置中添加时区并存储。时区根据一年中的时间有不同的时间。您可以使用TimeZoneInfo.GetSystemTimeZones方法提供用户可供选择的时区列表。您可以将任何日期存储为UTC并将其(使用TimeZoneInfo.ConvertTime方法)转换为显示时的用户时间,并在保存时将其转换回UTC。这将允许用户随时更改其时区而不会导致问题。如果你遵循这种格式,你不应该遇到任何问题。
预先警告,如果您不将日期存储为UTC并按照上面的建议进行转换,则可能会遇到问题。在某些时区,从夏令时变为标准时,某些日期不存在某些时间。 TimeZoneInfo类不适合这些不存在的时间。
正确的答案是每个人都告诉你要做的事情 - 在框架中使用TimeZone API。但在.NET 3.5之前,TimeZoneInfo API并不存在。如果您确实不想使用API??S,或者您使用的是.NET 3.5之前的某些内容,则可以在注册表中找到所有时区信息。
http://www.michaelbrumm.com/simpletimezone.html上有一组类可以直接读取注册表数据并进行所需的所有时区计算 - 并对DST进行调整。这是一个很好的代码(我们已经可靠地使用它多年)与源代码,所以你可以看到他们实际在做什么。
你不应该这样做。您应该只使用.net中内置的TimeZoneInfo。
例如:
1 2 | TimeZoneInfo.ConvertTimeToUtc(); TimeZoneInfo.ConvertTimeFromUtc(); |
由于您似乎无法查找API参数,因此您可以:
http://msdn.microsoft.com/en-us/library/system.timezoneinfo.converttimefromutc.aspx
http://msdn.microsoft.com/en-us/library/bb381744.aspx
您需要时区文件http://www.twinsun.com/tz/tz-link.htm
日光节省不一致,各国改变其DLS规则。
因此,您的应用程序将始终需要最新的Tz文件。
您的用户在注册时应选择
使用
- 哪个国家/地区用户属于;
- 什么是
- 什么是
已知问题:如果您没有历史DLS详细信息,则历史记录中的时间无法转换为实际时间,因为这些详细信息在该历史时间可能无效。