Cómo: Mostrar información localizada de fecha y hora a usuarios web
Dado que una página web puede mostrarse en cualquier parte del mundo, las operaciones de análisis y formato que se realizan sobre los valores de fecha y hora no deben basarse en un formato predeterminado (que suele ser el formato de la referencia cultural local del servidor web) al interactuar con el usuario. En lugar de ello, los formularios web que controlan cadenas de fecha y hora escritas por el usuario deben analizar estas cadenas usando la referencia cultural preferida del usuario. De igual forma, los datos de fecha y hora deben mostrarse al usuario en un formato que se ajuste a su referencia cultural. En este tema se explica cómo hacerlo.
Para analizar cadenas de fecha y hora escritas por el usuario
Determine si la matriz de cadenas devuelta por la propiedad HttpRequest.UserLanguages contiene elementos. Si no es así, continúe en el paso 6.
Si la matriz de cadenas devuelta por la propiedad UserLanguages contiene elementos, recupere el primer elemento. El primer elemento indica el idioma y la región predeterminados o preferidos por el usuario.
Cree una instancia de un objeto CultureInfo que represente la referencia cultural preferida del usuario; para ello, llame al constructor CultureInfo.CultureInfo(String, Boolean).
Llame al método TryParse o Parse del tipo DateTime o DateTimeOffset para intentar realizar la conversión. Utilice una sobrecarga del método TryParse o Parse con un parámetro provider y pásele cualquiera de los objetos siguientes:
El objeto CultureInfo creado en el paso 3.
El objeto DateTimeFormatInfo devuelto por la propiedad DateTimeFormat del objeto CultureInfo que se creó en el paso 3.
Si la conversión no se realiza correctamente, repita los pasos 2 al 4 en cada elemento restante de la matriz de cadenas devuelta por la propiedad UserLanguages.
Si la conversión sigue produciendo errores o si la matriz de cadenas devuelta por la propiedad UserLanguages está vacía, analice la cadena usando la referencia cultural de todos los idiomas devuelta por la propiedad CultureInfo.InvariantCulture.
Para analizar la fecha y hora locales de la solicitud del usuario
Agregue un control HiddenField a un formulario web.
Cree una función JavaScript que controle el evento onClick de un botón Submit; para ello, especifique la fecha y hora actuales y el desfase de la zona horaria local con respecto a la hora universal coordinada (hora UTC) en la propiedad Value. Use un delimitador (por ejemplo, un punto y coma) para separar los dos componentes de la cadena.
Use el evento PreRender del formulario web para insertar la función en el flujo de salida HTML; para ello, pase el texto del script al método ClientScriptManager.RegisterClientScriptBlock(Type, String, String, Boolean).
Asocie el controlador de eventos con el evento onClick del botón Submit; para ello, especifique el nombre de la función JavaScript en el atributo OnClientClick del botón Submit.
Cree un controlador para el evento Click del botón Submit.
En el controlador de eventos, determine si la matriz de cadenas devuelta por la propiedad HttpRequest.UserLanguages contiene elementos. Si no es así, continúe en el paso 14.
Si la matriz de cadenas devuelta por la propiedad UserLanguages contiene elementos, recupere el primer elemento. El primer elemento indica el idioma y la región predeterminados o preferidos por el usuario.
Cree una instancia de un objeto CultureInfo que represente la referencia cultural preferida del usuario; para ello, llame al constructor CultureInfo.CultureInfo(String, Boolean).
Pase la cadena asignada a la propiedad Value al método Split para almacenar la representación de cadena de la fecha y hora locales del usuario y la representación de cadena del desfase de la zona horaria local del usuario en diferentes elementos de la matriz.
Llame al método DateTime.Parse o DateTime.TryParse(String, IFormatProvider, DateTimeStyles, DateTime) para convertir la fecha y hora de la solicitud del usuario en un valor DateTime. Utilice una sobrecarga del método con un parámetro provider y pásele cualquiera de los objetos siguientes:
El objeto CultureInfo creado en el paso 8.
El objeto DateTimeFormatInfo devuelto por la propiedad DateTimeFormat del objeto CultureInfo que se creó en el paso 8.
Si la operación de análisis produce un error en el paso 10, vaya al paso 13. De lo contrario, llame al método UInt32.Parse(String) para convertir la representación de cadena del desfase de zona horaria del usuario en un entero.
Cree una instancia de DateTimeOffset que represente la hora local del usuario llamando al constructor DateTimeOffset.DateTimeOffset(DateTime, TimeSpan).
Si la conversión del paso 10 no se realiza correctamente, repita los pasos del 7 al 12 en cada elemento restante de la matriz de cadenas devuelta por la propiedad UserLanguages.
Si la conversión sigue produciendo errores o si la matriz de cadenas devuelta por la propiedad UserLanguages está vacía, analice la cadena usando la referencia cultural de todos los idiomas devuelta por la propiedad CultureInfo.InvariantCulture. A continuación, repita los pasos del 7 al 12.
El resultado es un objeto DateTimeOffset que representa la hora local del usuario de su página web. Puede determinar a continuación la hora UTC equivalente llamando al método ToUniversalTime. También puede determinar la fecha y hora equivalentes del servidor web llamando al método TimeZoneInfo.ConvertTime(DateTimeOffset, TimeZoneInfo) y pasando un valor TimeZoneInfo.Local como zona horaria a la que debe convertirse la hora.
Ejemplo
El ejemplo siguiente incluye código fuente HTML y el código de un formulario web de ASP.NET que solicita al usuario que escriba un valor de fecha y hora. También hay un script de cliente que escribe información sobre la fecha y hora local de la solicitud del usuario y el desfase de la zona horaria del usuario con respecto a la hora UTC en un campo oculto. Esta información se analiza a continuación en el servidor, que devuelve una página web en la que se muestran los datos especificados por el usuario. También muestra la fecha y hora de la solicitud del usuario utilizando la hora local del usuario, la hora del servidor y la hora 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>
El script de cliente llama al método toLocaleString de JavaScript. Esto genera una cadena que se ajusta a las convenciones de formato de la configuración regional del usuario, por lo que es más probable que se analice correctamente en el servidor.
La propiedad HttpRequest.UserLanguages se rellena a partir de los nombres de la referencia cultural incluidos en los encabezados Accept-Language de una solicitud HTTP. Sin embargo, no todos los exploradores incluyen encabezados Accept-Language en sus solicitudes y además los usuarios pueden suprimir los encabezados por completo. Por ello, es importante tener una referencia cultural de reserva al analizar los datos proporcionados por el usuario. Normalmente, la referencia cultural de reserva es la referencia cultural de todos los idiomas devuelta por la propiedad CultureInfo.InvariantCulture. Los usuarios también pueden proporcionar a Internet Explorer nombres de la referencia cultural escribiéndolos en un cuadro de texto, de modo que es posible que los nombres de la referencia cultural no sean válidos. Por todo esto, es importante utilizar el control de excepciones al crear instancias de un objeto CultureInfo.
Cuando se recupera de una solicitud HTTP enviada por Internet Explorer, la matriz HttpRequest.UserLanguages se rellena en orden de preferencia del usuario. El primer elemento de la matriz contiene el nombre de la referencia cultural o región primaria del usuario. Si la matriz contiene algún otro elemento, Internet Explorer le asigna arbitrariamente un especificador de calidad, que está delimitado del nombre de la referencia cultural mediante un punto y coma. Por ejemplo, una entrada para la referencia cultural fr-FR podría tener la forma fr-FR;q=0.7.
En el ejemplo, se llama al constructor CultureInfo con el parámetro useUserOverride establecido en false para crear un nuevo objeto CultureInfo. De este modo, tiene la seguridad de que si el nombre de la referencia cultural es el nombre de la referencia cultural predeterminada del servidor, el nuevo objeto CultureInfo creado por el constructor de clase contiene la configuración predeterminada de una referencia cultural y no refleja las configuraciones invalidadas con la aplicación Configuración regional y de idioma del servidor. Es poco probable que los valores de una configuración invalidada en el servidor permanezcan en el sistema del usuario o se reflejen en los datos proporcionados por el usuario.
Dado que en este ejemplo se analizan dos representaciones de cadena de una fecha y hora (un valor proporcionado por el usuario y otro valor almacenado en el campo oculto), se definen los objetos CultureInfo que a priori podrían ser necesarios. En el ejemplo se crea una matriz de objetos CultureInfo con un elemento más que el número de elementos devuelto por la propiedad HttpRequest.UserLanguages. A continuación, se crean instancias de un objeto CultureInfo para cada cadena de idioma o región y también se crean instancias de un objeto CultureInfo que representa CultureInfo.InvariantCulture.
Su código puede llamar al método Parse o TryParse para convertir la representación de cadena del usuario de una fecha y hora en un valor DateTime. Es posible que sea necesario llamar varias veces a un método de análisis en una única operación de análisis. Como consecuencia, el método TryParse resulta más conveniente porque devuelve false si se producen errores en una operación de análisis. Por el contrario, mantener un control sobre las excepciones repetidas que puede iniciar el método Parse puede ser una tarea excesivamente costosa cuando se trata de una aplicación web.
Compilar el código
Para compilar el código, cree una página web de ASP.NET sin código subyacente. A continuación, copie el ejemplo en la página web de modo que reemplace todo el código existente. La página web de ASP.NET debe contener los controles siguientes:
Un control Label, al que no se hace referencia en código. Establezca la propiedad Text en "Escriba un número:".
Un control TextBox denominado DateString.
Un control Button denominado OKButton. Establezca la propiedad Text en "Aceptar".
Un control HiddenField denominado DateInfo.
Seguridad
Para impedir que un usuario inserte el script en el flujo HTML, los datos proporcionados por el usuario nunca deben devolverse directamente en la respuesta del servidor. En su lugar, deberían codificarse con el método HttpServerUtility.HtmlEncode.
Vea también
Conceptos
Efectuar operaciones de formato
Cadenas con formato de fecha y hora estándar