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

时间:2011-09-11 23:01:21

标签: c# asp.net

我将用户的时区存储为会话中的小数。例如,如果用户在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小时。

您有什么建议?感谢。

8 个答案:

答案 0 :(得分:9)

要进行此类计算,您需要使用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;

答案 1 :(得分:3)

正如BrokenGlass所提到的,一个简单的偏移量不足以确定白天的处理时间,因为每个区域中的不同国家可能会以不同的方式处理夏令时。 C#TimeZone类更具体,并且支持夏令时(查看MSDN的详细信息)。遗憾的是,没有简单的方法可以从浏览器中获取相关时区,但this post上有一些关于如何允许用户选择时区的建议。

如果您想在没有用户帮助的情况下尝试计算时区,可以采用以下几种方法(通常围绕获取浏览器的首选语言,然后将其映射到国家/地区......),一些示例是herehere

答案 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

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

答案 4 :(得分:2)

正确答案是每个人都告诉你的事情 - 在框架中使用TimeZone API。但在.NET 3.5之前,TimeZoneInfo API并不存在。如果你真的不想使用API​​S,或者你使用的是.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详细信息,则历史记录中的时间无法转换为实际时间,因为这些可能在该历史时间内无效。