1

我正在使用来自 http://www.babiej.demon.nl/Tz4Net/main.htm 的 TZ4Net 库,以便在 ASP.NET 中使用准确的时区

我最近开始收到“过渡期的来源时间”。

从这部分代码

static DateTime Convert(DateTime srcTime, string srcName, string dstName)
{
  if (OlsonTimeZone.LookupName(srcName) == null)
{
  throw new ArgumentException("Unknown source timezone name.");
}
if (OlsonTimeZone.LookupName(dstName) == null)
{
  throw new ArgumentException("Unknown destintation timezone name.");
}
OlsonTimeZone srcZone = OlsonTimeZone.GetInstance(srcName);
TimeCheckResult srcCheckRes = srcZone.CheckLocalTime(srcTime);
switch (srcCheckRes)
{
case TimeCheckResult.Valid :
{
  OlsonTimeZone dstZone = OlsonTimeZone.GetInstance(dstName);
  DateTime dstTime = dstZone.ToLocalTime(srcZone.ToUniversalTime(srcTime));
  return dstTime;
}
case TimeCheckResult.InSpringForwardGap :
case TimeCheckResult.InFallBackRange :
{
  throw new ArgumentException("Source time in transition period."); // THIS PART HERE
}
default :
{
  throw new ArgumentException("Source time out of range.");
}
}
}

什么是 TimeCheckResult.InFallBackRange,我应该如何处理此类错误?

4

3 回答 3

1

术语“Spring Forward”和“Fall Back”是指夏令时的更改。您可以在DST 标签 wiki中阅读更多内容。

在“Spring Forward”过渡期间,有一个本地时间不存在的间隙值。例如,在美国,大多数时区在 3 月的第二个星期日从 1:59:59 跳到 3:00:00。所以 2:00:00 的时间是无效的,因为它在间隙中。

在“回退”过渡期间,有一个值范围在本地时间存在两次。例如,在美国,大多数时区从 11 月的第一个星期日的 1:59:59 回到 1:00:00。因此 1:00:00 的时间存在两次,因此对于它可以指代的两个时刻中的哪一个是模棱两可的。

当您尝试从处于过渡期的本地时间进行转换时,您应该执行以下操作:

  • 对于处于“Spring Forward”过渡造成的间隙的时间,那根本不是一个有效的时间。

    • 您可能应该向您的用户显示一条错误消息,以便他们可以输入有效时间。

    • 或者,如果您想假设他们只是忘记调整 DST,您可能希望将时间提前节省量(通常为 1 小时)。这通常用于每天重复发生的事件。

  • 对于由于处于“回退”过渡而模棱两可的时间,您需要确定您真正想要使用的两种可能性中的哪一种。

    • 在许多情况下,您应该提示您的用户两个选择,以便他们做出决定。问一个问题,“您的意思是美国东部时间 1:00 (-0400) 还是美国东部标准时间 1:00 (-0500)?”

    • 有时你会想为他们挑选。您可以根据您的要求选择第一次出现或第二次出现。

TZ4Net 是一个很棒的库,作者 (Zbigniew Babiej) 多年来一直很友好地维护它。但它最初是在 .NET 2.0 左右编写的,因此它不处理DateTimeOffset值。它也不考虑使用.Kind的任何DateTime值的属性。虽然DateTimeKind是在 .Net 2.0 中引入的,但似乎从未将其合并到 TZ4Net 中。因此,您必须非常小心地为其函数提供正确的值。

如果您想继续使用 Olson 时区,可以继续使用 TZ4Net,但我也建议您尝试Noda Time。它是一个社区开发的开源项目(而不是单一作者),其主要开发人员是Jon Skeet。您对 DST 转换也会有同样的顾虑,但 Noda Time 的 API 将迫使您预先处理这些顾虑,而不是在部署应用程序后才发现。

如果你只想保留你今天拥有的东西,你可以修改上面的函数来处理 Fall Back 转换:

  • 假设第一个(日光)实例:

    case TimeCheckResult.InFallBackRange:
    {
        OlsonTimeZone dstZone = OlsonTimeZone.GetInstance(dstName);
        DateTime dstTime = dstZone.ToLocalTime(srcZone.ToUniversalTime(srcTime.AddHours(-1)).AddHours(1));
        return dstTime;
    }
    
  • 假设第二个(标准)实例:

    case TimeCheckResult.InFallBackRange:
    {
        OlsonTimeZone dstZone = OlsonTimeZone.GetInstance(dstName);
        DateTime dstTime = dstZone.ToLocalTime(srcZone.ToUniversalTime(srcTime.AddHours(1)).AddHours(-1));
        return dstTime;
    }
    

对于 Spring Forward 过渡:

  • 你可能应该保留这个:

    case TimeCheckResult.InSpringForwardGap:
        throw new ArgumentException("Source time in transition period.");
    
  • 但是,如果您想假设用户只是忘记提前他们的时钟,您可以这样做以在转换中提前:

    case TimeCheckResult.InSpringForwardGap:
    {
        OlsonTimeZone dstZone = OlsonTimeZone.GetInstance(dstName);
        DateTime dstTime = dstZone.ToLocalTime(srcZone.ToUniversalTime(srcTime.AddHours(1)));
        return dstTime;
    }
    
于 2013-10-30T22:02:42.827 回答
0
case TimeCheckResult.InSpringForwardGap :
case TimeCheckResult.InFallBackRange :

表示时钟在您尝试计算的时间段内向前或向后移动。可能没有定义如何在不同的时间段内处理这个问题。它每年只发生两次;)。处理它实际上取决于您计算时区差异的原因。

于 2013-10-30T16:27:58.503 回答
0

TZ4NET作者的回应

嗨,马修,

这意味着 11/3/2013 1:26:00 在源时区中是一个不明确的时间。例如,在 2013 年 11 月 3 日 02:00 的 America/New_York,时间将设置回 01:00。这意味着时间 01:26 将在当天出现两次,因此在转换过程中不清楚您是指第一次出现还是第二次出现,因为它们将对应于不同的 UTC 时间。我认为最好的办法是检测到这一点,并要求用户详细说明他/她指的是什么时间。如果这不是交互式过程,最简单的方法是检测它并将源时间增加 1 小时,然后从目标时间减去 1 小时。

我希望这能回答你的问题,问候,ZB

于 2013-11-01T12:39:36.627 回答