TZ4NET .NET 时区库中"Source time in transition period."异常意味着什么



我正在使用TZ4Net库http://www.babiej.demon.nl/Tz4Net/main.htm为了在 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,我应该怎么做来处理这种类型的错误?

术语"春季前进"和"后退"是指夏令时的更改。 您可以在 DST 标记维基中阅读更多信息。

在"弹簧前进"转换期间,存在本地时间中不存在的间隙值。 例如,在美国,大多数时区在 3 月的第二个星期日从 1:59:59 跳到 3:00:00。 因此,2:00:00 的时间无效,因为它处于间隙中。

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

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

  • 对于一个落在"春天前进"过渡造成的空白中的时间,这根本不是一个有效的时间。

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

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

  • 对于由于落
  • 入"回退"过渡而模棱两可的时间,您需要确定实际要使用两种可能性中的哪一种。

    • 在许多情况下,您应该提示用户这两个选项,以便他们可以做出决定。 问这个问题,"你的意思是 1:00 EDT (-0400) 还是 1:00 EST (-0500)?

    • 有时你会想为他们挑选。 您可以根据需要选择第一个匹配项或第二个匹配项。

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

如果你想继续使用

奥尔森时区,你可以继续使用TZ4Net,但我也想建议你试试Noda Time。 它是一个社区开发的开源项目(而不是单一作者),其首席开发人员是Jon Skeet。 您将对DST转换有相同的顾虑,但是Noda Time的API将迫使您预先处理这些问题,而不必在部署应用程序后找出答案。

如果您只想保留您今天拥有的内容,您可以按如下方式修改上述函数以处理回退转换:

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

    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;
    }
    

对于弹簧前向过渡:

  • 您可能应该保留以下内容:

    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;
    }
    
case TimeCheckResult.InSpringForwardGap :
case TimeCheckResult.InFallBackRange :

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

TZ4NET 作者的回应

嗨马修,

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

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