web-dev-qa-db-fra.com

Conversion de l'heure UTC / GMT en heure locale

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

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 semblait effectuer une sorte de conversion implicite et que le temps était toujours de 12 heures.

L'échantillon de code suivant résout ce problème dans une certaine mesure en ce sens que la différence de 12 heures a disparu mais qu'il ne tient pas compte de l'heure d'été dans la Nouvelle-Zélande.

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

Selon ce site de 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 pouvons-nous ajuster pour l'heure supplémentaire? Cela peut-il être fait par programme ou s'agit-il d'une sorte de réglage sur le PC?

284
nzpcmad

Pour les chaînes telles que 2012-09-19 01:27:30.000, DateTime.Parse ne peut indiquer le fuseau horaire de la date et de l'heure.

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

  • Non spécifié
  • Local
  • UTC

NOTE Si vous souhaitez représenter une date/heure autre que UTC ou votre fuseau horaire local, vous devez utilisez 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 souhaité.

363
Drew Noakes

Je regarderais dans l'utilisation de 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 modifications 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);
117
Daniel Ballinger
TimeZone.CurrentTimeZone.ToLocalTime(date);
50
coder1

Les objets DateTime ont la Kind de Unspecified par défaut, ce qui, aux fins de ToLocalTime, est supposé être UTC.

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

convertedDate.ToLocalTime();

L'étape consistant à changer la Kind de la DateTime de Unspecified à UTC est inutile. Unspecified est supposé être UTC aux fins de ToLocalTime: http://msdn.Microsoft.com/en-us/library/system.datetime.tolocaltime. aspx

23
CJ7

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

DateTime.Parse() peut être difficile - voir ici par exemple.

Si la DateTime provient d'un service Web ou d'une autre source dont le format est connu, vous pouvez envisager quelque chose comme:

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

ou encore mieux

DateTime.TryParseExact(...)

L'indicateur AssumeUniversal indique à l'analyseur que la date/heure est déjà UTC; la combinaison de AssumeUniversal et AdjustToUniversal lui dit de ne pas convertir le résultat en heure "locale", ce qu'il tentera de faire par défaut. (J'essaie personnellement de traiter exclusivement avec UTC dans les couches métier/application/service. Quoi qu'il en soit, éviter 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 DateTime.Parse représentait un pourcentage important de l'utilisation du processeur. (Incidemment, le constructeur CultureInfo était pas un contributeur important à l'utilisation du processeur.)

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

16
David

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

Si vous ne faites qu'obtenir l'heure actuelle de l'horloge interne de l'ordinateur pour afficher une date/heure à l'écran ou un rapport, tout va bien. Mais si tu es économie les informations de date/heure pour référence ultérieure ou sont l'informatique date/heure, méfiez-vous!

Supposons qu'un bateau de croisière soit arrivé à Honolulu le 20 décembre 2007 à 15h00 UTC. Et vous voulez savoir quelle heure il était.
1. Il y a probablement au moins trois "sections locales" impliquées. Local peut signifier Honolulu, ou peut indiquer l'emplacement de votre ordinateur, ou l'emplacement de votre client.
2. Si vous utilisez les fonctions intégrées pour effectuer la conversion, ce sera probablement faux. En effet, l'heure d'été est (probablement) actuellement en vigueur sur votre ordinateur, mais ne l'était PAS en décembre. Mais Windows ne le sait pas ... il n’ya qu’un seul drapeau pour déterminer si l’heure avancée est en vigueur. Et s'il est actuellement en vigueur, il sera alors heureux d'ajouter 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, simplement parce que votre pays change à une date précise, les autres pays le feront aussi.

14
Mark T

N'oubliez pas que si vous avez déjà un objet DateTime et que vous ne savez pas si c'est UTC ou Local, il est assez facile d'utiliser les méthodes directement sur l'objet:

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

Comment pouvons-nous ajuster pour l'heure supplémentaire?

Sauf indication contraire, .net utilisera les paramètres du PC local. J'aurais une lecture de: http://msdn.Microsoft.com/en-us/library/system.globalization.daylighttime.aspx

En apparence, le code pourrait ressembler à quelque chose comme:

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

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

4
Brendan Kowitz
@TimeZoneInfo.ConvertTimeFromUtc(timeUtc, TimeZoneInfo.Local)
4
Prince Prasad

En réponse à la suggestion de Dana:

L'échantillon 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 initiale était le 20/08/08; le genre était UTC.

"ConvertedDate" et "dt" sont identiques:

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

2
nzpcmad

Je suis tombé sur cette question car j'avais un problème avec les dates UTC que vous recevez via l'API Twitter (champ created_at sur un statut); J'ai besoin de les convertir en DateTime. Aucune des réponses/exemples de code dans les réponses sur cette page ne suffisait à m'empêcher de recevoir l'erreur "La chaîne n'a pas été reconnue comme une dateTime valide" (mais c'est le point le plus proche que j'ai pour trouver la bonne réponse sous 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 dans cet article de blog: http://www.wduffy.co.uk/blog/parsing-dates-when-aspnets-datetimeparse- doesnt-work / - utilise essentiellement DateTime.ParseExact avec une chaîne de format au lieu de DateTime.Parse

1
DannykPowell

Le problème qui se posait dans le fait qu'un groupe de données était poussé sur le réseau (service Web au client) était automatiquement modifié, car le champ DateType de DataColumn était défini sur local. Assurez-vous de vérifier le type DateType si vous transmettez des ensembles de données.

Si vous ne souhaitez pas le modifier, définissez-le sur Non spécifié.

1
Miles