用时区和夏令时确定一天的开始

本文关键字:一天 开始 时区 夏令时 | 更新日期: 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小时。

你有什么建议吗?谢谢。

用时区和夏令时确定一天的开始

要进行这样的计算,您需要使用TimeZoneInfoDateTimeOffset类。

首先,我们需要获取本地时间和用户本地时间的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.aspx

http://msdn.microsoft.com/en-us/library/bb381744.aspx

需要时区文件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详细信息,历史中的时间无法转换为实际时间,因为这些在该历史时间可能无效。