Convierta globalmente las fechas y horas UTC a las horas locales especificadas por el usuario

Estoy almacenando todos los campos DateTime como hora UTC. Cuando un usuario solicita una página web, me gustaría tomar su zona horaria local preferida (y no la zona horaria local de la máquina servidor) y mostrar automáticamente todos los campos DateTime en todos los formularios web como fechas locales.

Por supuesto, podría aplicar la conversión en cada llamada a DateTime.ToString () en cada forma o implementar alguna utilidad auxiliar, pero es una tarea que consume mucho tiempo, y también hay algunos componentes de terceros que son difíciles de configurar con plantillas personalizadas de visualización de DateTime. .

Básicamente, me gustaría hacer que la clase DateTime se comporte de la siguiente manera:

from this moment on for this web request, whenever some code calls DateTime.ToString(), convert it to the local time using the timezone offset given at the very beginning of the web request, but if possible, please keep .NET core library DateTime.ToString() calls intact (I don't want to mess up event logging timestamps etc.) 

¿Hay alguna forma de hacerlo?

Por cierto, estoy usando ASP.NET MVC 4, si es importante.

No puede hacer directamente lo que pidió, pero le sugiero algunas alternativas. Como señaló Nicholas, no hay nada en HTTP que le proporcione la zona horaria directamente.

Opción 1

  • Primero, decida con qué tipo de datos de zona horaria desea trabajar. Hay dos tipos diferentes disponibles, ya sea las zonas horarias de Microsoft a las que puede acceder con la clase TimeZoneInfo o las zonas TimeZoneInfo IANA / Olson que utiliza el rest del mundo. Lee aquí para más información . Mi recomendación sería la última, utilizando la implementación provista por NodaTime .

  • Luego, determine a qué zona horaria desea convertir. Debe permitirle a su usuario un ajuste en algún lugar para elegir su zona horaria.

    • Puede mostrar una lista desplegable para elegir una de varias zonas horarias, o puede hacer algo más útil, como mostrar un mapa del mundo en el que pueden hacer clic para seleccionar su zona horaria. Hay varias bibliotecas que pueden hacer esto en Javascript, pero mi favorito es este .

    • Es posible que desee adivinar una zona horaria predeterminada para usar, por lo que puede ser lo más preciso posible antes de elegir de la lista (o mapa). Hay una gran biblioteca para esto llamada jsTimeZoneDetect . Interrogará el reloj del navegador y hará una mejor suposición de qué zona horaria podría ser. Es bastante bueno, pero sigue siendo solo una suposición. No lo use a ciegas, pero sí utilícelo para determinar un punto de partida. Actualización Ahora también puede hacer esto con moment.tz.guess() , en el componente moment-timezone de moment.js.

  • Ahora que conoce el huso horario del usuario, puede usar ese valor para convertir sus valores UTC DateTime a esa zona horaria local. Desafortunadamente, no hay nada que puedas configurar en el hilo que hará eso. Cuando cambia la zona horaria del sistema, es global para todos los procesos y subprocesos. Por lo tanto, no tiene más remedio que pasar la zona horaria a todos los lugares donde la envíe. (Creo que esta fue su pregunta principal.) Vea esto casi duplicado aquí.

  • Antes de convertirlo a una cadena, también necesitará conocer la configuración regional del usuario (que puede obtener del valor Request.UserLanguages ). Puede asignarlo al hilo actual, o puede pasarlo como un parámetro al método DateTime.ToString() . Esto no hace ninguna conversión de zona horaria; solo se asegura de que los números estén en la posición correcta, utilizando los separadores correctos y el idioma apropiado para los nombres de días de la semana o meses.

opcion 2

No lo convierta a tiempo local en el servidor.

  • Como dijo que está trabajando con valores UTC, asegúrese de que su propiedad Utc sea Utc . Probablemente deberías hacer esto cuando cargas desde tu base de datos, pero si lo haces puedes hacerlo manualmente:

     myDateTime = DateTime.SpecifyKind(myDateTime, DateTimeKind.Utc); 
  • Envíelo de nuevo al navegador como UTC puro, en un formato invariable como ISO8601. En otras palabras:

     myDateTime.ToString("o"); // example: "2013-05-02T21:01:26.0828604Z" 
  • Use JavaScript en el navegador para analizarlo como UTC. Recogerá automáticamente la configuración de hora local del navegador. Una forma es usar el objeto Date incorporado en JavaScript, como este:

     var dt = new Date('2013-05-02T21:01:26.0828604Z'); 

    Sin embargo, esto solo funcionará en los navegadores más nuevos compatibles con el formato ISO-8601. En cambio, recomiendo usar la biblioteca moment.js . Es coherente en todos los navegadores y tiene un mejor soporte para las fechas ISO y la localización. Además, obtiene muchas otras funciones de análisis y formateo útiles.

     // pass the value from your server var m = moment('2013-05-02T21:01:26.0828604Z'); // use one of the formats supported by moment.js // this is locale-specific "long date time" format. var s = m.format('LLLL'); 

La ventaja de la Opción 1 es que puede trabajar con horas en cualquier zona horaria. Si puede preguntarle al usuario su zona horaria desde una lista desplegable, entonces no necesita usar ningún Javascript.

La ventaja de la Opción 2 es que consigue que el navegador haga parte del trabajo por usted. Esta es la mejor manera de hacerlo si está enviando datos brutos, como realizar llamadas AJAX a una WebAPI. Sin embargo, JavaScript solo conoce el UTC y la zona horaria local del navegador. Por lo tanto, no funciona tan bien si necesita convertir a otras zonas.

También debe tener en cuenta que si elige la opción n. ° 2, puede verse afectado por una falla en el diseño de ECMAScript 5.1. Esto entra en juego si trabaja con fechas que están cubiertas por un conjunto diferente de reglas de horario de verano que las que están vigentes actualmente. Puedes leer más en esta pregunta y en mi blog .

Sería mucho más fácil si tuviéramos alguna información de zona horaria en los encabezados HTTP, pero lamentablemente no lo hacemos. Estos son muchos aros para saltar, pero es la mejor manera de tener flexibilidad y precisión.

La respuesta corta es que no puedes. HTTP no requiere (o incluso proporciona una forma estándar) para el agente de usuario (navegador) para proporcionar información de hora local o zona horaria en la solicitud HTTP.

Usted necesita

  • pregunte al usuario por su zona horaria preferida, o
  • ¿el javascript del lado del cliente se lo informa de alguna manera (cookie? ajax? other?)

Tenga en cuenta que una solución javascript del lado del cliente tampoco es perfecta. Javascript deshabilitado (o inexistente, para algunos navegadores). Es posible que JavaScript no tenga acceso a la información de la zona horaria. Etc.