如何:让用户解析不明确的时间

不明确时间是指映射到多个协调世界时 (UTC) 的时间。 在向后调整时钟时间时,例如从时区的夏令时调整到标准时间这段转换期间,便会出现不明确时间。 在处理不明确时间时,可执行以下任一操作:

  • 如果不明确时间是用户输入的数据项,则可以让用户自行解决。

  • 假设一下时间如何映射到 UTC。 例如,可以假定不明确时间始终以时区的标准时间表示。

本主题介绍如何让用户解析不明确时间。

让用户解决不明确时间

  1. 获取用户输入的日期和时间。

  2. 调用 IsAmbiguousTime 方法来确定时间是否不明确。

  3. 如果时间不明确,请调用 GetAmbiguousTimeOffsets 方法来检索 TimeSpan 对象的数组。 数组中的每个元素都包含一个 UTC 时差,不明确时间可以映射到该 UTC 时差。

  4. 让用户选择所需时差。

  5. 用本地时间减去用户所选时差,得出 UTC 日期和时间。

  6. 调用 static(Visual Basic 中的 SharedSpecifyKind 方法,将 UTC 日期和时间值的 Kind 属性设置为 DateTimeKind.Utc

示例

以下示例将提示用户输入日期和时间,如果时间不明确,会让用户选择不明确时间映射到的 UTC 时间。

private void GetUserDateInput()
{
    // Get date and time from user
    DateTime inputDate = GetUserDateTime();
    DateTime utcDate;

    // Exit if date has no significant value
    if (inputDate == DateTime.MinValue) return;

    if (TimeZoneInfo.Local.IsAmbiguousTime(inputDate))
    {
        Console.WriteLine("The date you've entered is ambiguous.");
        Console.WriteLine("Please select the correct offset from Universal Coordinated Time:");
        TimeSpan[] offsets = TimeZoneInfo.Local.GetAmbiguousTimeOffsets(inputDate);
        for (int ctr = 0; ctr < offsets.Length; ctr++)
        {
            Console.WriteLine($"{ctr}.) {offsets[ctr].Hours} hours, {offsets[ctr].Minutes} minutes");
        }
        Console.Write("> ");
        int selection = Convert.ToInt32(Console.ReadLine());

        // Convert local time to UTC, and set Kind property to DateTimeKind.Utc
        utcDate = DateTime.SpecifyKind(inputDate - offsets[selection], DateTimeKind.Utc);

        Console.WriteLine($"{inputDate} local time corresponds to {utcDate} {utcDate.Kind.ToString()}.");
    }
    else
    {
        utcDate = inputDate.ToUniversalTime();
        Console.WriteLine($"{inputDate} local time corresponds to {utcDate} {utcDate.Kind.ToString()}.");
    }
}

private static DateTime GetUserDateTime()
{
    // Flag to exit loop if date is valid.
    bool exitFlag = false;
    string? dateString;
    DateTime inputDate = DateTime.MinValue;

    Console.Write("Enter a local date and time: ");
    while (!exitFlag)
    {
        dateString = Console.ReadLine();
        if (dateString?.ToUpper() == "E")
            exitFlag = true;

        if (DateTime.TryParse(dateString, out inputDate))
            exitFlag = true;
        else
            Console.Write("Enter a valid date and time, or enter 'e' to exit: ");
    }

    return inputDate;
}
Private Sub GetUserDateInput()
    ' Get date and time from user
    Dim inputDate As Date = GetUserDateTime()
    Dim utcDate As Date

    ' Exit if date has no significant value
    If inputDate = Date.MinValue Then Exit Sub

    If TimeZoneInfo.Local.IsAmbiguousTime(inputDate) Then
        Console.WriteLine("The date you've entered is ambiguous.")
        Console.WriteLine("Please select the correct offset from Universal Coordinated Time:")
        Dim offsets() As TimeSpan = TimeZoneInfo.Local.GetAmbiguousTimeOffsets(inputDate)
        For ctr As Integer = 0 to offsets.Length - 1
            Dim zoneDescription As String
            If offsets(ctr).Equals(TimeZoneInfo.Local.BaseUtcOffset) Then
                zoneDescription = TimeZoneInfo.Local.StandardName
            Else
                zoneDescription = TimeZoneInfo.Local.DaylightName
            End If
            Console.WriteLine("{0}.) {1} hours, {2} minutes ({3})", _
                              ctr, offsets(ctr).Hours, offsets(ctr).Minutes, zoneDescription)
        Next
        Console.Write("> ")
        Dim selection As Integer = CInt(Console.ReadLine())

        ' Convert local time to UTC, and set Kind property to DateTimeKind.Utc
        utcDate = Date.SpecifyKind(inputDate - offsets(selection), DateTimeKind.Utc)

        Console.WriteLine("{0} local time corresponds to {1} {2}.", inputDate, utcDate, utcDate.Kind.ToString())
    Else
        utcDate = inputDate.ToUniversalTime()
        Console.WriteLine("{0} local time corresponds to {1} {2}.", inputDate, utcDate, utcDate.Kind.ToString())
    End If
End Sub

Private Function GetUserDateTime() As Date
    Dim exitFlag As Boolean = False            ' flag to exit loop if date is valid
    Dim dateString As String
    Dim inputDate As Date = Date.MinValue

    Console.Write("Enter a local date and time: ")
    Do While Not exitFlag
        dateString = Console.ReadLine()
        If dateString.ToUpper = "E" Then exitFlag = True
        If Date.TryParse(dateString, inputDate) Then
            exitFlag = true
        Else
            Console.Write("Enter a valid date and time, or enter 'e' to exit: ")
        End If
    Loop

    Return inputDate
End Function

该示例代码的核心使用一组 TimeSpan 对象,来指示不明确时间与 UTC 之间可能的时差。 但是,这些时差值对用户可能没有什么意义。 为了阐明时差的含义,该代码还会指示时差是表示本地时区的标准时间还是其夏令时。 该代码通过将时差与 BaseUtcOffset 属性的值相比较,确定哪个是标准时间,哪个是夏令时。 此属性指示 UTC 与时区的标准时间之差。

在本示例中,均通过 TimeZoneInfo.Local 属性引用本地时区;绝不会将本地时区分配给对象变量。 这是建议做法,因为调用 TimeZoneInfo.ClearCachedData 方法会使本地时区分配到的任何对象无效。

编译代码

此示例需要:

  • 使用 using 语句导入的 System 命名空间(在 C# 代码中是必需的)。

另请参阅