【问题标题】:determining the beginning of a day with timezones AND daylight saving使用时区和夏令时确定一天的开始
【发布时间】:2011-11-14 23:05:19
【问题描述】:

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

您有什么建议?谢谢。

【问题讨论】:

  • 我认为只有时间偏移就足够了,请查看TimeZone / TimeZoneInfo
  • 不要使用 DateTime.Now.ToUniversalTime(),使用 DateTime.UtcNow
  • 并非所有时区都是整数偏移量,因此即使您切换到从浏览器获取偏移量,您也需要使用比 int 更好的东西。
  • 我没有使用 int,我使用的是小数。我检查了一下,发现 ToUniversalTime() 和 DateTime.UtcNow 之间没有任何区别,即使将计算机的日期更改为 12 月的某个时间也是如此。
  • 使用 Decimal 很好,但还不够……您是否只存储每个 User 的偏移量?这还不够-您需要存储此偏移量是否包括夏令时...两者(偏移量以及偏移量是否包括DS)都可以构建一个工作算法(尽管不是100%一致-最好是存储一个真实的时区每个用户!),否则不是 :-(

标签: c# asp.net


【解决方案1】:

接受的答案不考虑在午夜有 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);
}

【讨论】:

【解决方案2】:

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

【讨论】:

  • 您的服务器已经有这个文件是最新的;如果您传递 Tz 名称而不是时间偏移量,您的 API 将为 DLS 调整时间。
【解决方案3】:

我建议在用户设置中添加一个时区并将其存储起来。时区有不同的时间,具体取决于一年中的时间。您可以使用TimeZoneInfo.GetSystemTimeZones 方法提供时区列表供用户选择。您可以将任何日期存储为 UTC,并在显示时将其(使用TimeZoneInfo.ConvertTime 方法)转换为用户时间,并在保存时将其转换回 UTC。这将允许用户随时更改他们的时区而不会造成问题。如果您遵循这种格式,您应该不会遇到任何问题。

请注意,如果您不将日期存储为 UTC 并按照上面的建议进行转换,您可能会遇到问题。在某些时区,当从夏令时更改为标准时间时,某些日期不存在某些时间。 TimeZoneInfo 类不能很好地处理这些不存在的时间。

【讨论】:

    【解决方案4】:

    正确的答案是每个人都告诉你要做的——在框架中使用 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】:

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

      【讨论】:

      • 另外,避免使用“EST”,它和其他 TZ 缩写不是全球唯一的 :( 使用 UTC 偏移量,或使用完整的时区名称(“Ameria/NewYork”)。更多信息见这个问题:stackoverflow.com/questions/4309030/…
      • @jpeacock:.NET 对时区名称很挑剔(您确实需要使用GetSystemTimeZones 来查看它们)。此外,您不能使用 UTC 偏移量——这就是 OP 现在正在做的事情,而且并不令人满意。
      • @frenchie:原始海报——你。 :)
      【解决方案6】:

      您不应该这样做。您应该只使用 .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

      【讨论】:

      • 我在哪里传递时区信息。我正在寻找用户的开始时间(StartTime),以便我可以将那个时间放在我的查询中。我正在寻找类似 StartTime = function GetStartTime(Date, UserTimeZone) 的东西。
      • @frenchie - 请原谅我假设您实际上会查看 API 参考以查看要传递给它的参数。我真傻,以为你会那样做。
      • 对,所以问题不在于将时区转换为 UTC 并返回,问题是如果给定时区偏移量(即 -5.00 用于 EST),我如何知道 EST 时区是否为在夏令时的情况下,从 UTC 开始 -5 小时或从 UTC 开始 -4 小时。目标是确定用户一天开始的 UTC 时间。有时会是 UTC -5,有时会是 UTC-4,具体取决于夏令时。如果用户想查看 HIS 9 月 18 日的数据,我需要知道 UTC 格式的开始时间是什么,并考虑夏令时。
      • @frenchie - 这就是重点。如果您使用 TimeZoneInfo 函数,则不必这样做。它会为你解决这个问题。您只需将正确的 TimeZoneInfo 传递给它,您在注册时将其保存在您的用户个人资料中。忘记偏移量,它们并不重要。
      • 在哪里可以找到此类支持的所有时区列表?此页面按操作系统提供列表,但如何根据服务器的操作系统找到列表:msdn.microsoft.com/en-us/library/bb384272.aspx
      【解决方案7】:

      您需要使用 JavaScript 从用户的浏览器收集必要的信息 - 这部分请参见 http://www.onlineaspect.com/2007/06/08/auto-detect-a-time-zone-with-javascript/

      当您获得此信息后,您可以设置 UserTimeZone(顺便说一下,这不应该是 int,因为有些时区只有几分之一!)以适应当前时区,包括 DST...

      【讨论】:

      • 用户在注册时提供了他的时区;我需要的是一种方法来确定他的时区是否处于夏令时。
      • 如果您点击链接,它提供了一种方法来确定用户的当前时区是否处于夏令时!
      • Javascript 不是我想要的方式。用户在注册时向我提供了他的时区(以及地址),考虑到夏令时,我需要弄清楚他一天的开始时间。他可能来自加利福尼亚并前往东海岸,在一家网吧使用显示 EST 的浏览器。
      • 这是否可能完全取决于用户提供的“时区信息” - 如果您显示(代码、数据、您与用户存储的任何“时区信息”),那么它至少是有可能想办法……
      【解决方案8】:

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

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

      【讨论】:

      • 用户在注册时提供了他的时区;我需要的是一种方法来确定他的时区是否处于夏令时。
      • 看看this - 底线,您对时区信息的定义不足以确定夏令时。您希望他们向您提供他们的国家,以及他们在该国家/地区的时区。
      • Daniel 是正确的,尤其是因为时区从北到南,而南北半球的夏令时正好相反。
      猜你喜欢
      • 2023-03-04
      • 2013-10-13
      • 2019-04-25
      • 1970-01-01
      • 2012-03-21
      • 2015-08-11
      • 1970-01-01
      • 1970-01-01
      • 2013-09-24
      相关资源
      最近更新 更多