Postupy: Zobrazit lokalizované informace data a času uživatelům webu
Protože lze webovou stránku zobrazit kdekoli na světě, operace pro analýzu a formátování data a času by při interakci s uživatelem neměli spoléhat na výchozí formát (což je nejčastěji formát jazykové verze místního webového serveru). Místo toho by webové formuláře, které zpracovávají řetězce data a času zadávané uživatelem, měly analyzovat řetězce pomocí upřednostňované jazykové verze daného uživatele. Podobně by měly být datum a čas zobrazeny uživateli ve formátu, který odpovídá jazykové verzi daného uživatele. Toto téma ukazuje, jak toto provést.
Chcete-li analyzovat řetězce data a času zadané uživatelem
Určete, zda je naplněno pole řetězců vrácené vlastností HttpRequest.UserLanguages. Pokud tomu tak není, pokračujte krokem 6.
Pokud je naplněno pole řetězců vrácené vlastností UserLanguages, načtěte jeho první prvek. První prvek označuje výchozí nastavení uživatele nebo preferovaný jazyk a oblast.
Vytvořte instanci objektu CultureInfo, který představuje preferovanou jazykovou verzi uživatele, voláním konstruktoru CultureInfo.CultureInfo(String, Boolean).
Zavolejte metodu TryParse nebo Parse typu DateTime nebo DateTimeOffset pro pokus o převod. Použijte přetížení metody TryParse nebo Parse s parametrem provider a předejte jí kterýkoli ze dvou následujících objektů:
Objekt CultureInfo vytvořený v kroku 3.
Objekt DateTimeFormatInfo, který je vrácen vlastností DateTimeFormat objektu CultureInfo vytvořeného v kroku 3.
Pokud převod selže, opakujte kroky 2 až 4 pro každý zbývající element v poli řetězců vráceného vlastností UserLanguages.
Pokud se převod stále nedaří nebo pokud je pole řetězců vrácené vlastností UserLanguages prázdné, analyzujte řetězec pomocí neutrální jazykové verze, která je vrácena vlastností CultureInfo.InvariantCulture.
Chcete-li analyzovat místní datum a čas požadavku uživatele
Přidejte ovládací prvek HiddenField do webového formuláře.
Vytvořte funkci jazyka JavaScript, která zpracovává událost onClick tlačítka Submit pomocí zápisu aktuálního data a času a posunu místního časového pásma od času UTC do vlastnosti Value. Použijte oddělovač (jako je středník) k oddělení těchto dvou součástí řetězce.
Použijte událost PreRender webového formuláře, která vloží funkci do výstupního datového proudu HTML předáním textu skriptu metodě ClientScriptManager.RegisterClientScriptBlock(Type, String, String, Boolean).
Připojte obslužnou rutinu události k tlačítku Submit a jeho události onClick poskytnutím názvu funkce jazyka JavaScript atributu OnClientClick tlačítka Submit.
Vytvořte obslužnou rutiny pro tlačítko Submit a jeho událost Click.
V obslužné rutině události zjistěte, zda je pole řetězců vrácené vlastností HttpRequest.UserLanguages naplněné. Pokud tomu tak není, pokračujte krokem 14.
Pokud je pole řetězců vrácené vlastností UserLanguages naplněno, získejte jeho první prvek. První prvek označuje výchozí nastavení uživatele nebo preferovaný jazyk a oblast.
Vytvořte instanci objektu CultureInfo, který představuje preferovanou jazykovou verzi uživatele, voláním konstruktoru CultureInfo.CultureInfo(String, Boolean).
Předejte řetězec přiřazený vlastnosti Value metodě Split pro uložení řetězcového vyjádření místního data a času uživatele a posunu místního časového pásma uživatele do samostatných prvků pole.
Zavolejte metodu DateTime.Parse nebo metodu DateTime.TryParse(String, IFormatProvider, DateTimeStyles, DateTime%) pro převedení data a času požadavku uživatele na hodnotu DateTime. Použijte přetížení metody s parametrem provider a předejte jí kterýkoli ze dvou následujících objektů:
Objekt CultureInfo vytvořený v kroku 8.
Objekt DateTimeFormatInfo, který je vrácen vlastností DateTimeFormat objektu CultureInfo vytvořeného v kroku 8.
V případě selhání operace analýzy v kroku 10, přejděte ke kroku 13. V opačném případě volejte metodu UInt32.Parse(String) pro převedení řetězcového vyjádření posunu časového pásma uživatele na celé číslo.
Vytvořte instanci DateTimeOffset, která představuje místní čas uživatele, voláním konstruktoru DateTimeOffset.DateTimeOffset(DateTime, TimeSpan).
Pokud převod v kroku 10 selže, opakujte kroky 7 až 12 pro každý zbývající prvek v poli řetězců vráceném vlastností UserLanguages.
Pokud se převod stále nedaří nebo pokud je pole řetězců vrácené vlastností UserLanguages prázdné, analyzujte řetězec pomocí neutrální jazykové verze, která je vrácena vlastností CultureInfo.InvariantCulture. Poté opakujte kroky 7 až 12.
Výsledkem je objekt DateTimeOffset, který představuje místní čas uživatele vaší webové stránky. Poté můžete určit odpovídající čas standardu UTC voláním metody ToUniversalTime. Můžete také určit odpovídající datum a čas na vašem webovém serveru voláním metody TimeZoneInfo.ConvertTime(DateTimeOffset, TimeZoneInfo) a předáním hodnoty TimeZoneInfo.Local jakožto časového pásma, do kterého má být čas převeden.
Příklad
Následující příklad obsahuje zdrojový kód jazyk HTML i kód pro formulář technologie ASP.NET, který žádá uživatele o zadání hodnoty data a času. Skript na straně klienta také zapíše informace o místním datu a času žádosti uživatele a posunu časového pásma uživatele od času UTC do skrytého pole. Tyto informace jsou poté analyzovány serverem, který vrací webovou stránku zobrazující uživatelský vstup. Také zobrazuje datum a čas požadavku uživatele pomocí místního času uživatele, čas serveru a čas 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>
Skript na straně klienta volá metodu toLocaleString jazyka JavaScript. Ta vytváří řetězec řídící se úmluvami formátování národního prostředí uživatele, jehož analýza bude zcela určitě úspěšnější na serveru.
Vlastnost HttpRequest.UserLanguages je naplněna názvy jazykových verzí obsažených v záhlaví Accept-Language, které jsou součástí požadavku HTTP. Ne všechny prohlížeče však ve svých požadavcích zahrnují záhlaví Accept-Language a uživatelé mohou také potlačit záhlaví úplně. Proto je důležité mít záložní jazykovou verzi při analýze vstupu uživatele. Obvykle je záložní jazyková verze neutrální jazyková verze vrácená CultureInfo.InvariantCulture. Uživatelé mohou také poskytovat Internet Explorer s názvy jazykových verzí, které vložili do textového pole, které vytvoří tu možnost, že názvy jazykových verzí mohou být neplatné. Proto je důležité použít zpracování výjimek při vytvoření instance objektu CultureInfo.
Při načítání z odeslaných HTTP požadavků Internet Explorerem je pole HttpRequest.UserLanguages naplněno v pořadí podle předvolby uživatele. První prvek pole obsahuje název primární jazykové verze/oblasti uživatele. Pokud pole obsahuje jakékoli další položky, Internet Explorer jim libovolně přiřadí specifikátor kvality, který je oddělen středníkem od názvu jazykové verze. Například položka pro jazykovou verzi fr-FR může mít formu fr-FR;q=0.7.
Příklad volá konstruktor CultureInfo s jeho parametrem useUserOverride nastaveným false pro vytvoření nového objektu CultureInfo. To zajišťuje, že pokud je název jazykové verze výchozím názvem jazykové verze na serveru, pak nový objekt CultureInfo vytvořený pomocí konstruktoru třídy obsahuje výchozí nastavení jazykové verze a neprojeví se žádné nastavení přepsané použitím aplikace serveru Místní a jazykové nastavení. Je nepravděpodobné, že v uživatelském systému existují hodnoty z jakéhokoli přepsaného nastavení na serveru, nebo že se projeví v uživatelském vstupu.
Protože jsou v tomto příkladu analyzovány dvě řetězcové vyjádření data a času (jeden uživatelský vstup, druhý uložený do skrytého pole), jsou možné objekty CultureInfo, které mohou být požadovány, definovány předem. Vytvoří se pole objektů CultureInfo, které je o jedničku větší než počet prvků vrácených vlastností HttpRequest.UserLanguages. Poté se vytvoří instance objektu CultureInfo pro každý řetězec jazyka nebo oblasti a také se vytvoří instance objektu CultureInfo, který představuje CultureInfo.InvariantCulture.
Váš kód může volat metodu Parse nebo metodu TryParse pro převedení řetězcového vyjádření data a času uživatele na hodnotu DateTime. Pro jedinou operaci analýzy může být vyžadováno opakované volání metody analýzy. V důsledku toho je metoda TryParse lepší, protože vrátí false v případě selhání operace analýzy. Naopak ošetřování opakovaných výjimek, které mohou být vyvolány metodou Parse může být ve webové aplikaci velmi nákladnou záležitostí.
Probíhá kompilace kódu
Chcete-li kód zkompilovat, vytvořte webovou stránku ASP.NET bez kódu na pozadí. Zkopírujte poté příklad do webové stránka tak, aby nahradil veškerý existující kód. Webová stránka ASP.NET by měla obsahovat následující ovládací prvky:
Ovládací prvek Label, na který není odkaz v kódu. Nastavte jeho vlastnost Text na "Zadejte číslo:".
Ovládací prvek TextBox s názvem DateString.
Ovládací prvek Button s názvem OKButton. Nastavte jeho vlastnost Text na "OK".
Ovládací prvek HiddenField s názvem DateInfo.
Zabezpečení
Pro zabránění vložení skriptu do proudu HTML uživatelem, by uživatelský vstup neměl být nikdy přímo vepisován zpět do odpovědi serveru. Místo toho by měl být kódován pomocí metody HttpServerUtility.HtmlEncode.
Viz také
Koncepty
Standardní formátovací řetězce data a času