Converter hora UTC / GMT para hora local

301

Estamos desenvolvendo um aplicativo C # para um cliente de serviço da web. Isso será executado nos PCs com Windows XP.

Um dos campos retornados pelo serviço da web é um campo DateTime. O servidor retorna um campo no formato GMT, ou seja, com um "Z" no final.

No entanto, descobrimos que o .NET parece fazer algum tipo de conversão implícita e o tempo estava sempre em 12 horas.

O exemplo de código a seguir resolve isso em certa medida, pois a diferença de 12 horas se foi, mas não leva em consideração o horário de verão da Nova Zelândia.

CultureInfo ci = new CultureInfo("en-NZ");
string date = "Web service date".ToString("R", ci);
DateTime convertedDate = DateTime.Parse(date);            

De acordo com o site desta data :

Deslocamento UTC / GMT

Fuso horário padrão: UTC / GMT +12 horas
Horário de verão: +1 hora
Deslocamento atual do fuso horário: UTC / GMT +13 horas

Como nos ajustamos para a hora extra? Isso pode ser feito programaticamente ou é algum tipo de configuração nos PCs?

rbrayb
fonte
2
o Zhorário refere-se ao UTC, não ao GMT. Os dois podem diferir em até 0,9 segundos.
mc0e 27/02

Respostas:

374

Para cadeias como 2012-09-19 01:27:30.000, DateTime.Parsenão é possível saber de que fuso horário a data e a hora são.

DateTimepossui uma propriedade Kind , que pode ter uma das três opções de fuso horário:

  • Não especificado
  • Local
  • Utc

OBSERVAÇÃO Se você deseja representar uma data / hora diferente do UTC ou do fuso horário local, use-o DateTimeOffset.


Portanto, para o código em sua pergunta:

DateTime convertedDate = DateTime.Parse(dateStr);

var kind = convertedDate.Kind; // will equal DateTimeKind.Unspecified

Você diz que sabe que tipo é, então diga.

DateTime convertedDate = DateTime.SpecifyKind(
    DateTime.Parse(dateStr),
    DateTimeKind.Utc);

var kind = convertedDate.Kind; // will equal DateTimeKind.Utc

Agora, assim que o sistema souber a hora UTC, basta ligar para ToLocalTime:

DateTime dt = convertedDate.ToLocalTime();

Isso lhe dará o resultado desejado.

Drew Noakes
fonte
19
apenas uma outra maneira de especificar o tipo:DateTime convertedTime = new DateTime(DateTime.Parse(dateStr).Ticks), DateTimeKind.Utc);
Brad
não é ToLocalTime ()? @ Brad - suas parênteses não coincidem.
TrueWill
2
Esta solução é responsável pelo horário de verão? Quando tento, saio uma hora.
Bob Horn
7
A etapa de alterar o Kindde DateTimede Unspecifiedpara UTCé desnecessária. Unspecifiedé assumido como tendo UTCos seguintes propósitos ToLocalTime: msdn.microsoft.com/en-us/library/…
CJ7
16
@ CJ7: Sim, mas ser explícito é particularmente útil para outros desenvolvedores que talvez precisem manter o código.
Ryan
121

Eu usaria a classe System.TimeZoneInfo se você estiver no .NET 3.5. Consulte http://msdn.microsoft.com/en-us/library/system.timezoneinfo.aspx . Isso deve levar em consideração as alterações do horário de verão corretamente.

// Coordinated Universal Time string from 
// DateTime.Now.ToUniversalTime().ToString("u");
string date = "2009-02-25 16:13:00Z"; 
// Local .NET timeZone.
DateTime localDateTime = DateTime.Parse(date); 
DateTime utcDateTime = localDateTime.ToUniversalTime();

// ID from: 
// "HKEY_LOCAL_MACHINE\Software\Microsoft\Windows NT\CurrentVersion\Time Zone"
// See http://msdn.microsoft.com/en-us/library/system.timezoneinfo.id.aspx
string nzTimeZoneKey = "New Zealand Standard Time";
TimeZoneInfo nzTimeZone = TimeZoneInfo.FindSystemTimeZoneById(nzTimeZoneKey);
DateTime nzDateTime = TimeZoneInfo.ConvertTimeFromUtc(utcDateTime, nzTimeZone);
Daniel Ballinger
fonte
Se você estiver trabalhando em seu próprio fuso horário (neste caso, en-NZ), não precisará lidar com o TimeZoneInfo. É apenas complexidade desnecessária. Veja minha resposta para mais detalhes.
de Drew Noakes
11
E se as necessidades ninguém, aqui está a lista de fusos horários que eu encontrei para TimeZoneInfo.FindSystemTimeZoneById - codeproject.com/Messages/3867850/...
nikib3ro
Brilhante! Obrigado por este post Dan. Estou procurando essa correção há 3 dias.
Kevin Moore
58
TimeZone.CurrentTimeZone.ToLocalTime(date);
coder1
fonte
8
Isso funciona apenas se o sistema souber que a data da qual está sendo convertida está no UTC. Por favor, veja minha resposta.
de Drew Noakes
1
Mas o UTC é o padrão, não é? Portanto, ele funciona para "não especificado", como na resposta de CJ7.
NickG
25

DateTimeobjetos têm o KinddeUnspecified , por padrão, o que, para efeitos de ToLocalTimese presume ser UTC.

Para obter a hora local de um Unspecified DateTime objeto, basta fazer o seguinte:

convertedDate.ToLocalTime();

A etapa de alterar o Kindde DateTimede Unspecifiedpara UTCé desnecessária. Unspecifiedé considerado UTCpara os fins de ToLocalTime: http://msdn.microsoft.com/en-us/library/system.datetime.tolocaltime.aspx

CJ7
fonte
6
E vice-versa: convertedDate.FromLocalTime();irá converter para UTC.
R. Schreurs
16

Sei que essa é uma pergunta mais antiga, mas me deparei com uma situação semelhante e queria compartilhar o que havia encontrado para futuros pesquisadores, possivelmente incluindo eu :).

DateTime.Parse()pode ser complicado - veja aqui por exemplo.

Se DateTimevier de um serviço da Web ou de alguma outra fonte com um formato conhecido, considere algo como

DateTime.ParseExact(dateString, 
                   "MM/dd/yyyy HH:mm:ss", 
                   CultureInfo.InvariantCulture, 
                   DateTimeStyles.AssumeUniversal | DateTimeStyles.AdjustToUniversal)

ou, melhor ainda,

DateTime.TryParseExact(...)

O AssumeUniversalsinalizador informa ao analisador que a data / hora já é UTC; a combinação de AssumeUniversaleAdjustToUniversal diz para não converter o resultado em horário "local", o que tentará fazer por padrão. (Pessoalmente, tento lidar exclusivamente com o UTC na (s) camada (s) de negócios / aplicativo / serviço. Mas ignorar a conversão para a hora local também acelera as coisas - em 50% ou mais nos meus testes, veja abaixo.)

Aqui está o que estávamos fazendo antes:

DateTime.Parse(dateString, new CultureInfo("en-US"))

Criámos o perfil do aplicativo e descobrimos que o DateTime.Parse representava uma porcentagem significativa do uso da CPU. (Aliás, o CultureInfoconstrutor não estava contribuiu significativamente para o uso da CPU.)

Portanto, configurei um aplicativo de console para analisar uma sequência de data / hora 10000 vezes de várias maneiras. Conclusão:
Parse()10 s
ParseExact()(convertendo para local) 20-45 ms
ParseExact()(não convertendo para local) 10-15 ms
... e sim, os resultados Parse()são em segundos , enquanto os outros estão em milissegundos .

David
fonte
14

Gostaria apenas de adicionar uma nota geral de cautela.

Se tudo o que você está fazendo é obter a hora atual do relógio interno do computador para colocar uma data / hora no visor ou em um relatório, tudo está bem. Mas se você estiver salvando as informações de data / hora para referência posterior ou estiver computando datas / horas, cuidado!

Digamos que você determine que um navio de cruzeiro chegou a Honolulu em 20 de dezembro de 2007 às 15:00 UTC. E você quer saber a hora local.
1. Provavelmente há pelo menos três 'locais' envolvidos. Local pode significar Honolulu ou o local onde o computador está localizado ou o local onde o cliente está localizado.
2. Se você usar as funções internas para fazer a conversão, provavelmente estará errado. Isso ocorre porque o horário de verão está (provavelmente) atualmente em vigor no seu computador, mas NÃO estava em vigor em dezembro. Mas o Windows não sabe disso ... tudo o que tem é um sinalizador para determinar se o horário de verão está em vigor no momento. E, se estiver em vigor no momento, adicionará uma hora para uma data de dezembro.
3)O horário de verão é implementado de maneira diferente (ou não existe) em várias subdivisões políticas. Não pense que apenas porque seu país muda em uma data específica, outros países também o farão.

Mark T
fonte
6
Na verdade, o número 2 não está totalmente correto. De fato, existem regras sobre o horário de verão em todos os fusos horários em que o computador saberá se as informações foram instaladas (e atualizadas). Para muitas zonas, essas regras são fixas. Outros implementam "horário de verão dinâmico". O Brasil é meu animal de estimação por isso. Em resumo, porém, seu coputer pode descobrir se o horário local seria o horário de verão em dezembro, supondo que nenhuma alteração seja aprovada dentro de agora.
Roger Willcocks
Mesmo se você não mora no Brasil, o horário de verão é "dinâmico", na medida em que os políticos podem alterá-lo a qualquer momento (como foi feito há alguns anos nos EUA). Como a maioria dos softwares é escrita com o uso futuro em mente, é importante perceber que NÃO existe uma maneira prática, previsível ou até teórica de saber quais regras de horário de verão serão efetivadas. Você pode se aproximar, mas economize um pouco de frustração ao desistir da perfeição.
21414 DaveMalley #
6
@TimeZoneInfo.ConvertTimeFromUtc(timeUtc, TimeZoneInfo.Local)
Prince Prasad
fonte
5

Não esqueça que se você já possui um objeto DateTime e não tem certeza se é UTC ou Local, é fácil usar os métodos diretamente no objeto:

DateTime convertedDate = DateTime.Parse(date);
DateTime localDate = convertedDate.ToLocalTime();

Como nos ajustamos para a hora extra?

A menos que o .net especificado use as configurações locais do PC. Eu teria uma leitura de: http://msdn.microsoft.com/en-us/library/system.globalization.daylighttime.aspx

Pela aparência, o código pode ser algo como:

DaylightTime daylight = TimeZone.CurrentTimeZone.GetDaylightChanges( year );

E, como mencionado acima, verifique novamente em qual configuração de fuso horário o servidor está. Existem artigos na rede sobre como afetar com segurança as alterações no IIS.

Brendan Kowitz
fonte
O sistema lidará com essa flexibilidade, desde que você informe ao sistema qual é o tipo da sua data (local / utc / não especificado).
de Drew Noakes
2

Em resposta à sugestão de Dana:

O exemplo de código agora se parece com:

string date = "Web service date"..ToString("R", ci);
DateTime convertedDate = DateTime.Parse(date);            
DateTime dt = TimeZone.CurrentTimeZone.ToLocalTime(convertedDate);

A data original era 20/08/08; o tipo era UTC.

Tanto "convertedDate" como "dt" são os mesmos:

21/08/08 10:00:26; o tipo era local

rbrayb
fonte
Por favor, veja minha resposta para uma explicação disso.
de Drew Noakes
1

Eu tive o problema de estar em um conjunto de dados sendo enviado através do fio (serviço da web para o cliente) que seria alterado automaticamente porque o campo DateType do DataColumn estava definido como local. Certifique-se de verificar qual é o DateType se você estiver pressionando DataSets.

Se você não deseja que ele mude, defina-o como Não especificado

Milhas
fonte
1

Me deparei com essa pergunta porque estava tendo um problema com as datas UTC em que você retorna pela API do twitter (campo created_at em um status); Eu preciso convertê-los para DateTime. Nenhuma das respostas / amostras de código nas respostas desta página foi suficiente para me impedir de receber um erro "A string não foi reconhecida como um DateTime válido" (mas é o mais próximo que tenho de encontrar a resposta correta no SO)

A publicação deste link aqui, caso isso ajude alguém - a resposta que eu precisava foi encontrada nesta postagem do blog: http://www.wduffy.co.uk/blog/parsing-dates-when-aspnets-datetimeparse-doesnt-work/ - basicamente use DateTime.ParseExact com uma string de formato em vez de DateTime.Parse

DannykPowell
fonte