我看到许多网站(通常是论坛)允许用户通过选择来指定他们的时区偏好:
- 时区
- 是否使用夏令时
据我所知,在进行转换时,.NET 总是将 DST 考虑在内,所以问题是:
“不使用夏令时”部分如何在 C# 中实现?
在这一点之下,我介绍了我到目前为止所做的事情,但感觉很老套,我想知道是否有更清洁/更好的方法。
首先,为了确保自动应用 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);
}
该测试通过,表明:
- 当不适用 DST 时(例如在冬季),罗马尼亚时间为 UTC + 2 小时
- 当 DST 应用时(例如在夏季),罗马尼亚时间为 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;
}
回到最初的 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 日期即可。
谁能指出这样做的正确方法是什么?