用时区和夏令时确定一天的开始
本文关键字:一天 开始 时区 夏令时 | 更新日期: 2023-09-27 18:08:11
我将用户的时区存储为会话中的小数。例如,如果用户在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);
我遇到的问题是,这并没有考虑到夏令时,所以即使美国东部时间比UTC晚5小时,由于夏令时的变化,目前它实际上比UTC晚4小时。
你有什么建议吗?谢谢。要进行这样的计算,您需要使用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;
正如BrokenGlass所提到的,简单的偏移量不足以确定夏令时的处理,因为每个区域的不同国家可能以不同的方式处理夏令时。c# TimeZone
类更加具体,并且支持夏令时(在MSDN上查看详细信息)。不幸的是,没有简单的方法可以从浏览器中获得相关的时区,但是这篇文章中有一些关于如何允许用户选择他们的时区的建议。
如果您想尝试在没有用户帮助的情况下计算出时区,有几种方法可以做到这一点(通常是围绕获取浏览器的首选语言,然后将其映射到国家…),这里和这里有一些示例。
您需要使用JavaScript从用户的浏览器中收集必要的信息—关于这一部分,请参阅http://www.onlineaspect.com/2007/06/08/auto-detect-a-time-zone-with-javascript/
当你有了这些信息,你可以设置UserTimeZone
(顺便说一句,这不应该是int
,因为有几个小时的时区!),以适应当前的时区,包括DST…
正确的答案是每个人都告诉你要做的——在框架中使用TimeZone api。但是在。net 3.5之前,TimeZoneInfo api并不存在。如果你真的不想使用这些api,或者你使用的是。net 3.5之前的版本,你可以在
的注册表中找到所有的时区信息。 HKLM/Software/Microsoft/Windows NT/CurrentVersion/Timezones
在http://www.michaelbrumm.com/simpletimezone.html上有一组可用的类,它们直接读取注册表数据并执行所需的所有时区计算—并根据夏时制进行调整。它是带有源代码的好代码(我们已经可靠地使用它多年了),因此您可以看到他们实际在做什么。
我建议在用户设置中添加一个时区并存储它。时区根据一年中的不同时间有不同的时间。您可以使用TimeZoneInfo提供一个时区列表供用户选择。GetSystemTimeZones方法。您可以将任何日期存储为UTC并将其转换(使用TimeZoneInfo)。ConvertTime方法)在显示时将其转换为用户时间,并在保存时将其转换回UTC。这将允许用户在任何时候更改他们的时区而不会引起问题。如果你遵循这个格式,你应该不会遇到任何问题。
预先警告,如果您不将日期存储为UTC并按照上面的建议进行转换,您可能会遇到问题。在某些时区,当从夏令时改为标准时间时,某些时间在某些日子不存在。TimeZoneInfo类不能很好地处理这些不存在的时间。
接受的答案不考虑在午夜有夏令时春季过渡的时区,因此一天的开始可能不是00:00
,而是01:00
。伊朗、古巴和巴西(部分地区)就是很好的例子。
此外,一些时区可能有回退转换,给出两个可能的时间点,即午夜。
考虑下面的函数,它满足两种情况:
using System.Linq;
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);
}
你不应该这样做。你应该直接使用。net内置的TimeZoneInfo。
例如:TimeZoneInfo.ConvertTimeToUtc();
TimeZoneInfo.ConvertTimeFromUtc();
因为你似乎无法查找API参数,这里你可以:
http://msdn.microsoft.com/en-us/library/system.timezoneinfo.converttimefromutc.aspxhttp://msdn.microsoft.com/en-us/library/bb381744.aspx
需要时区文件http://www.twinsun.com/tz/tz-link.htmRecord all you time in UTC/GMT.
夏令时不一致,各国改变DLS规则。
因此,您的应用程序将始终需要最新的Tz文件。
您的用户在注册时应该选择Time-Zone name
而不是时间偏移。因为两个区域可以有相同的时间偏移(有时使用DLS)。
使用Tz Name
你可以知道
- 用户所属的国家/地区;
-什么是his time offset
;
-什么是his DLS offset
,add these both
获取用户当前时间
已知问题:如果没有历史DLS详细信息,历史中的时间无法转换为实际时间,因为这些在该历史时间可能无效。