从DateTime中删除DST

Cri*_*scu 5 c# datetime dst c#-4.0

我已经看到许多网站(通常是论坛)允许用户通过选择以下内容来指定他们的TimeZone首选项:

  • TimeZone
  • 是否使用夏令时

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

如何在C#中实现"不使用DST"部分?

在这一点下面,我介绍了我到目前为止所做的工作,但感觉很烦,而且我想知道是否有更清洁/更好的方法.

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

[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);
}
Run Code Online (Sandbox Code Playgroud)

该测试通过,表明:

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

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

所以我编写了以下方法,如果DateTime对象受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;
}
Run Code Online (Sandbox Code Playgroud)

返回到原始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);
}
Run Code Online (Sandbox Code Playgroud)

此测试也通过,表明现在DST的效果被取消(无论一年中的哪个时间,我们总是得到UTC + 2h).

写下来时,我得到了另一个似乎有用的想法:不使用任何TimeZoneInfo.Convert...()方法,只需添加roTimezone.BaseUtcOffset到UTC日期即可.

任何人都可以指出这样做的正确方法是什么?

Mat*_*int 5

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

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

  2. 采用 UTCDateTime并添加 ,BaseUtcOffset以便您始终处理标准时间。

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

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

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

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

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