Procedura: visualizzare le informazioni su data e ora localizzate agli utenti del Web

Poiché una pagina Web può essere visualizzata in qualsiasi parte del mondo, le operazioni che analizzano e formattano valori di data e ora non devono basarsi su un formato predefinito (quasi sempre corrispondente al formato delle impostazioni cultura locali del server Web) in caso di interazione con l'utente. I Web Form che gestiscono le stringhe di data e ora immesse dall'utente devono invece analizzare tali stringhe utilizzando le impostazioni cultura preferite dell'utente. Analogamente, i valori di data e ora devono essere visualizzati in un formato conforme alle impostazioni cultura dell'utente. In questo argomento viene illustrato come eseguire queste operazioni.

Per analizzare le stringhe di data e ora immesse dall'utente

  1. Determinare se la matrice di stringhe restituita dalla proprietà HttpRequest.UserLanguages è compilata. In caso contrario, andare al passaggio 6.

  2. Se la matrice di stringhe restituita dalla proprietà UserLanguages è compilata, recuperarne il primo elemento. Il primo elemento indica la lingua e l'area predefinite o preferite dell'utente.

  3. Creare un'istanza di un oggetto CultureInfo che rappresenta le impostazioni cultura preferite dell'utente chiamando il costruttore CultureInfo.CultureInfo(String, Boolean).

  4. Chiamare il metodo TryParse o il metodo Parse del tipo DateTime o DateTimeOffset per provare la conversione. Utilizzare un overload del metodo TryParse o del metodo Parse con un parametro provider e passare uno degli elementi seguenti:

  5. Se la conversione non riesce, ripetere i passaggi da 2 a 4 per ogni elemento rimanente nella matrice di stringhe restituita dalla proprietà UserLanguages.

  6. Se la conversione ancora non riesce o se la matrice di stringhe restituita dalla proprietà UserLanguages è vuota, analizzare la stringa utilizzando le impostaazioni cultura inglese, restituita dalla proprietà CultureInfo.InvariantCulture.

Per analizzare la data e l'ora locale della richiesta dell'utente

  1. Aggiungere un controllo HiddenField a un Web Form.

  2. Creare una funzione JavaScript che gestisce l'evento onClick di un pulsante Submit scrivendo la data e l'ora corrente e l'offset del fuso orario locale rispetto all'ora UTC (Coordinated Universal Time) nella proprietà Value. Utilizzare un delimitatore, ad esempio un punto e virgola, per separare i due componenti della stringa.

  3. Utilizzare l'evento PreRender del Web Form per inserire la funzione nel flusso di output HTML passando il testo dello script al metodo ClientScriptManager.RegisterClientScriptBlock(Type, String, String, Boolean).

  4. Connettere il gestore eventi all'evento onClick del pulsante Submit specificando il nome della funzione JavaScript per l'attributo OnClientClick del pulsante Submit.

  5. Creare un gestore per l'evento Click del pulsante Submit.

  6. Nel gestore eventi determinare se la matrice di stringhe restituita dalla proprietà HttpRequest.UserLanguages è compilata. In caso contrario, andare al passaggio 14.

  7. Se la matrice di stringhe restituita dalla proprietà UserLanguages è compilata, recuperarne il primo elemento. Il primo elemento indica la lingua e l'area predefinite o preferite dell'utente.

  8. Creare un'istanza di un oggetto CultureInfo che rappresenta le impostazioni cultura preferite dell'utente chiamando il costruttore CultureInfo.CultureInfo(String, Boolean).

  9. Passare la stringa assegnata alla proprietà Value al metodo Split per memorizzare la rappresentazione di stringa della data e ora locale dell'utente e la rappresentazione di stringa dell'offset del fuso orario locale dell'utente in elementi di matrice separati.

  10. Chiamare il metodo DateTime.Parse o DateTime.TryParse(String, IFormatProvider, DateTimeStyles, DateTime) per convertire la data e l'ora della richiesta dell'utente in un valore DateTime. Utilizzare un overload del metodo con un parametro provider e passare uno degli elementi seguenti:

  11. Se l'operazione di analisi al passaggio 10 non riesce, procedere con il passaggio 13. In caso contrario, chiamare il metodo UInt32.Parse(String) per convertire la rappresentazione di stringa dell'offset del fuso orario dell'utente in un valore intero.

  12. Creare un'istanza di un oggetto DateTimeOffset che rappresenta l'ora locale dell'utente chiamando il costruttore DateTimeOffset.DateTimeOffset(DateTime, TimeSpan).

  13. Se la conversione al passaggio 10 non riesce, ripetere i passaggi da 7 a 12 per ogni elemento rimanente nella matrice di stringhe restituita dalla proprietà UserLanguages.

  14. Se la conversione ancora non riesce o se la matrice di stringhe restituita dalla proprietà UserLanguages è vuota, analizzare la stringa utilizzando la lingua inglese, restituita dalla proprietà CultureInfo.InvariantCulture. Ripetere quindi i passaggi da 7 a 12.

Il risultato è un oggetto DateTimeOffset che rappresenta l'ora locale dell'utente della pagina Web. È quindi possibile determinare l'ora UTC equivalente chiamando il metodo ToUniversalTime. È anche possibile determinare la data e l'ora equivalente sul server Web chiamando il metodo TimeZoneInfo.ConvertTime(DateTimeOffset, TimeZoneInfo) e passando il valore TimeZoneInfo.Local come fuso orario in cui convertire l'ora.

Esempio

Nell'esempio riportato di seguito sono contenuti sia l'origine che il codice HTML per un Web Form ASP.NET che richiede all'utente l'immissione di un valore di data e ora. Uno script sul lato client scrive anche informazioni sulla data e l'ora locale della richiesta dell'utente e sull'offset del fuso orario dell'utente rispetto all'ora UTC in un campo nascosto. Queste informazioni vengono quindi analizzate dal server che restituisce una pagina Web con l'input dell'utente. Nella pagina vengono visualizzate anche la data e l'ora della richiesta dell'utente utilizzando l'ora locale dell'utente, l'ora del server e l'ora UTC.


<%@ Page Language="VB" %>
<%@ Import  Namespace="System.Globalization" %> 
<%@ Assembly Name="System.Core" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<script runat="server">

    Protected Sub OKButton_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles OKButton.Click
        Dim locale As String = ""
        Dim styles As DateTimeStyles = DateTimeStyles.AllowInnerWhite Or DateTimeStyles.AllowLeadingWhite Or _
                                       DateTimeStyles.AllowTrailingWhite
        Dim inputDate, localDate As Date
        Dim localDateOffset As DateTimeOffset
        Dim integerOffset As Integer
        Dim result As Boolean

        ' Exit if input is absent.
        If String.IsNullOrEmpty(Me.DateString.Text) Then Exit Sub

        ' Hide form elements.
        Me.DateForm.Visible = False

        ' Create array of CultureInfo objects
        Dim cultures(Request.UserLanguages.Length) As CultureInfo
        For ctr As Integer = Request.UserLanguages.GetLowerBound(0) To Request.UserLanguages.GetUpperBound(0)
            locale = Request.UserLanguages(ctr)
            If Not String.IsNullOrEmpty(locale) Then
                ' Remove quality specifier, if present.
                If locale.Contains(";") Then _
                   locale = Left(locale, InStr(locale, ";") - 1)
                Try
                   cultures(ctr) = New CultureInfo(Request.UserLanguages(ctr), False)
                Catch
                End Try
            Else
                cultures(ctr) = CultureInfo.CurrentCulture
            End If
        Next
        cultures(Request.UserLanguages.Length) = CultureInfo.InvariantCulture
        ' Parse input using each culture.
        For Each culture As CultureInfo In cultures
            result = Date.TryParse(Me.DateString.Text, culture.DateTimeFormat, styles, inputDate)
            If result Then Exit For
        Next
        ' Display result to user.
        If result Then
            Response.Write("<P />")
            Response.Write("The date you input was " + Server.HtmlEncode(CStr(Me.DateString.Text)) + "<BR />")
        Else
            ' Unhide form.
            Me.DateForm.Visible = True
            Response.Write("<P />")
            Response.Write("Unable to recognize " + Server.HtmlEncode(Me.DateString.Text) + ".<BR />")
        End If

        ' Get date and time information from hidden field.
        Dim dates() As String = Request.Form.Item("DateInfo").Split(";")

        ' Parse local date using each culture.
        For Each culture As CultureInfo In cultures
            result = Date.TryParse(dates(0), culture.DateTimeFormat, styles, localDate)
            If result Then Exit For
        Next
        ' Parse offset 
        result = Integer.TryParse(dates(1), integerOffset)
        ' Instantiate DateTimeOffset object representing user's local time
        If result Then
            Try
                localDateOffset = New DateTimeOffset(localDate, New TimeSpan(0, -integerOffset, 0))
            Catch ex As Exception
                result = False
            End Try
        End If
        ' Display result to user.
        If result Then
            Response.Write("<P />")
            Response.Write("Your local date and time is " + localDateOffset.ToString() + ".<BR />")
            Response.Write("The date and time on the server is " & _
                           TimeZoneInfo.ConvertTime(localDateOffset, _
                                                    TimeZoneInfo.Local).ToString() & ".<BR />")
            Response.Write("Coordinated Universal Time is " + localDateOffset.ToUniversalTime.ToString() + ".<BR />")
        Else
            Response.Write("<P />")
            Response.Write("Unable to recognize " + Server.HtmlEncode(dates(0)) & ".<BR />")
        End If
    End Sub

    Protected Sub Page_PreRender(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.PreRender
        Dim script As String = "function AddDateInformation() { " & vbCrLf & _
                  "var today = new Date();" & vbCrLf & _
                  "document.DateForm.DateInfo.value = today.toLocaleString() + " & Chr(34) & Chr(59) & Chr(34) & " + today.getTimezoneOffset();" & vbCrLf & _
                  " }"
        ' Register client script
        Dim scriptMgr As ClientScriptManager = Page.ClientScript
        scriptMgr.RegisterClientScriptBlock(Me.GetType(), "SubmitOnClick", script, True)
    End Sub
</script>

<html xmlns="http://www.w3.org/1999/xhtml">
<head id="Head1" runat="server">
    <title>Parsing a Date and Time Value</title>
</head>
<body>
    <form id="DateForm" runat="server">
    <div>
    <center>
       <asp:Label ID="Label1" runat="server" Text="Enter a Date and Time:" Width="248px"></asp:Label>
       <asp:TextBox ID="DateString" runat="server" Width="176px"></asp:TextBox><br />
    </center>       
    <br />
    <center>
    <asp:Button ID="OKButton" runat="server" Text="Button"  
            OnClientClick="AddDateInformation()" onclick="OKButton_Click" />
    <asp:HiddenField ID="DateInfo"  Value=""  runat="server" />
    </center>
    <br />
    </div>
    </form>
</body>
</html>

<%@ Page Language="C#" %>
<%@ Import Namespace="System.Globalization" %>
<%@ Assembly Name="System.Core" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<script runat="server">

    protected void OKButton_Click(object sender, EventArgs e)
    {
        string locale = "";
        DateTimeStyles styles = DateTimeStyles.AllowInnerWhite | DateTimeStyles.AllowLeadingWhite |
                                       DateTimeStyles.AllowTrailingWhite;
        DateTime inputDate;
        DateTime localDate = DateTime.Now;
        DateTimeOffset localDateOffset = DateTimeOffset.Now;
        int integerOffset;
        bool result = false;

        // Exit if input is absent.
        if (string.IsNullOrEmpty(this.DateString.Text)) return;

        // Hide form elements.
        this.DateForm.Visible = false;

        // Create array of CultureInfo objects
        CultureInfo[] cultures = new CultureInfo[Request.UserLanguages.Length + 1];
        for (int ctr = Request.UserLanguages.GetLowerBound(0); ctr <= Request.UserLanguages.GetUpperBound(0);
             ctr++)
        {
            locale = Request.UserLanguages[ctr];
            if (! string.IsNullOrEmpty(locale))
            {

                // Remove quality specifier, if present.
                if (locale.Contains(";"))
                   locale = locale.Substring(locale.IndexOf(';') -1);
                try
                {
                    cultures[ctr] = new CultureInfo(Request.UserLanguages[ctr], false);
                }
                catch (Exception) { }
            }
            else
            {
                cultures[ctr] = CultureInfo.CurrentCulture;
            }
        }
        cultures[Request.UserLanguages.Length] = CultureInfo.InvariantCulture;
        // Parse input using each culture.
        foreach (CultureInfo culture in cultures)
        {
            result = DateTime.TryParse(this.DateString.Text, culture.DateTimeFormat, styles, out inputDate);
            if (result) break;
        }
        // Display result to user.
        if (result)
        {
            Response.Write("<P />");
            Response.Write("The date you input was " + Server.HtmlEncode(this.DateString.Text) + "<BR />");
        }
        else
        {
            // Unhide form.
            this.DateForm.Visible = true;
            Response.Write("<P />");
            Response.Write("Unable to recognize " + Server.HtmlEncode(this.DateString.Text) + ".<BR />");
        }

        // Get date and time information from hidden field.
        string[] dates= Request.Form["DateInfo"].Split(';');

        // Parse local date using each culture.
        foreach (CultureInfo culture in cultures)
        {
            result = DateTime.TryParse(dates[0], culture.DateTimeFormat, styles, out localDate);
            if (result) break;
        }
        // Parse offset 
        result = int.TryParse(dates[1], out integerOffset);
        // Instantiate DateTimeOffset object representing user's local time
        if (result) 
        {
            try
            {
                localDateOffset = new DateTimeOffset(localDate, new TimeSpan(0, -integerOffset, 0));
            }
            catch (Exception)
            {
                result = false;
            }
        }
        // Display result to user.
        if (result)
        {
            Response.Write("<P />");
            Response.Write("Your local date and time is " + localDateOffset.ToString() + ".<BR />");
            Response.Write("The date and time on the server is " + 
                           TimeZoneInfo.ConvertTime(localDateOffset, 
                                                    TimeZoneInfo.Local).ToString() + ".<BR />");
            Response.Write("Coordinated Universal Time is " + localDateOffset.ToUniversalTime().ToString() + ".<BR />");
        }
        else
        {
            Response.Write("<P />");
            Response.Write("Unable to recognize " + Server.HtmlEncode(dates[0]) + ".<BR />");
        }
    }

    protected void Page_PreRender(object sender, System.EventArgs e)
    {
        string script = "function AddDateInformation() { \n" +
                  "var today = new Date();\n" +
                  "document.DateForm.DateInfo.value = today.toLocaleString() + \";\" + today.getTimezoneOffset();\n" +
                  " }";
        // Register client script
        ClientScriptManager scriptMgr = Page.ClientScript;
        scriptMgr.RegisterClientScriptBlock(this.GetType(), "SubmitOnClick", script, true);
    }

</script>

<html xmlns="http://www.w3.org/1999/xhtml">
<head id="Head1" runat="server">
    <title>Parsing a Date and Time Value</title>
</head>
<body>
    <form id="DateForm" runat="server">
    <div>
    <center>
       <asp:Label ID="Label1" runat="server" Text="Enter a Date and Time:" Width="248px"></asp:Label>
       <asp:TextBox ID="DateString" runat="server" Width="176px"></asp:TextBox><br />
    </center>       
    <br />
    <center>
    <asp:Button ID="OKButton" runat="server" Text="Button"  
            OnClientClick="AddDateInformation()" onclick="OKButton_Click" />
    <asp:HiddenField ID="DateInfo"  Value=""  runat="server" />
    </center>
    <br />
    </div>
    </form>
</body>
</html>

Lo script sul lato client chiama il metodo JavaScript toLocaleString. e produce una stringa che rispetta le convenzioni di formattazione delle impostazioni locali dell'utente, la cui analisi viene in genere eseguita correttamente sul server.

La proprietà HttpRequest.UserLanguages viene compilata con i nomi delle impostazioni cultura contenuti nelle intestazioni Accept-Language incluse in una richiesta HTTP. Non tutti i browser, tuttavia, includono intestazioni Accept-Language nelle rispettive richieste e inoltre le intestazioni possono essere del tutto rimosse dall'utente. Per questo motivo è importante disporre di impostazioni cultura di fallback durante l'analisi dell'input dell'utente. In genere le impostazioni cultura di fallback corrispondono alla lingua inglese restituita da CultureInfo.InvariantCulture. Gli utenti possono anche specificare i nomi delle impostazioni cultura in una casella di testo di Internet Explorer, rischiando tuttavia di inserire nomi non validi. È pertanto indispensabile utilizzare la gestione delle eccezioni durante la creazione di un'istanza di un oggetto CultureInfo.

Quando viene recuperata da una richiesta HTTP inviata da Internet Explorer, la matrice HttpRequest.UserLanguages viene compilata in base alla preferenza dell'utente. Il primo elemento della matrice contiene il nome delle impostazioni cultura/area primarie dell'utente. Se la matrice contiene elementi aggiuntivi, Internet Explorer assegna loro arbitrariamente un identificatore di qualità, separato dal nome delle impostazioni cultura con un punto e virgola. Una voce per le impostazioni cultura fr-FR può ad esempio avere il formato fr-FR;q=0.7.

Nell'esempio viene chiamato il costruttore CultureInfo il cui parametro useUserOverride è impostato su false per creare un nuovo oggetto CultureInfo. In questo modo, se il nome delle impostazioni cultura è il nome predefinito sul server, il nuovo oggetto CultureInfo creato dal costruttore della classe contiene le impostazioni cultura predefinite e non riflette le impostazioni sottoposte a override mediante l'applicazione Opzioni internazionali e della lingua del server. È improbabile che i valori delle impostazioni sottoposte a override sul server si trovino sul sistema dell'utente o vengano riflessi nell'input dell'utente.

Poiché in questo esempio vengono analizzate due rappresentazioni di stringa di una data e di un'ora (una immessa dall'utente, l'altra memorizzata nel campo nascosto), vengono definiti in anticipo gli oggetti CultureInfo che potrebbero essere richiesti. Viene creata una matrice di oggetti CultureInfo ovvero una matrice maggiore del numero di elementi restituiti dalla proprietà HttpRequest.UserLanguages. Viene quindi creata un'istanza di un oggetto CultureInfo per ogni stringa di lingua/area, nonché un'istanza di un oggetto CultureInfo che rappresenta CultureInfo.InvariantCulture.

Il codice può chiamare il metodo Parse o TryParse per convertire la rappresentazione di stringa dell'utente di una data e ora in un valore DateTime. Chiamate ripetute a un metodo di analisi potrebbero richiedere una singola operazione di analisi. Di conseguenza, il metodo TryParse è preferibile in quanto restituisce false se un'operazione di analisi non riesce. Al contrario, la gestione delle eccezioni ripetute che possono essere generate dal metodo Parse può rappresentare una proposta piuttosto dispendiosa in un'applicazione Web.

Compilazione del codice

Per compilare il codice, creare una pagina Web ASP.NET senza code-behind. Copiare quindi l'esempio nella pagina Web in modo che sostituisca tutto il code-behind. La pagina Web ASP.NET deve contenere i controlli seguenti:

  • Un controllo Label al quale non viene fatto riferimento nel codice. Impostarne la proprietà Text su "Immettere un numero:".

  • Un controllo TextBox denominato DateString.

  • Un controllo Button denominato OKButton. Impostarne la proprietà Text su "OK".

  • Un controllo HiddenField denominato DateInfo.

Sicurezza

Per impedire che un utente inserisca uno script nel flusso HTML, l'input dell'utente non deve essere mai restituito direttamente nella risposta del server. Tale input deve essere invece codificato utilizzando il metodo HttpServerUtility.HtmlEncode.

Vedere anche

Concetti

Esecuzione di operazioni di formattazione

Stringhe di formato di data e ora standard

Stringhe di formato data e ora personalizzate

Analisi delle stringhe relative a data e ora