Partager via


Comment : afficher des informations de date et d'heure localisées pour les utilisateurs du Web

Comme une page Web peut être affichée n'importe où dans le monde, les opérations d'analyse et de mise en forme des valeurs de date et d'heure ne doivent pas dépendre d'un format par défaut (généralement le format de la culture locale du serveur Web) lors de l'interaction avec l'utilisateur. Les formulaires Web qui gèrent les chaînes de date et d'heure entrées par l'utilisateur doivent analyser les chaînes à l'aide de la culture par défaut de ce dernier. De même, les données de date et d'heure doivent être affichées dans un format conforme à la culture de l'utilisateur. Cette rubrique indique comment faire.

Pour analyser les chaînes de date et d'heure entrées par l'utilisateur

  1. Déterminez si le tableau de chaînes retourné par la propriété HttpRequest.UserLanguages est rempli. Si ce n'est pas le cas, passez à l'étape 6.

  2. Si le tableau de chaînes retourné par la propriété UserLanguages est rempli, récupérez son premier élément. Le premier élément indique la langue et la région par défaut ou préférées de l'utilisateur.

  3. Instanciez un objet CultureInfo qui représente la culture par défaut de l'utilisateur en appelant le constructeur CultureInfo.CultureInfo(String, Boolean).

  4. Appelez la méthode TryParse ou Parse du type DateTime ou DateTimeOffset pour essayer de procéder à la conversion. Utilisez une surcharge de la méthode TryParse ou Parse avec un paramètre provider et passez-lui l'un ou l'autre des éléments suivants :

  5. Si la conversion échoue, répétez les étapes 2 à 4 pour chaque élément restant du tableau de chaînes retourné par la propriété UserLanguages.

  6. Si la conversion échoue encore ou si le tableau de chaînes retourné par la propriété UserLanguages est vide, analysez la chaîne en utilisant la culture indifférente retournée par la propriété CultureInfo.InvariantCulture.

Pour analyser les date et heure locales de la demande de l'utilisateur

  1. Ajoutez un contrôle HiddenField à un formulaire Web.

  2. Créez une fonction JavaScript qui gère l'événement onClick d'un bouton Submit en écrivant la date et l'heure actuelles ainsi que le décalage par rapport au temps universel coordonné (offset UTC) dans la propriété Value. Utilisez un séparateur (tel qu'un point-virgule) pour séparer les deux composants de la chaîne.

  3. Utilisez l'événement PreRender du formulaire Web pour injecter la fonction dans le flux de sortie HTML en passant le texte du script à la méthode ClientScriptManager.RegisterClientScriptBlock(Type, String, String, Boolean).

  4. Connectez le gestionnaire d'événements à l'événement onClick du bouton Submit en fournissant le nom de la fonction JavaScript à l'attribut OnClientClick du bouton Submit.

  5. Créez un gestionnaire pour l'événement Click du bouton Submit.

  6. Dans le gestionnaire d'événements, déterminez si le tableau de chaînes retourné par la propriété HttpRequest.UserLanguages est rempli. Si ce n'est pas le cas, passez à l'étape 14.

  7. Si le tableau de chaînes retourné par la propriété UserLanguages est rempli, récupérez son premier élément. Le premier élément indique la langue et la région par défaut ou préférées de l'utilisateur.

  8. Instanciez un objet CultureInfo qui représente la culture par défaut de l'utilisateur en appelant le constructeur CultureInfo.CultureInfo(String, Boolean).

  9. Passez la chaîne assignée à la propriété Value à la méthode Split pour stocker la représentation sous forme de chaîne des date et heure locales de l'utilisateur ainsi que la représentation sous forme de chaîne du décalage de fuseau horaire local de l'utilisateur dans des éléments de tableau séparés.

  10. Appelez la méthode DateTime.Parse ou DateTime.TryParse(String, IFormatProvider, DateTimeStyles, DateTime) pour convertir les date et heure de la demande de l'utilisateur en une valeur DateTime. Utilisez une surcharge de la méthode avec un paramètre provider et passez-lui l'un ou l'autre des éléments suivants :

  11. Si l'opération d'analyse de l'étape 10 échoue, passez à l'étape 13. Sinon, appelez la méthode UInt32.Parse(String) pour convertir la représentation sous forme de chaîne du décalage de fuseau horaire de l'utilisateur en un entier.

  12. Instanciez un objet DateTimeOffset qui représente l'heure locale de l'utilisateur en appelant le constructeur DateTimeOffset.DateTimeOffset(DateTime, TimeSpan).

  13. Si la conversion de l'étape 10 échoue, répétez les étapes 7 à 12 pour chaque élément restant du tableau de chaînes retourné par la propriété UserLanguages.

  14. Si la conversion échoue encore ou si le tableau de chaînes retourné par la propriété UserLanguages est vide, analysez la chaîne en utilisant la culture indifférente retournée par la propriété CultureInfo.InvariantCulture. Répétez ensuite les étapes 7 à 12.

Le résultat est un objet DateTimeOffset qui représente l'heure locale de l'utilisateur de votre page Web. Vous pouvez ensuite déterminer l'heure UTC équivalente en appelant la méthode ToUniversalTime. Vous pouvez également déterminer les date et heure équivalentes sur votre serveur Web en appelant la méthode TimeZoneInfo.ConvertTime(DateTimeOffset, TimeZoneInfo) et en passant une valeur TimeZoneInfo.Local en tant que fuseau horaire vers lequel convertir l'heure.

Exemple

L'exemple suivant contient à la fois la source HTML et le code pour un formulaire Web ASP.NET qui demande à l'utilisateur d'entrer une valeur de date et d'heure. Un script côté client écrit également des informations sur les date et heure locales de la demande de l'utilisateur et le décalage par rapport à l'heure UTC (offset de fuseau horaire de l'utilisateur) dans un champ masqué. Ces informations sont ensuite analysées par le serveur qui retourne une page Web affichant l'entrée de l'utilisateur. Les date et heure de la demande de l'utilisateur sont également affichées en utilisant l'heure locale de l'utilisateur, l'heure sur le serveur et l'heure 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>

Le script côté client appelle la méthode toLocaleString JavaScript. Une chaîne qui suit les conventions de mise en forme des paramètres régionaux de l'utilisateur est alors générée. Elle a plus de chances d'être analysée avec succès sur le serveur.

La propriété HttpRequest.UserLanguages est remplie à l'aide des noms de culture contenus dans les en-têtes Accept-Language inclus dans une requête HTTP. Toutefois, certains navigateurs n'incluent pas d'en-têtes Accept-Language dans leurs demandes, et les utilisateurs peuvent également supprimer complètement les en-têtes. Il est donc important d'avoir une culture de secours lors de l'analyse de l'entrée d'utilisateur. En général, la culture de secours est la culture indifférente retournée par CultureInfo.InvariantCulture. Comme les utilisateurs peuvent également fournir à Internet Explorer des noms de culture qu'ils entrent dans une zone de texte, il peut arriver que ces noms ne soient pas valides. Il est donc important d'utiliser la gestion des exceptions lors de l'instanciation d'un objet CultureInfo.

Lorsqu'il est récupéré d'une requête HTTP soumise par Internet Explorer, le tableau HttpRequest.UserLanguages est rempli par ordre de préférence de l'utilisateur. Le premier élément du tableau comporte le nom de la culture/région principale de l'utilisateur. Si le tableau contient des éléments supplémentaires, Internet Explorer leur assigne arbitrairement un spécificateur de qualité séparé du nom de culture par un point-virgule. Par exemple, une entrée de la culture fr-FR peut se présenter sous la forme fr-FR;q=0.7.

L'exemple appelle le constructeur CultureInfo avec son paramètre useUserOverride défini à false pour créer un objet CultureInfo. Ceci garantit que, si le nom de culture est celui utilisé par défaut sur le serveur, le nouvel objet CultureInfo créé par le constructeur de classe contient les paramètres par défaut d'une culture et ne reflète pas les paramètres substitués à l'aide de l'application Options régionales et linguistiques du serveur. Il est peu probable que les valeurs des paramètres substitués sur le serveur existent sur le système de l'utilisateur ou qu'elles apparaissent dans l'entrée de l'utilisateur.

Comme cet exemple analyse deux représentations sous forme de chaîne des date et heure (l'une est entrée par l'utilisateur, l'autre est stockée dans le champ masqué), il définit les objets CultureInfo qui peuvent être requis à l'avance. Il crée un tableau d'objets CultureInfo qui contient un élément de plus que ceux retournés par la propriété HttpRequest.UserLanguages. Ensuite, il instancie un objet CultureInfo pour chaque chaîne de langue/région, ainsi qu'un objet CultureInfo qui représente CultureInfo.InvariantCulture.

Votre code peut appeler la méthode Parse ou TryParse pour convertir la représentation sous forme de chaîne des date et heure de l'utilisateur en une valeur DateTime. Il se peut que vous deviez répéter les appels à une méthode d'analyse si vous effectuez une seule opération d'analyse. Par conséquent, en cas d'échec d'une opération d'analyse, il est préférable d'utiliser la méthode TryParse, car elle retourne la valeur false. En revanche, la gestion des exceptions répétées pouvant être levées par la méthode Parse risque d'être très onéreuse dans une application Web.

Compilation du code

Pour compiler le code, créez une page Web ASP.NET sans code-behind. Copiez ensuite l'exemple dans la page Web pour qu'il remplace tout le code existant. La page Web ASP.NET doit contenir les contrôles suivants :

  • un contrôle Label qui n'est pas référencé dans le code (affectez la valeur "Enter a Number:" à sa propriété Text) ;

  • un contrôle TextBox nommé DateString ;

  • un contrôle Button nommé OKButton (affectez la valeur "OK" à sa propriété Text) ;

  • un contrôle HiddenField nommé DateInfo.

Sécurité

Pour empêcher l'injection de script dans le flux de données HTML, l'entrée d'utilisateur ne doit jamais être répétée directement dans la réponse du serveur, mais encodée à l'aide de la méthode HttpServerUtility.HtmlEncode.

Voir aussi

Concepts

Exécution d'opérations de mise en forme

Chaînes de format de date et d'heure standard

Chaînes de format de date et d'heure personnalisées

Analyse des chaînes de date/heure