Il presente articolo è stato tradotto automaticamente.

Tutto su CLR

Intervalli di tempo per formattazione e analisi in .NET Framework 4

Ron Petrusha

In Microsoft .NET Framework 4 TimeSpan struttura è stata migliorata mediante l'aggiunta di supporto per la formattazione e l'analisi che è paragonabile al supporto per valori DateTime formattazione e analisi. In questo articolo verrà survey la nuova formattazione e le funzionalità di analisi, nonché fornire alcuni suggerimenti utili per l'utilizzo di valori TimeSpan.

Formattazione di .NET Framework 3.5 e versioni precedenti

In Microsoft .NET Framework 3.5 e versioni precedenti, l'unico metodo di formattazione per intervalli di tempo è il metodo TimeSpan.ToString senza parametri. Il formato esatto della stringa restituita dipende dal valore TimeSpan. Come minimo, include i componenti di un valore TimeSpan ore, minuti e secondi. Se è diverso da zero, il componente giorno è anche incluso. E se è presente un componente secondi frazionario, vengono incluse tutti i sette cifre del componente segni di graduazione. Il periodo (“. ”) viene utilizzato come separatore tra giorni e ore e tra secondi e frazioni di secondo.

Supporto esteso per la formattazione in .NET Framework 4

Mentre il metodo TimeSpan.ToString predefinito si comporta in modo identico in .NET Framework 4, esistono altri due overload. Il primo ha un solo parametro, può essere una stringa di formato standard o personalizzato che definisce il formato della stringa di risultato. Il secondo ha due parametri: uno standard o la stringa di formato personalizzato e un'implementazione IFormatProvider che rappresenta la lingua che fornisce informazioni di formattazione. Questo metodo, per inciso, fornisce l'implementazione IFormattable per la struttura TimeSpan, ma consente valori TimeSpan da utilizzare con i metodi, ad esempio String.Format, che supportano la formattazione composita.

Oltre a includere le stringhe di formato standard e personalizzati e fornendo un'implementazione IFormattable, stringhe formattate ora possono essere sensibile alle impostazioni cultura. Due stringhe di formato standard, “ g ” (l'identificatore generico di formato breve) e “ G ” (l'identificatore generico di formato lungo) di utilizzare le convenzioni di formattazione delle impostazioni cultura correnti o una lingua specifica nella stringa di risultati. Formati di esempio in Nella figura 1 forniscono un'illustrazione visualizzando la stringa di risultati per un intervallo formattato utilizzando “ G ” formato stringa e le lingue en-us e fr-FR di tempo.

Figura 1 Intervallo di tempo formattati utilizzando stringhe di formato “ 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

Figura 1 Intervallo di tempo formattati utilizzando stringhe di formato “ 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));
   }
}

Nell'esempio riportato in Nella figura 1 viene visualizzato il seguente output:

  en-US: 1:12:42:30.5660000
  FR-FR: 1:12:42:30,5660000

L'analisi in .NET Framework 3.5 e versioni precedenti

In .NET Framework 3.5 e versioni precedenti, supporto per l'analisi di intervalli di tempo viene gestita dai System.TimeSpan.Parse e System.TimeSpan.TryParse metodi static, che supporta un numero limitato di formati invarianti. Nell'esempio riportato in Nella figura 2 analizza la rappresentazione in forma di stringa di un intervallo di tempo in ogni formato riconosciuto dal metodo.

Nella figura 2 L'analisi di stringa di intervallo di tempo in più formati (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

Nella figura 2 L'analisi di stringa di intervallo di tempo in più formati (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);
         }
   }
}

Nell'esempio riportato in Nella figura 2 viene visualizzato il seguente output:

Convertito 12 per 12.00:00:00
Convertito 12.16:07 per 12.16:07:00
Convertito 12.16:07:32 per 12.16:07:32
Convertito 12.16:07:32.449 per 12.16:07:32.4490000
Convertito 12.16:07:32.4491522 per 12.16:07:32.4491522
Convertito: 07:16 per 16:07:00
Convertito 16:07:32 per 16:07:32
Convertito 16:07:32.449 a 16:07:32.4490000

Come illustrato nell'output, il metodo consente di analizzare un numero intero, che interpreta come il numero di giorni in un intervallo di tempo (ulteriori informazioni, vedere più avanti). In caso contrario, è necessario che la stringa da analizzare includa almeno un'ora e un valore in minuti.

Supporto esteso per l'analisi in .NET Framework 4

In .NET Framework 4 e 4 di Silverlight, il supporto per l'analisi le rappresentazioni di stringa di intervalli di tempo è stato migliorato e ora è paragonabile per il supporto per stringhe di analisi di data e ora. Struttura TimeSpan include ora un nuovo overload per i metodi Parse e TryParse, nonché metodi ParseExact e TryParseExact completamente nuovi, ognuna delle quali dispone di quattro overload. Questi metodi di analisi supportano stringhe di formato standard e personalizzati e offrono un supporto per la formattazione specifiche delle impostazioni cultura. Due stringhe di formato standard (“ g ” e “ G ”) sono sensibili alle impostazioni cultura, mentre le altre stringhe di formato standard (“ c ”, “ t ” e “ T ”) come stringhe di formato personalizzate nonché tutti i sono invariabili. Supporto per l'analisi e formattazione ora intervalli verranno ulteriormente migliorato nelle future versioni di .NET Framework.

Nell'esempio riportato in Nella figura 3 viene illustrato come utilizzare il metodo ParseExact per analizzare i dati di intervallo di tempo in .NET Framework 4. Definisce una matrice di sette stringhe di formato personalizzato, se la rappresentazione in forma di stringa dell'intervallo di tempo da analizzare non è conforme a uno di questi formati, il metodo ha esito negativo e verrà generata un'eccezione.

Nella figura 3 L'analisi di dati Interval ora con il metodo 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

Nella figura 3 L'analisi di dati Interval ora con il metodo 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");
         }         
      }
   }
}

Nell'esempio riportato in Nella figura 3 viene visualizzato il seguente output:

Convertito ‘ 16 ’ per 16: 00: 00
Convertito ‘1’ per 01:00:00
Convertito ‘16.03 ’ su 16: 03: 00
Convertito ‘1:12’ per 01:12:00
Convertito ‘1.13:34:15 ’ per 1.13:34:15
Convertito ‘41237’ per 00:00:00.4123700
Convertito ‘ 0609 ’ su 06: 09: 00

Creazione di un'istanza di TimeSpan con un valore numerico singolo

È interessante notare che queste stesse sette tempo intervallo stringhe sono state passate al metodo TimeSpan.Parse(String) in qualsiasi versione di .NET Framework, sarebbe tutto analizzare correttamente, ma in quattro casi, restituirà un risultato diverso. Chiamata TimeSpan.Parse(String) con queste stringhe produce il seguente output:

Convertito ‘ 16 ’ per 16.00:00:00
Convertito ‘1’ per 1.00:00:00
Convertito ‘ 16.03 ’ su 16: 03: 00
Convertito ‘1:12’ per 01:12:00
Convertito ‘ 1.13:34:15 ’ per 1.13:34:15
Convertito ‘41237’ per 41237.00:00:00
Convertito ‘0609’ per 609.00:00:00

La principale differenza nelle chiamate di metodo TimeSpan.Parse(String) e TimeSpan.ParseExact(String, String[], IFormatProvider) risiede nella gestione di stringhe che rappresentano i valori integer. Il metodo TimeSpan.Parse(String) li interpreta come giorni. L'interpretazione dei valori integer per TimeSpan.ParseExact (String, String [], IFormatProvider) metodo dipende dalle stringhe di formato personalizzato fornite nel parametro della matrice di stringa. In questo esempio, che dispongono solo di uno o due cifre integer vengono interpretate come il numero di ore, con quattro cifre vengono interpretate come il numero di ore e minuti e che dispongono di cinque cifre integer vengono interpretate come un numero di secondi frazionario.

In molti casi, le applicazioni .NET Framework ricevano stringhe che contengono dati di intervallo di tempo in un formato arbitrario (ad esempio, che rappresenta un numero di millisecondi interi o valori integer che rappresenta un numero di ore). Nelle versioni precedenti di .NET Framework, era necessario manipolare i dati in modo che sarebbe in un formato accettabile prima di passarlo al metodo TimeSpan.Parse. In .NET Framework 4, è possibile utilizzare stringhe di formato personalizzato per definire l'interpretazione delle stringhe di intervallo di tempo che contengono solo valori integer e la modifica preliminare dei dati di stringa non è necessaria. Nell'esempio riportato in Nella figura 4 illustra questo fornendo rappresentazioni diverse per i valori integer sono da uno a cinque cifre.

Nella figura 4 Rappresentazioni di valori integer con 1 a 5 cifre (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

Nella figura 4 Rappresentazioni di valori integer con 1 a 5 cifre (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);    
      }
   }
}

Nell'esempio riportato in Nella figura 4 viene visualizzato il seguente output:

Convertito ‘3’ per 03:00:00
Convertito ‘17’ per 17:00:00
Convertito ‘192’ per 00:00:000.1920000
Convertito ‘ 3451 ’ per 00:00:000,3451000
Convertito ‘ 79123 ’ per 00:00:000,7912300
Convertito ‘ 01233 ’ per 00:00:00.0123300

La gestione OverflowExceptions all'analisi di intervalli di tempo

Questi nuovi TimeSpan analisi e la formattazione delle funzionalità introdotte in .NET Framework 4 conservano un comportamento che alcuni clienti hanno poco pratica. Per garantire la compatibilità con le versioni precedenti, i metodi di analisi TimeSpan generano un'eccezione OverflowException in presenza delle seguenti condizioni:

  • Se il valore del componente ore supera 23.
  • Se il valore del componente minuti maggiore di 59.
  • Se il valore del componente secondi maggiore di 59.

Esistono numerosi modi per gestire questa. Invece di chiamare il metodo TimeSpan.Parse, è possibile utilizzare il metodo Int32.Parse per convertire i componenti di stringa singolo in valori integer, che è quindi possibile passare a uno dei costruttori della classe TimeSpan. A differenza di TimeSpan l'analisi di metodi, costruttori di TimeSpan non generano un'eccezione OverflowException se passato al costruttore il valore ore, minuti o secondi non rientra nell'intervallo.

Si tratta di una soluzione accettabile, anche se è disponibile una limitazione: È necessario che tutte le stringhe di essere analizzate e convertite in valori integer prima di chiamare il costruttore TimeSpan. Se la maggior parte dei dati da analizzare non un overflow durante l'operazione di analisi, questa soluzione prevede l'elaborazione non necessaria.

Un'altra alternativa consiste nel tentare di analizzare i dati e quindi gestire l'OverflowException viene generata quando i componenti di intervallo di tempo singoli non sono compresi nell'intervallo. Anche questa è una soluzione accettabile anche se in un'applicazione per la gestione delle eccezioni non necessari può essere costoso.

La soluzione migliore consiste nell'utilizzare il metodo TimeSpan.TryParse inizialmente analizzare i dati e quindi modificare il tempo di singoli intervallo componenti solo se il metodo restituisce false. Se l'operazione di analisi ha esito negativo, è possibile utilizzare il metodo String.Split per separare la rappresentazione in forma di stringa dell'intervallo di tempo nei relativi componenti individuali, è quindi possibile passare al costruttore TimeSpan(Int32, Int32, Int32, Int32, Int32). Nell'esempio riportato in Nella figura 5 fornisce un'implementazione semplice:

Nella figura 5 La gestione delle stringhe di intervallo temporale non standard (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

Nella figura 5 La gestione delle stringhe di intervallo temporale non standard (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;
   }   
}

Come illustrato nell'output seguente, l'esempio in Nella figura 5 gestisce correttamente i valori di ora sono superiori a 23, come pure minuto e secondo i valori siano a 59:

  ‘37:16:45.33’ --> 1.13:16:45.0330000
  ‘0:128:16.324’ --> 02:08:16.3240000
  ‘120:08’ --> 5.00:08:00

Compatibilità delle applicazioni

Paradossalmente, supporto avanzato di formattazione per valori TimeSpan in .NET Framework 4 è suddiviso in alcune applicazioni che vengono formattati valori TimeSpan nelle versioni precedenti di .NET Framework. Il codice riportato di seguito, ad esempio, viene eseguito normalmente in .NET Framework 3.5, ma genera un FormatException in .NET Framework 4:

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

Per formattare ogni argomento nel relativo elenco di parametri, il metodo String.Format determina se l'oggetto implementa IFormattable. In caso affermativo, chiama l'implementazione IFormattable.ToString dell'oggetto. Se non esiste, ignora qualsiasi stringa di formato fornito nella voce di indice e chiama il metodo ToString privo di parametri dell'oggetto.

In .NET Framework 3.5 e versioni precedenti, TimeSpan non implementa IFormattable, né esso viene supportano stringhe di formato. Di conseguenza, la stringa di formato “ r ” viene ignorata e viene chiamato il metodo TimeSpan.ToString senza parametri. In .NET Framework 4, invece, TimeSpan.ToString (String, IFormatProvider) è chiamato e passata la stringa di formato non supportato, che causa l'eccezione.

Se possibile, questo codice deve essere modificato chiamando il metodo TimeSpan.ToString senza parametri oppure passando una stringa di formato valido per un metodo di formattazione. Se non è possibile, tuttavia, un elemento <TimeSpan_LegacyFormatMode> può essere aggiunto al file di configurazione dell'applicazione in modo che risulti simile al seguente:

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

Impostando l'attributo enabled su true, è possibile garantire che TimeSpan utilizza il comportamento della formattazione legacy.

Ron Petrusha è un programming writer nel team di .NET Framework Base Class Library. È anche autore di numerosi libri e articoli su Windows per la programmazione, programmazione e VB.NET di programmazione Web di programmazione.

Grazie ai seguenti esperti tecnici per la revisione di questo articolo: Melitta Andersen e Josh Free