¿Cómo traducir entre las zonas horarias de Windows e IANA?

149

Como se describe en la wiki de etiqueta de zona horaria , hay dos estilos diferentes de zonas horarias.

  • Los que proporciona Microsoft para usar con Windows y la TimeZoneInfoclase .Net (cuando se ejecuta en Windows) se identifican con un valor como "Eastern Standard Time".

  • Los proporcionados por IANA en TZDB, y utilizados por la TimeZoneInfoclase .NET cuando se ejecutan en Linux u OSX, se identifican con un valor como "America/New_York".

Muchas API basadas en Internet utilizan las zonas horarias de la IANA, pero, por numerosas razones, es posible que deba convertir esto en una identificación de zona horaria de Windows, o viceversa.

¿Cómo se puede lograr esto en .Net?

Matt Johnson-Pint
fuente

Respuestas:

198

La fuente principal de los datos para la conversión entre Windows y los identificadores de zona horaria de IANA es el windowsZones.xmlarchivo, distribuido como parte del proyecto Unicode CLDR . La última versión de desarrollo se puede encontrar aquí .

Sin embargo , CLDR se lanza solo dos veces al año. Esto, junto con la cadencia periódica de las actualizaciones de Windows y las actualizaciones irregulares de la base de datos de zonas horarias de la IANA, hace que sea complicado usar los datos CLDR directamente. Tenga en cuenta que los cambios de zona horaria se realizan a voluntad de los distintos gobiernos del mundo, y no todos los cambios se realizan con suficiente antelación para que entren en estos ciclos de lanzamiento antes de sus respectivas fechas de vigencia.

Hay algunos otros casos extremos que deben manejarse que no están cubiertos estrictamente por el CLDR, y de vez en cuando aparecen nuevos. Por lo tanto, he encapsulado la complejidad de la solución en el TimeZoneConverter micro-biblioteca, que se puede instalar desde Nuget.

Usar esta biblioteca es simple. Aquí hay algunos ejemplos de conversión:

string tz = TZConvert.IanaToWindows("America/New_York");
// Result:  "Eastern Standard Time"

string tz = TZConvert.WindowsToIana("Eastern Standard Time");
// result:  "America/New_York"

string tz = TZConvert.WindowsToIana("Eastern Standard Time", "CA");
// result:  "America/Toronto"

Hay más ejemplos en el sitio del proyecto .

Es importante reconocer que si bien una zona horaria de IANA se puede asignar a una única zona horaria de Windows, lo contrario no es cierto. Una sola zona horaria de Windows podría asignarse a más de una zona horaria de IANA. Esto se puede ver en los ejemplos anteriores, donde Eastern Standard Timese asigna a ambos America/New_Yorky a America/Toronto. TimeZoneConverter entregará el que marca CLDR "001", conocido como la "zona dorada", a menos que proporcione específicamente un código de país y haya una coincidencia para una zona diferente en ese país.

Nota: Esta respuesta ha evolucionado a lo largo de los años, por lo que los comentarios a continuación pueden o no aplicarse a la revisión actual. Revise el historial de edición para más detalles. Gracias.

Matt Johnson-Pint
fuente
1
usando este método cuando la conversión (GMT+05:30) Chennai, Kolkata, Mumbai, New Delhida Asia/Calcuttaque debería ser Asia/Kolkata. Parece que TzdbDateTimeZoneSourcecontiene valores antiguos.
Anto Subash
1
@MattJohnson al convertir el método de Asia/Kolkatauso IanaToWindows, falla. pero funciona con Asia/Calcuttacuál es el nombre anterior. Ha actualizado el método WindowsToIanapero IanaToWindowstambién tiene el mismo problema. algunas otras zonas que no están trabajando son America/Argentina/Buenos_Aires, America/Indiana/Indianapolis, Asia/Kathmandu.
Anto Subash
1
@AntoJSubash - Nuevamente, ¡gran observación! He editado el IanaToWindowsmétodo para compensar. ¡Muchas gracias!
Matt Johnson-Pint
2
@MattJohnson Segundo la observación de @ sirrocco. Usar la identificación canónica también me var canonical = tzdbSource.CanonicalIdMap[ ianaZoneId ]; links = Enumerable.Repeat( canonical, 1 ).Concat( links );ayudó.
Johannes Rudolph
2
@sirrocco - Lo siento, no vi tu comentario antes. Actualizadas las funciones. ¡Gracias!
Matt Johnson-Pint
4

Sé que esta es una vieja pregunta, pero tenía un caso de uso que pensé que compartiría aquí, ya que esta es la publicación más relevante que encontré al buscar. Estaba desarrollando una aplicación .NET Core usando un contenedor Docker Linux, pero para la implementación en un servidor Windows. Por lo tanto, solo necesitaba mi contenedor Docker Linux para admitir los nombres de zona horaria de Windows. Lo hice funcionar sin cambiar el código de mi aplicación haciendo lo siguiente:

cp /usr/share/zoneinfo/America/Chicago "/usr/share/zoneinfo/Central Standard Time"
cp /usr/share/zoneinfo/America/New_York "/usr/share/zoneinfo/Eastern Standard Time"
cp /usr/share/zoneinfo/America/Denver "/usr/share/zoneinfo/Mountain Standard Time"
cp /usr/share/zoneinfo/America/Los_Angeles "/usr/share/zoneinfo/Pacific Standard Time"

Luego, en mi código .NET, lo siguiente funcionó sin ninguna modificación: TimeZoneInfo.FindSystemTimeZoneById("Central Standard Time")

Siempre presente
fuente
1
¡Eso es un pensamiento genial fuera de la caja! Parece que debería estar bien, siempre y cuando cubra algunas zonas horarias específicas. Tenga en cuenta que hay más de esos cuatro en los Estados Unidos. Para cubrir los 50 estados en la actualidad, también deberá agregar enlaces para America/Phoenixto "US Mountain Standard Time", Pacific/Honoluluto "Hawaiian Standard Time", America/Anchorageto "Alaskan Standard Time"y America/Adakto "Aleutian Standard Time". Eso no cubre territorios estadounidenses o discrepancias históricas, pero lo ayudará a comenzar.
Matt Johnson-Pint
2
No recomendaría este enfoque si la intención de uno es abarcar todo el mundo o tratar con un identificador de zona horaria válido. La lista es demasiado larga y demasiado volátil para eso.
Matt Johnson-Pint