程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> 關於.NET >> .NET Framework 4中的時間間隔格式設置和分析

.NET Framework 4中的時間間隔格式設置和分析

編輯:關於.NET

在 Microsoft .NET Framework 4 中,TimeSpan 結構通過增加格式設置和分析支持得到改進,這種支 持可與 DateTime 值的格式設置和分析支持相媲美。在本文中,我將探討新增的格式設置和分析功能,並 提供 TimeSpan 值使用方面的一些實用提示。

.NET Framework 3.5 及更早版本中的格式設置

在 Microsoft .NET Framework 3.5 及更早版本中,用於時間間隔的唯一格式設置方法是無參數 TimeSpan.ToString 方法。返回字符串的具體格式取決於 TimeSpan 值。其中至少包含 TimeSpan 值的小 時、分鐘和秒組成部分。如果是非零值,還包含日組成部分。此外,如果存在小數秒組成部分,則還將包 含計時周期組成部分的所有七位數字。句點(“.”)用作日與小時之間以及秒與小數秒之間的分隔符。

.NET Framework 4 中的擴展格式設置支持

在 .NET Framework 4 中,默認 TimeSpan.ToString 方法的行為沒有變化,但現在新增了兩個重載。 第一個重載只有一個參數,該參數可以是標准或自定義格式字符串,用於定義結果字符串的格式。第二個 重載有兩個參數:一個標准或自定義格式字符串和一個 IFormatProvider 實現,後者表示提供格式設置 信息的區域性。順便說一下,此方法為 TimeSpan 結構提供了 IFormattable 實現,它使 TimeSpan 值可 用於支持復合格式設置的 String.Format 等方法。

除了包含標准或自定義格式字符串以及提供 IFormattable 實現之外,現在設置了格式的字符串還可 以區分區域性。兩個標准格式字符串“g”(一般短格式說明符)和“G”(一般長格式說明符)在結果字 符串中使用當前區域性或特定區域性的格式設置約定。圖 1 中的示例格式對此進行了演示,該示例顯示 的時間間隔結果字符串使用“G”格式字符串以及 en-US 和 fr-FR 區域性設置了格式。

圖 1 使用“G”格式字符串設置格式的時間間隔 (VB)

Visual Basic
Imports System.Globalization

Module Example
   Public Sub Main()
    Dim interval As New TimeSpan(1, 12, 42, 30, 566)
    Dim cultures() As CultureInfo = { New CultureInfo("en-US"),
                     New CultureInfo("fr-FR") }
    For Each culture As CultureInfo In cultures
      Console.WriteLine("{0}: {1}", culture, interval.ToString(
       "G", culture))
    Next
   End Sub
End Module

圖 1 使用“G”格式字符串設置格式的時間間隔 (C#)

using System;
using System.Globalization;

public class Example
{
   public static void Main()
   {
    TimeSpan interval = new TimeSpan(1, 12, 42, 30, 566);
    CultureInfo[] cultures = { new CultureInfo("en-US"),
                  new CultureInfo(“"fr-FR") };
    foreach (CultureInfo culture in cultures)
      Console.WriteLine("{0}: {1}", culture, interval.ToString( _
       "G", culture));
   }
}

圖 1 中的示例顯示以下輸出:

en-US:1:12:42:30.5660000

fr-FR:1:12:42:30,5660000

.NET Framework 3.5 及更早版本中的分析

在 .NET Framework 3.5 及更早版本中,由靜態 System.TimeSpan.Parse 和 System.TimeSpan.TryParse 方法處理對時間間隔的分析支持,這兩個方法支持數量有限的固定格式。圖 2 中的示例分析某一時間間隔的字符串表示形式,該時間間隔采用了前述方法可識別的每種格式。

圖 2 分析多種格式的時間間隔字符串 (VB)

Module Example
   Public Sub Main()
    Dim values() As String = {"12", "12.16:07", "12.16:07:32", _
                 "12.16:07:32.449", "12.16:07:32.4491522",
_
                 "16:07", "16:07:32", "16:07:32.449" }

    For Each value In values
      Try
       Console.WriteLine("Converted {0} to {1}", _
                value, TimeSpan.Parse(value))
      Catch e As OverflowException
       Console.WriteLine("Overflow: {0}", value)
      Catch e As FormatException
       Console.WriteLine("Bad Format: {0}", value)
      End Try
    Next
   End Sub

圖 2 分析多種格式的時間間隔字符串 (C#)

using System;

public class Example
{
   public static void Main()
   {
    string[] values = { "12", "12.16:07", "12.16:07:32",
              "12.16:07:32.449", "12.16:07:32.4491522",
              "16:07", "16:07:32", "16:07:32.449" };

    foreach (var value in values)
      try {
       Console.WriteLine("Converted {0} to {1}",
                value, TimeSpan.Parse(value));}
      catch (OverflowException) {
       Console.WriteLine("Overflow: {0}", value); }
      catch (FormatException) {
       Console.WriteLine("Bad Format: {0}", value);
      }
   }
}

圖 2 中的示例顯示以下輸出:

Converted 12 to 12.00:00:00

Converted 12.16:07 to 12.16:07:00

Converted 12.16:07:32 to 12.16:07:32

Converted 12.16:07:32.449 to 12.16:07:32.4490000

Converted 12.16:07:32.4491522 to 12.16:07:32.4491522

Converted 16:07 to 16:07:00

Converted 16:07:32 to 16:07:32

Converted 16:07:32.449 to 16:07:32.4490000

如輸出所示,該方法可以分析單個整數,將該整數解釋為時間間隔中的天數(稍後將對此進行詳細的 說明)。若非單個整數,則要分析的字符串至少須包含小時值和分鐘值。

.NET Framework 4 中的擴展分析支持

在 .NET Framework 4 和 Silverlight 4 中,對時間間隔字符串表示形式的分析支持已得到增強,現 可與日期和時間字符串的分析支持相媲美。TimeSpan 結構現在為 Parse 和 TryParse 方法提供了新的重 載,並提供了全新的 ParseExact 和 TryParseExact 方法,後兩個方法各有四個重載。這些分析方法支 持標准和自定義格式字符串,並為區分區域性的格式設置提供一定的支持。兩個標准格式字符串(“g” 和 “G”)是區分區域性的,而其余的標准格式字符串(“c”、“t”和“T”)以及所有自定義格式字 符串都是固定的。在 .NET Framework 的未來版本中,將會進一步增強對時間間隔的分析和格式設置支持 。

圖 3 中的示例演示了在 .NET Framework 4 中如何使用 ParseExact 方法分析時間間隔數據。它定義 了一個數組,其中包含七個自定義格式字符串;如果要分析的時間間隔字符串表示形式與其中所有格式都 不相符,該方法就會失敗並引發異常。

圖 3 使用 ParseExact 方法分析時間間隔數據 (VB)

Module modMain
   Public Sub Main()
    Dim formats() As String = { "hh", "%h", "h\:mm", "hh\:mm",
                  "d\.hh\:mm\:ss", "fffff", "hhmm" }
    Dim values() As String = { '16", "1", "16:03", "1:12",
                  "1.13:34:15", "41237", "0609" }
    Dim interval As TimeSpan

    For Each value In values
      Try
       interval = TimeSpan.ParseExact(value, formats, Nothing)
       Console.WriteLine("Converted '{0}' to {1}",
                value, interval)
      Catch e As FormatException
       Console.WriteLine("Invalid format: {0}", value)
      Catch e As OverflowException
       Console.WriteLine("Overflow: {0}", value)
      Catch e As ArgumentNullException
       Console.WriteLine("No string to parse")
      End Try
    Next
   End Sub
End Module

圖 3 使用 ParseExact 方法分析時間間隔數據 (C#)

using System;

public class Example
{
   public static void Main()
   {
    string[] formats = { "hh", "%h", @"h\:mm", @"hh\:mm",
               @"d\.hh\:mm\:ss", "fffff", "hhmm" };
    string[] values = { "16", "1", "16:03", '1:12',
              "1.13:34:15", "41237", "0609" };
    TimeSpan interval;

    foreach (var value in values)
    {
      try {
       interval = TimeSpan.ParseExact(value, formats, null);
       Console.WriteLine("Converted '{0}' to {1}", value,
                interval); }
      catch (FormatException) {
       Console.WriteLine("Invalid format: {0}", value); }
      catch (OverflowException) {
       Console.WriteLine("Overflow: {0}", value); }
      catch (ArgumentNullException) {
       Console.WriteLine("No string to parse");
      }
    }
   }
}

圖 3 中的示例顯示以下輸出:

Converted ‘16’ to 16:00:00

Converted ‘1’ to 01:00:00

Converted ‘16:03’ to 16:03:00

Converted ‘1:12’ to 01:12:00

Converted ‘1.13:34:15’ to 1.13:34:15

Converted ‘41237’ to 00:00:00.4123700

Converted ‘0609’ to 06:09:00

使用單個數值實例化 TimeSpan

有趣的是,如果在 .NET Framework 的任何版本中向 TimeSpan.Parse(String) 方法傳遞這七個時間 間隔字符串,則這些字符串的分析將全部成功,但其中四個字符串會返回不同的結果。對這些字符串調用 TimeSpan.Parse(String) 將產生以下輸出:

Converted ‘16’ to 16.00:00:00

Converted ‘1’ to 1.00:00:00

Converted ‘16:03’ to 16:03:00

Converted ‘1:12’ to 01:12:00

Converted ‘1.13:34:15’ to 1.13:34:15

Converted ‘41237’ to 41237.00:00:00

Converted ‘0609’ to 609.00:00:00

TimeSpan.Parse(String) 與 TimeSpan.ParseExact(String, String[], IFormatProvider) 方法調用 的主要區別在於對表示整數值的字符串的處理方式。TimeSpan.Parse(String) 方法將此類字符串解釋為 日。TimeSpan.ParseExact(String, String[], IFormatProvider) 方法對整數的解釋取決於在字符串數 組參數中提供的自定義格式字符串。在此示例中,只有一個或兩個整數位的字符串將解釋為小時數,具有 四個整數位的字符串將解釋為小時數和分鐘數,而具有五個整數位的字符串將解釋為秒的小數。

在許多情況下,.NET Framework 應用程序以任意格式接收包含時間間隔數據的字符串(如表示毫秒數 的整數或表示小時數的整數)。在以前的 .NET Framework 版本中,須將這樣的數據處理成可接受的格式 ,然後才能傳遞給 TimeSpan.Parse 方法。在 .NET Framework 4 中,可以使用自定義格式字符串定義對 只包含整數的時間間隔字符串的解釋,而無需預先處理字符串數據。圖 4 中的示例為一到五位的整數提 供不同的表示形式,從而演示這一點。

圖 4 1 到 5 位整數表示形式 (VB)

Module Example
   Public Sub Main()
    Dim formats() As String = { "%h", "hh", "fff", "ffff', 'fffff' }
    Dim values() As String = { "3", "17", "192", "3451",
                  "79123", "01233" }

    For Each value In values
      Dim interval As TimeSpan
      If TimeSpan.TryParseExact(value, formats, Nothing, interval) Then
       Console.WriteLine("Converted '{0}' to {1}",
                value, interval.ToString())
      Else
       Console.WriteLine("Unable to parse {0}.", value)
      End If
    Next
   End Sub
End Module

圖 4 1 到 5 位整數表示形式 (C#)

using System;

public class Example
{
   public static void Main()
   {
    string[] formats = { "%h", "hh", "fff", "ffff", "fffff" };
    string[] values = { "3", "17", "192", "3451", "79123",  "01233" };

    foreach (var value in values)
    {
      TimeSpan interval;
      if (TimeSpan.TryParseExact(value, formats, null, out interval))
       Console.WriteLine("Converted '{0}' to {1}",
                value, interval.ToString());
      else
       Console.WriteLine("Unable to parse {0}.", value);
    }
   }
}

圖 4 中的示例顯示以下輸出:

Converted ‘3’ to 03:00:00

Converted ‘17’ to 17:00:00

Converted ‘192’ to 00:00:00.1920000

Converted ‘3451’ to 00:00:00.3451000

Converted ‘79123’ to 00:00:00.7912300

Converted ‘01233’ to 00:00:00.0123300

處理分析時間間隔時產生的 OverflowException

.NET Framework 4 中所引入的這些新 TimeSpan 格式設置和分析功能保留了一個可能會對某些用戶造 成不便的行為。為了向後兼容,在以下情況下 TimeSpan parsing 方法會引發 OverflowException:

如果小時組成部分的值超過 23。

如果分鐘組成部分的值超過 59。

如果秒組成部分的值超過 59。

可以通過多種方式來處理此異常。可以使用 Int32.Parse 方法(而不是調用 TimeSpan.Parse 方法) 將各字符串組成部分轉換為整數值,然後將其傳遞給某一 TimeSpan 類構造函數。與 TimeSpan 分析方法 不同,TimeSpan 構造函數不會在傳遞給該構造函數的小時、分鐘或秒值超出范圍時引發 OverflowException。

這是一種可接受的解決方法,但它也存在一個限制:它要求在調用 TimeSpan 構造函數之前分析所有 字符串並將其轉換為整數。如果要分析的大部分數據在分析操作期間都不會溢出,則此解決方法會導致不 必要的處理。

另一種解決方法是先嘗試分析數據,然後再處理在時間間隔的各組成部分超出范圍時引發的 OverflowException。這也是一種可接受的解決方法,但應用程序中的不必要異常處理可能會產生昂貴的 開銷。

最佳解決方法是先使用 TimeSpan.TryParse 方法分析數據,然後僅當該方法返回 false 時再處理時 間間隔的各組成部分。如果分析操作失敗,則可以使用 String.Split 方法將時間間隔的字符串表示形式 拆分為各組成部分,然後將其傳遞給 TimeSpan(Int32, Int32, Int32, Int32, Int32) 構造函數。圖 5 中的示例提供了一個簡單實現:

圖 5 處理非標准時間間隔字符串 (VB)

Module Example
   Public Sub Main()
    Dim values() As String = { "37:16:45.33", "0:128:16.324",
                  "120:08" }
    Dim interval As TimeSpan
    For Each value In values
      Try
       interval = ParseIntervalWithoutOverflow(value)
       Console.WriteLine("'{0}' --> {1}", value, interval)
      Catch e As FormatException
       Console.WriteLine("Unable to parse {0}.", value)
      End Try
    Next
   End Sub

   Private Function ParseIntervalWithoutOverflow(value As String)
           As TimeSpan
    Dim interval As TimeSpan
    If Not TimeSpan.TryParse(value, interval) Then
      Try
       ‘ Handle failure by breaking string into components.
       Dim components() As String = value.Split( {"."c, ":"c } )
       Dim offset As Integer = 0 
       Dim days, hours, minutes, seconds, milliseconds As Integer
       ‘ Test whether days are present.
       If value.IndexOf(".") >= 0 AndAlso 
            value.IndexOf(".") < value.IndexOf(":") Then
         offset = 1
         days = Int32.Parse(components(0))
       End If
       ‘ Call TryParse to parse values so no exceptions result.
       hours = Int32.Parse(components(offset))
       minutes = Int32.Parse(components(offset + 1))
       If components.Length >= offset + 3 Then
         seconds = Int32.Parse(components(offset + 2))
       End If
       If components.Length >= offset + 4 Then
         milliseconds = Int32.Parse(components(offset + 3))
       End If
       ‘ Call constructor.
       interval = New TimeSpan(days, hours, minutes,
                   seconds, milliseconds)
      Catch e As FormatException
       Throw New FormatException(
            String.Format("Unable to parse '{0}'"), e)
      Catch e As ArgumentOutOfRangeException
       Throw New FormatException(
            String.Format("Unable to parse '{0}'"), e)
      Catch e As OverflowException
       Throw New FormatException(
            String.Format("Unable to parse '{0}'"), e)
      Catch e As ArgumentNullException
       Throw New ArgumentNullException("value cannot be null.",
                       e)
      End Try
    End If
    Return interval
   End Function
End Module

圖 5 處理非標准時間間隔字符串 (C#)

using System;

public class Example
{
   public static void Main()
   {
    string[] values = { "37:16:45.33", "0:128:16.324", "120:08" };
    TimeSpan interval;
    foreach (var value in values)
    {
      try {
       interval = ParseIntervalWithoutOverflow(value);
       Console.WriteLine("'{0}' --> {1}", value, interval);
      }
      catch (FormatException) {
       Console.WriteLine("Unable to parse {0}.", value);
      }
    }
   }

   private static TimeSpan ParseIntervalWithoutOverflow(string value)
   {
    TimeSpan interval;
    if (! TimeSpan.TryParse(value, out interval))
    {
      try {
       // Handle failure by breaking string into components.
       string[] components = value.Split(
                  new Char[] {'.', ':' } );

       int offset = 0;
       int days = 0;
       int hours = 0;
       int minutes = 0;
       int seconds = 0;
       int milliseconds = 0;
       // Test whether days are present.
       if (value.IndexOf(".") >= 0 &&
            value.IndexOf(".") < value.IndexOf(":"))
       {
         offset = 1;
         days = Int32.Parse(components[0]);
       }
       // Call TryParse to parse values so no exceptions result.
       hours = Int32.Parse(components[offset]);
       minutes = Int32.Parse(components[offset + 1]);
       if (components.Length >= offset + 3)
         seconds = Int32.Parse(components[offset + 2]);

       if (components.Length >= offset + 4)
         milliseconds = Int32.Parse(components[offset + 3]);

       // Call constructor.
       interval = new TimeSpan(days, hours, minutes,
                   seconds, milliseconds);
      }
      catch (FormatException e) {
       throw new FormatException(
            String.Format("Unable to parse '{0}'"), e);
      }
      catch (ArgumentOutOfRangeException e) {
       throw new FormatException(
            String.Format("Unable to parse '{0}'"), e);
      }
      catch (OverflowException e)
      {
       throw new FormatException(
            String.Format("Unable to parse '{0}'"), e);
      }
      catch (ArgumentNullException e)
      {
       throw new ArgumentNullException("value cannot be null.",
                       e);
      }
    }
    return interval;
   }
}

如以下輸出所示,圖 5 中的示例成功處理了四個大於 23 的值,以及大於 59 的分鐘值和秒值:

‘37:16:45.33’ --> 1.13:16:45.0330000

‘0:128:16.324’ --> 02:08:16.3240000

‘120:08’ --> 5.00:08:00

應用程序兼容性

矛盾的是,在 .NET Framework 4 中對 TimeSpan 值的增強格式設置支持破壞了在 .NET Framework 早期版本中對 TimeSpan 值做過格式設置的一些應用程序。例如,下面的代碼在 .NET Framework 3.5 中 可正常執行,但在 .NET Framework 4 中執行時會引發 FormatException:

string result = String.Format("{0:r}", new TimeSpan(4, 23, 17));

為了對參數列表中的每個參數進行格式設置,String.Format 方法將確定該對象是否實現了 IFormattable。如果實現,則會調用該對象的 IFormattable.ToString 實現。如果未實現,則會放棄索 引項中提供的任何格式字符串並調用該對象的無參數 ToString 方法。

在 .NET Framework 3.5 及更早版本中,TimeSpan 未實現 IFormattable,也不支持格式字符串。因 此,將忽略“r”格式字符串,並將調用無參數 TimeSpan.ToString 方法。而在 .NET Framework 4 中, 將調用 TimeSpan.ToString(String, IFormatProvider) 並傳遞不受支持的格式字符串,從而導致該異常 。

如果可能,應修改此代碼使之調用無參數 TimeSpan.ToString 方法,或向格式設置方法傳遞有效的格 式字符串。但是,如果這樣做不可行,可向應用程序的配置文件中添加一個 <TimeSpan_LegacyFormatMode> 元素,類似如下所示:

<?xml version ="1.0"?>
<configuration>
   <runtime>
    <TimeSpan_LegacyFormatMode enabled="true"/>
   </runtime>
</configuration>

通過將其 enabled 特性設置為 true,可確保 TimeSpan 使用舊式格式設置行為。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved