Convertir l'heure UTC / GMT en heure locale

301

Nous développons une application C # pour un client de service Web. Cela fonctionnera sur les PC Windows XP.

L'un des champs renvoyés par le service Web est un champ DateTime. Le serveur renvoie un champ au format GMT c'est-à-dire avec un "Z" à la fin.

Cependant, nous avons constaté que .NET semble faire une sorte de conversion implicite et le temps était toujours de 12 heures.

L'exemple de code suivant résout ce problème dans une certaine mesure en ce que la différence de 12 heures a disparu, mais il ne prend pas en compte l'heure d'été NZ.

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

Selon ce site date :

Décalage UTC / GMT

Fuseau horaire standard: UTC / GMT +12 heures
Heure d'été: +1 heure
Décalage horaire actuel: UTC / GMT +13 heures

Comment ajustons-nous pour l'heure supplémentaire? Cela peut-il être fait par programme ou s'agit-il d'une sorte de paramètre sur les PC?

rbrayb
la source
2
l' Zheure se réfère à UTC, pas à GMT. Les deux peuvent différer jusqu'à 0,9 seconde.
mc0e

Réponses:

374

Pour les chaînes telles que 2012-09-19 01:27:30.000, DateTime.Parsene peut pas dire de quel fuseau horaire proviennent la date et l'heure.

DateTimea une propriété Kind , qui peut avoir l'une des trois options de fuseau horaire:

  • Non spécifié
  • Local
  • UTC

REMARQUE Si vous souhaitez représenter une date / heure autre que UTC ou votre fuseau horaire local, vous devez utiliser DateTimeOffset.


Donc, pour le code dans votre question:

DateTime convertedDate = DateTime.Parse(dateStr);

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

Vous dites que vous savez de quel type il s'agit, alors dites-le.

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

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

Maintenant, une fois que le système connaît son heure UTC, vous pouvez simplement appeler ToLocalTime:

DateTime dt = convertedDate.ToLocalTime();

Cela vous donnera le résultat dont vous avez besoin.

Drew Noakes
la source
19
juste une autre façon de spécifier le type:DateTime convertedTime = new DateTime(DateTime.Parse(dateStr).Ticks), DateTimeKind.Utc);
Brad
n'est-ce pas ToLocalTime ()? @Brad - vos parens ne correspondent pas.
TrueWill
2
Cette solution tient-elle compte de l'heure d'été? Quand je l'essaye, je pars d'une heure.
Bob Horn
7
L'étape de changement du Kinddu DateTimede Unspecifiedà UTCest inutile. Unspecifiedest supposé être UTCaux fins de ToLocalTime: msdn.microsoft.com/en-us/library/…
CJ7
16
@ CJ7: Oui, mais être explicite est particulièrement utile aux autres développeurs qui peuvent avoir à maintenir le code.
Ryan
121

Je chercherais à utiliser la classe System.TimeZoneInfo si vous êtes dans .NET 3.5. Voir http://msdn.microsoft.com/en-us/library/system.timezoneinfo.aspx . Cela devrait prendre en compte les changements de l'heure d'été correctement.

// 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
la source
Si vous travaillez dans votre propre fuseau horaire (en-NZ dans ce cas), vous n'avez pas besoin de traiter avec TimeZoneInfo. C'est juste une complexité inutile. Voir ma réponse pour plus de détails.
Drew Noakes
11
Et si les besoins de tout le monde, voici la liste des fuseaux horaires que j'ai trouvé pour TimeZoneInfo.FindSystemTimeZoneById - codeproject.com/Messages/3867850/...
nikib3ro
Brillant! Merci pour ce post Dan. Je cherche ce correctif depuis 3 jours.
Kevin Moore
58
TimeZone.CurrentTimeZone.ToLocalTime(date);
coder1
la source
8
Cela ne fonctionne que si le système sait que la date de conversion est en UTC. Veuillez voir ma réponse.
Drew Noakes
1
Mais l'UTC est la valeur par défaut, n'est-ce pas? Par conséquent, cela fonctionne pour "non spécifié" comme dans la réponse de CJ7.
NickG
25

DateTimeles objets ont le Kindde Unspecifiedpar défaut, aux fins de ToLocalTimeest supposé être UTC.

Pour obtenir l'heure locale d'un Unspecified DateTimeobjet, il vous suffit donc de faire ceci:

convertedDate.ToLocalTime();

L'étape de changement du Kinddu DateTimede Unspecifiedà UTCest inutile. Unspecifiedest supposé être UTCutilisé aux fins suivantes ToLocalTime: http://msdn.microsoft.com/en-us/library/system.datetime.tolocaltime.aspx

CJ7
la source
6
Et vice versa: convertedDate.FromLocalTime();se convertira en UTC.
R. Schreurs
16

Je sais que c'est une question plus ancienne, mais je suis tombé sur une situation similaire, et je voulais partager ce que j'avais trouvé pour les futurs chercheurs, peut-être moi-même :).

DateTime.Parse()peut être délicat - voir ici par exemple.

Si le produit DateTimeprovient d'un service Web ou d'une autre source avec un format connu, vous voudrez peut-être envisager quelque chose comme

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

ou, mieux encore,

DateTime.TryParseExact(...)

L' AssumeUniversalindicateur indique à l'analyseur que la date / heure est déjà UTC; la combinaison de AssumeUniversalet lui AdjustToUniversaldit de ne pas convertir le résultat en heure "locale", ce qu'il essaiera de faire par défaut. (J'essaie personnellement de traiter exclusivement avec UTC dans les couches entreprise / application / service de toute façon. Mais contourner la conversion en heure locale accélère également les choses - de 50% ou plus dans mes tests, voir ci-dessous.)

Voici ce que nous faisions auparavant:

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

Nous avions profilé l'application et constaté que le DateTime.Parse représentait un pourcentage important de l'utilisation du processeur. (Soit dit en passant, le CultureInfoconstructeur n'était pas un contributeur important à l'utilisation du processeur.)

J'ai donc mis en place une application console pour analyser une chaîne de date / heure 10000 fois de différentes manières. Conclusion:
Parse()10 sec
ParseExact()(conversion en local) 20-45 ms
ParseExact()(pas de conversion en local) 10-15 ms
... et oui, les résultats pour Parse()sont en secondes , tandis que les autres sont en millisecondes .

David
la source
14

Je voudrais juste ajouter une note générale de prudence.

Si tout ce que vous faites est d'obtenir l'heure actuelle de l'horloge interne de l'ordinateur pour afficher une date / heure sur l'affichage ou un rapport, alors tout va bien. Mais si vous enregistrez les informations de date / heure pour référence ultérieure ou calculez la date / heure, méfiez-vous!

Disons que vous déterminez qu'un bateau de croisière est arrivé à Honolulu le 20 décembre 2007 à 15h00 UTC. Et vous voulez savoir quelle heure locale c'était.
1. Il y a probablement au moins trois «locaux» impliqués. Local peut signifier Honolulu, ou cela peut signifier où se trouve votre ordinateur, ou cela peut signifier l'emplacement où se trouve votre client.
2. Si vous utilisez les fonctions intégrées pour effectuer la conversion, ce sera probablement faux. Cela est dû au fait que l'heure d'été est (probablement) actuellement en vigueur sur votre ordinateur, mais n'était PAS en vigueur en décembre. Mais Windows ne le sait pas ... il ne dispose que d'un seul indicateur pour déterminer si l'heure d'été est actuellement en vigueur. Et si elle est actuellement en vigueur, elle ajoutera volontiers une heure même à une date en décembre.
3.L'heure d'été est mise en œuvre différemment (ou pas du tout) dans les différentes subdivisions politiques. Ne pensez pas que ce n'est pas parce que votre pays change à une date précise que d'autres pays le feront aussi.

Mark T
la source
6
En fait, # 2 n'est pas tout à fait correct. Il existe en fait des règles concernant l'heure d'été dans chaque fuseau horaire que votre ordinateur saura si les informations ont été installées (et mises à jour). Pour de nombreuses zones, ces règles sont fixes. D'autres implémentent le "DST dynamique". Le Brésil est ma bête noire pour ça. En résumé, votre ordinateur peut déterminer si votre heure locale est l'heure d'été en décembre, en supposant qu'aucun changement ne soit adopté d'ici là.
Roger Willcocks
Même si vous ne vivez pas au Brésil, l'heure d'été est "dynamique" dans la mesure où les politiciens peuvent la changer à tout moment (comme cela a été fait il y a quelques années aux États-Unis). Comme la plupart des logiciels sont écrits en vue d'une utilisation future, il est important de réaliser qu'il n'y a AUCUN moyen pratique, prévisible ou même théorique de savoir quelles règles DST seront en vigueur. Vous pouvez vous approcher, mais évitez la frustration en abandonnant la perfection.
DaveWalley
6
@TimeZoneInfo.ConvertTimeFromUtc(timeUtc, TimeZoneInfo.Local)
Prince Prasad
la source
5

N'oubliez pas que si vous avez déjà un objet DateTime et que vous n'êtes pas sûr qu'il soit UTC ou Local, il est assez facile d'utiliser directement les méthodes sur l'objet:

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

Comment ajustons-nous pour l'heure supplémentaire?

Sauf indication contraire, .net utilisera les paramètres du PC local. Je voudrais lire: http://msdn.microsoft.com/en-us/library/system.globalization.daylighttime.aspx

Par l'apparence, le code pourrait ressembler à quelque chose comme:

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

Et comme mentionné ci-dessus, vérifiez à nouveau le réglage du fuseau horaire sur votre serveur. Il existe des articles sur le net pour savoir comment affecter en toute sécurité les modifications apportées à IIS.

Brendan Kowitz
la source
Le système gérera cette complexité pour vous, à condition que vous indiquiez au système quel «type» votre date est (local / utc / non spécifié).
Drew Noakes
2

En réponse à la suggestion de Dana:

L'exemple de code ressemble maintenant à:

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

La date d'origine était le 20/08/08; le genre était UTC.

"ConvertDate" et "dt" sont les mêmes:

21/08/08 10:00:26; le genre était local

rbrayb
la source
Veuillez voir ma réponse pour une explication de ceci.
Drew Noakes
1

J'ai eu le problème avec le fait qu'il soit dans un ensemble de données poussé à travers le fil (service Web vers le client) qu'il changerait automatiquement parce que le champ DateType du DataColumn était défini sur local. Assurez-vous de vérifier quel est le DateType si vous transmettez DataSets.

Si vous ne souhaitez pas qu'il change, définissez-le sur Non spécifié

Miles
la source
1

Je suis tombé sur cette question car j'avais un problème avec les dates UTC que vous récupérez via l'API Twitter (champ created_at sur un statut); Je dois les convertir en DateTime. Aucun des réponses / exemples de code dans les réponses sur cette page n'était suffisant pour m'empêcher d'obtenir une erreur "La chaîne n'a pas été reconnue comme une DateTime valide" (mais c'est le plus proche que j'ai pu trouver la bonne réponse sur SO)

Publier ce lien ici au cas où cela aiderait quelqu'un d'autre - la réponse dont j'avais besoin a été trouvée sur ce blog: http://www.wduffy.co.uk/blog/parsing-dates-when-aspnets-datetimeparse-doesnt-work/ - utilisez essentiellement DateTime.ParseExact avec une chaîne de format au lieu de DateTime.Parse

DannykPowell
la source