【问题标题】:Remove DST from DateTime从 DateTime 中删除 DST
【发布时间】:2012-08-29 07:37:23
【问题描述】:

我看到许多网站(通常是论坛)允许用户通过选择来指定他们的时区偏好:

  • 时区
  • 是否使用夏令时

据我所知,在进行转换时,.NET 总是将 DST 考虑在内,所以问题是:

如何在 C# 中实现“不使用夏令时”部分?

在这点下面,我介绍了我到目前为止所做的事情,但感觉很老套,我想知道是否有更清洁/更好的方法。

首先,为了确保自动应用夏令时,我编写了以下测试:

[Test]
public void DateTimeConversion_ToLocalTime_HandlesDSTByDefault()
{
    var utcDateInDstInterval = new DateTime(2012, 07, 15, 0, 0, 0, DateTimeKind.Utc);
    var utcDateOutisdeDstInterval = new DateTime(2012, 02, 15, 0, 0, 0, DateTimeKind.Utc);

    var roTimezone = TimeZoneInfo.FindSystemTimeZoneById("GTB Standard Time");

    Assert.AreEqual(3, TimeZoneInfo.ConvertTimeFromUtc(utcDateInDstInterval, roTimezone).Hour);
    Assert.AreEqual(2, TimeZoneInfo.ConvertTimeFromUtc(utcDateOutisdeDstInterval, roTimezone).Hour);
}

此测试通过,表明:

  • 当没有 DST 时(例如在冬季),罗马尼亚时间为 UTC + 2 小时
  • 适用 DST 时(例如在夏季),罗马尼亚时间为 UTC + 3 小时
  • 在进行 DateTime 转换时,会自动考虑 DST

接下来,我注意到返回 AdjustmentRule 对象数组的 TimeZoneInfo.GetAdjustmentRules() 方法,并发现如果我撤消这些规则的影响,我可以获得 DST 未受影响的值。

所以我写了下面的方法,如果DateTime对象is affected by DST

private DateTime RemoveDSTFromDateTime(DateTime dateTime, TimeZoneInfo timeZoneInfo)
{
    if (!dateTime.IsDaylightSavingTime())
        return dateTime;

    var result = dateTime;

    foreach (var adjustmentRule in timeZoneInfo.GetAdjustmentRules())
        result = result.Subtract(adjustmentRule.DaylightDelta);

    return result;
}

回到原来的 DST/No DST 场景,但这一次强制结果不受 DST 影响:

[Test]
public void DateTimeConversion_ToLocalTime_WithoutDST()
{
    var utcDateInDstInterval = new DateTime(2012, 07, 15, 0, 0, 0, DateTimeKind.Utc);
    var utcDateOutisdeDstInterval = new DateTime(2012, 02, 15, 0, 0, 0, DateTimeKind.Utc);

    var roTimezone = TimeZoneInfo.FindSystemTimeZoneById("GTB Standard Time");

    var convertedDateWithDst = TimeZoneInfo.ConvertTimeFromUtc(utcDateInDstInterval, roTimezone);
    var convertedDateWithoutDst = TimeZoneInfo.ConvertTimeFromUtc(utcDateOutisdeDstInterval, roTimezone);

    Assert.AreEqual(2, RemoveDSTFromDateTime(convertedDateWithDst, roTimezone).Hour);
    Assert.AreEqual(2, RemoveDSTFromDateTime(convertedDateWithoutDst, roTimezone).Hour);
}

这个测试也通过了,表明现在 DST 的效果被取消了(我们总是得到 UTC + 2h,不管一年中的什么时间)。

在写下这篇文章时,我想到了另一个似乎可行的想法:不要使用任何 TimeZoneInfo.Convert...() 方法,只需将 roTimezone.BaseUtcOffset 添加到 UTC 日期即可。

谁能指出正确的方法是什么?

【问题讨论】:

  • “据我所知,在进行转换时,.NET 总是将 DST 考虑在内” - 您实际上是误会了。
  • 你看NodaTime替换DateTimeDateTimeOffset了吗?
  • @Ramhound 是的,我可能是错的。这是我在做了一些 TimeZoneInfo 转换后得出的结论。你能提供一个更详细的链接吗?

标签: c# c#-4.0 datetime dst


【解决方案1】:

解析您的问题,您似乎有两个建议的解决方案:

  1. 确定 DST 调整并将其从转换后的输出中删除。

  2. 采用 UTC DateTime 并添加 BaseUtcOffset,因此您始终使用标准时间。

由于您的问题是如何不实施 DST,我想说您自己用选项 #2 回答了。

也就是说,您最初的假设是有缺陷的。许多网站要求时区和 DST 的原因通常是因为他们的时区实现不支持 DST。他们正在做的是让您选择一个基本偏移量并决定您是否希望在一年中的某个时间增加一个小时。他们可能称其为“时区”,但它绝不像 .Net 的 TimeZoneInfo 类或像 Noda Time 这样的库那样强大。

如果您真的选择“时区”,例如通过TimeZoneInfo.GetSystemTimeZones()(您应该保存Id 属性并显示DisplayName 属性),那么您绝对不应该询问用户是否使用夏令时。如果用户住在不使用 DST 的地方(例如在亚利桑那州),则已经有一个选择。用户通常很擅长选择正确的选择以获得他们想要的效果,尤其是如果他们居住在 DST 规则奇怪的地区(如印第安纳州)。

如果您使用像 "America/New York" 这样的 IANA/Olson 样式的时区,无论它们是由 Noda Time 还是其他一些实现提供,同样的想法也适用。您应该将 DST 决定委托给图书馆 - 而不是用户。

将其委托给图书馆的一个非常有效的论据是,这些 DST 规则可以更改,并且在其生命周期的许多时间点都发生了更改。如果您正在编写处理过去信息的任何类型的应用程序或网站,您可能会发现今天有效的内容并不是对过去发生的事情的正确调整。此外,时区世界也有很多奇怪之处,比如澳大利亚,某些地区的时差是 30 分钟而不是一小时。你真的想自己管理吗?可能不是。 :)

【讨论】:

    【解决方案2】:

    我的一个建议是创建一个自定义 TimeZoneInfo 并使用它来生成 DateTime 对象

    myTimeZoneInfo = TimeZoneInfo.CreateCustomTimeZone(
                     TimeZoneInfo.Local.Id,
                     TimeZoneInfo.Local.BaseUtcOffset,
                     TimeZoneInfo.Local.DisplayName,
                     TimeZoneInfo.Local.StandardName, "", null, true);
    

    关键参数是true,也就是disableDaylightSavingsTime

    用法:

    DateTime noDaylightSavings = TimeZoneInfo.ConvertTimeFromUtc(reading, myTimeZoneInfo);
    

    DateTime noDaylightSavings = TimeZoneInfo.ConvertTimeToUtc(reading, myTimeZoneInfo);
    

    【讨论】:

      【解决方案3】:

      我最近这样做是为了在远程计算亚利桑那州的当前时间时删除 DST。就我而言,我在数据库中按邮政编码有一个 DST (y/n) 标志。对于使用上述代码(示例 #2)删除 DST 调整的任何人,您可能需要限定您打算应用的调整的时间范围,因为一些调整存在但已过期(我认为可能是未来日期当变化即将来临)。以下是对我有用的示例:

      private DateTime RemoveDSTFromDateTime(DateTime dateTime, TimeZoneInfo timeZoneInfo)
      {
          #region detail...
          //removes daylight savings time adjustment as defined in current year
          int _currYear = DateTime.Now.Year;
      
          if (!dateTime.IsDaylightSavingTime())
              return dateTime;
      
          var result = dateTime;
      
          foreach (var adjustmentRule in timeZoneInfo.GetAdjustmentRules())
          {
              if (adjustmentRule.DateStart.Year <= _currYear && adjustmentRule.DateEnd.Year >= _currYear)
              {
                  result = result.Subtract(adjustmentRule.DaylightDelta);
              }
          }
      
          return result; 
          #endregion
      }
      

      【讨论】:

      • DateTime.NowdateTime.IsDaylightSavingTime 都将使用 local 时区 - 而不是您使用 timeZoneInfo 参数传入的时区。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-09-06
      • 2020-02-24
      • 2017-12-11
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多