Como traduzir entre fusos horários do Windows e da IANA?

149

Conforme descrito no wiki da tag de fuso horário , existem dois estilos diferentes de fuso horário.

  • Os fornecidos pela Microsoft para uso com o Windows e a TimeZoneInfoclasse .Net (quando executados no Windows) são identificados por um valor como "Eastern Standard Time".

  • Os fornecidos pela IANA no TZDB e usados ​​pela TimeZoneInfoclasse .NET ao executar no Linux ou OSX, são identificados por um valor como "America/New_York".

Muitas APIs baseadas na Internet usam os fusos horários da IANA, mas por vários motivos, pode ser necessário convertê-lo em um ID de fuso horário do Windows ou vice-versa.

Como isso pode ser feito no .Net?

Matt Johnson-Pint
fonte

Respostas:

198

A fonte principal dos dados para conversão entre os identificadores de fuso horário do Windows e da IANA é o windowsZones.xmlarquivo, distribuído como parte do projeto Unicode CLDR . A versão mais recente do desenvolvedor pode ser encontrada aqui .

No entanto , o CLDR é lançado apenas duas vezes por ano. Isso, juntamente com a cadência periódica de atualizações do Windows e as atualizações irregulares do banco de dados de fuso horário da IANA, dificultam o uso direto dos dados CLDR. Lembre-se de que as mudanças de fuso horário são feitas por capricho dos vários governos do mundo e nem todas são feitas com aviso suficiente para entrar nesses ciclos de liberação antes de suas respectivas datas efetivas.

Existem alguns outros casos extremos que precisam ser tratados e não são cobertos estritamente pelo CLDR, e novos surgem de tempos em tempos. Portanto, encapsulei a complexidade da solução na micro-biblioteca TimeZoneConverter , que pode ser instalada no Nuget.

Usar esta biblioteca é simples. Aqui estão alguns exemplos de conversão:

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"

Existem mais exemplos no site do projeto .

É importante reconhecer que, embora um fuso horário da IANA possa ser mapeado para um único fuso horário do Windows, o inverso não é verdadeiro. Um único fuso horário do Windows pode ser mapeado para mais de um fuso horário da IANA. Isso pode ser visto nos exemplos acima, onde Eastern Standard Timeé mapeado para ambos America/New_Yorke para America/Toronto. O TimeZoneConverter fornecerá o que o CLDR marcar "001", conhecido como "zona de ouro", a menos que você forneça especificamente um código de país e haja uma correspondência para uma zona diferente nesse país.

Nota: Esta resposta evoluiu ao longo dos anos, portanto, os comentários abaixo podem ou não se aplicar à revisão atual. Revise o histórico de edições para obter detalhes. Obrigado.

Matt Johnson-Pint
fonte
1
usando este método ao converter (GMT+05:30) Chennai, Kolkata, Mumbai, New DelhiAsia/Calcuttaque deveria ser Asia/Kolkata. parece que o TzdbDateTimeZoneSourcecontém valores antigos.
Anto Subash
1
@MattJohnson ao converter o método Asia/Kolkatausing IanaToWindows, ele falha. mas funciona com Asia/Calcuttao nome antigo. você atualizou o método, WindowsToIanamas IanaToWindowstambém tem o mesmo problema. algumas outras zonas que não são de trabalho são America/Argentina/Buenos_Aires, America/Indiana/Indianapolis, Asia/Kathmandu.
Anto Subash
1
@AntoJSubash - Novamente, ótima observação! Eu editei o IanaToWindowsmétodo para compensar. Muito obrigado!
Matt Johnson-Pint
2
@ MattJohnson I segunda observação de sirrocco. Usar o id canônico também var canonical = tzdbSource.CanonicalIdMap[ ianaZoneId ]; links = Enumerable.Repeat( canonical, 1 ).Concat( links );fez o truque para mim.
Johannes Rudolph
2
@sirrocco - Desculpe, não vi seu comentário mais cedo. Atualizado as funções. Obrigado!
Matt Johnson-Pint
4

Sei que essa é uma pergunta antiga, mas eu tinha um caso de uso que eu gostaria de compartilhar aqui, pois essa é a postagem mais relevante que encontrei ao pesquisar. Eu estava desenvolvendo um aplicativo .NET Core usando um contêiner do docker linux, mas para implantação em um servidor Windows. Então, eu só precisava do meu contêiner do docker linux para dar suporte aos nomes de fuso horário do Windows. Consegui isso funcionando sem alterar o código do aplicativo, fazendo o seguinte:

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"

Então, no meu código .NET, o seguinte funcionou sem nenhuma modificação: TimeZoneInfo.FindSystemTimeZoneById("Central Standard Time")

EverPresent
fonte
1
Esse é um ótimo pensamento pronto para uso imediato! Parece que está tudo bem, desde que você cubra alguns fusos horários específicos. Lembre-se de que existem mais do que quatro nos EUA. Para cobrir os 50 estados em dias atuais, você também precisará adicionar links para America/Phoenixa "US Mountain Standard Time", Pacific/Honolulua "Hawaiian Standard Time", America/Anchoragea "Alaskan Standard Time", e America/Adakpara "Aleutian Standard Time". Isso não abrange territórios dos EUA ou discrepâncias históricas, mas você o ajudará a começar.
Matt Johnson-Pint
2
Eu não recomendaria essa abordagem se a intenção é cobrir o mundo inteiro ou lidar com qualquer identificador de fuso horário válido. A lista é muito longa e muito volátil para isso.
Matt Johnson-Pint