一些特定的日期时间值在调用TimeZoneInfo.ConvertTimeToUtc时会引发异常。

29
当我以这个特定的dt值运行代码时,调用ConvertTimeToUtc方法时会抛出异常。我的本地机器时区ID是“GMT标准时间”。
var tzi = TimeZoneInfo.FindSystemTimeZoneById("Central Standard Time");
var dt = new DateTime(1995, 4, 2, 2, 55, 0);
var t = TimeZoneInfo.ConvertTimeToUtc(dt, tzi);

异常情况是:
System.ArgumentException was unhandled
Message="The supplied DateTime represents an invalid time.  For example, when the clock is adjusted forward, any time in the period that is skipped is invalid.\r\nParameter 
3个回答

25

没错,完全正确。1995年4月4日在中央标准时间上没有2:55am存在,因为挂钟由于夏令时的转换从2点跳到了3点。关于此,该例外情况似乎很明确。(这里“标准”使用有点棘手;称其为“中央时间”将更有意义,包括“中央标准时间”和“中央夏令时”,但这是另一回事。唉,我个人更喜欢Olson标识符...)

在其他情况下,本地时间可能会产生歧义——如果时钟向后拨动一个小时(或更多!),那么当地时间可能会出现两次。

问题是:您希望您的代码在此情况下如何运行?

有点不幸的是,该异常只是ArgumentException - 在Noda Time中,我们将为此情况创建一个异常,以便更容易地发现和捕获它。 (我们还会有类似于IsAmbiguous和IsSkipped的内容,因此您可以在不捕获异常的情况下进行检查。)

但基本信息是,这不是BCL中的漏洞 - 它是有意为之的。


20

可以使用以下方法测试是否为无效时间:

TimeZoneInfo.IsInvalidTime

或者如果它存在歧义使用

TimeZoneInfo.IsAmbiguousTime

如果存在歧义,可以从中检索出可能适用的时间数组:

TimeZoneInfo GetAmbiguousTimeOffsets
在交互式应用程序中,可以提示用户进行澄清。BCL团队就这个主题撰写了一篇很好的博客。https://learn.microsoft.com/en-au/archive/blogs/bclteam/system-timezoneinfo-working-with-ambiguous-and-invalid-points-in-time-josh-free

2

有时候你需要处理一个无效的时间,但你不能抛出错误并要求用户修复它 -- 你[程序员]需要处理它。

如果你可以安全地将时间更改(例如丢失一小时)应用于无效时间,则可以使用此解决方案:

static void Main(string[] args)
{
  // I hard-coded this to Mountain Time.  It works for other timezones too.
  TimeZoneInfo tzInfo = TimeZoneInfo.FindSystemTimeZoneById("Mountain Standard Time");

  string sDate = args[0];                     // get input time (string)
  DateTime dtDate = DateTime.Parse(sDate);    // convert to DateTime
  
  if (tzInfo.IsInvalidTime(dtDate)) {

    // If the input time is invalid, use the offset before and after time changes
    // to calculate how many minutes will be lost when time does change.  Then add
    // that many minutes to 'dtDate'.

    TimeSpan offsetYesterday = tzInfo.GetUtcOffset(dtDate.AddDays(-1)); // before time change
    TimeSpan offsetTomorrow = tzInfo.GetUtcOffset(dtDate.AddDays(1));   // after time change
    double minOffset = offsetTomorrow.TotalMinutes - offsetYesterday.TotalMinutes;

    dtDate = dtDate.AddMinutes(minOffset);
  }
  string utcDate = TimeZoneInfo.ConvertTimeToUtc(dtDate, tzInfo).ToString();
  Console.WriteLine("Converting {0} to UTC = {1}", sDate, utcDate);
}

以下是六个不同输入时间的输出,其中包括两个无效时间。代码通过添加丢失的分钟来调整无效时间,使它们成为用户如果记得时间更改后应该有的时间:
Converting 03-12-2023 01:00:00 to UTC = 3/12/2023 8:00:00 AM
Converting 03-12-2023 01:30:00 to UTC = 3/12/2023 8:30:00 AM
Converting 03-12-2023 02:00:00 to UTC = 3/12/2023 9:00:00 AM // was invalid, added 60 min
Converting 03-12-2023 02:30:00 to UTC = 3/12/2023 9:30:00 AM // was invalid, added 60 min
Converting 03-12-2023 03:00:00 to UTC = 3/12/2023 9:00:00 AM
Converting 03-12-2023 03:30:00 to UTC = 3/12/2023 9:30:00 AM

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接