Konvertieren Sie die UTC / GMT-Zeit in die Ortszeit

301

Wir entwickeln eine C # -Anwendung für einen Webdienst-Client. Dies läuft auf Windows XP-PCs.

Eines der vom Webdienst zurückgegebenen Felder ist ein DateTime-Feld. Der Server gibt ein Feld im GMT-Format zurück, dh mit einem "Z" am Ende.

Wir haben jedoch festgestellt, dass .NET eine implizite Konvertierung durchzuführen scheint und die Zeit immer 12 Stunden abgelaufen ist.

Das folgende Codebeispiel behebt dies bis zu einem gewissen Grad, indem der Unterschied von 12 Stunden weg ist, die Sommerzeit in Neuseeland jedoch nicht berücksichtigt wird.

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

Ab diesem Datum Website :

UTC / GMT-Offset

Standardzeitzone: UTC / GMT +12 Stunden
Sommerzeit: +1 Stunde
Aktueller Zeitzonenversatz: UTC / GMT +13 Stunden

Wie stellen wir uns auf die zusätzliche Stunde ein? Kann dies programmgesteuert erfolgen oder handelt es sich um eine Einstellung auf dem PC?

rbrayb
quelle
2
Die ZZeit bezieht sich auf UTC, nicht auf GMT. Die beiden können sich um bis zu 0,9 Sekunden unterscheiden.
mc0e

Antworten:

374

Für Strings wie 2012-09-19 01:27:30.000, DateTime.Parsekann nicht sagen , in welcher Zeitzone Datum und Uhrzeit ist aus.

DateTimehat eine Kind- Eigenschaft, die eine von drei Zeitzonenoptionen haben kann:

  • Nicht spezifiziert
  • Lokal
  • Koordinierte Weltzeit

HINWEIS Wenn Sie ein anderes Datum / eine andere Uhrzeit als UTC oder Ihre lokale Zeitzone darstellen möchten, sollten Sie verwenden DateTimeOffset.


Also für den Code in Ihrer Frage:

DateTime convertedDate = DateTime.Parse(dateStr);

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

Sie sagen, Sie wissen, um welche Art es sich handelt, also sagen Sie es.

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

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

Sobald das System die UTC-Zeit kennt, können Sie einfach anrufen ToLocalTime:

DateTime dt = convertedDate.ToLocalTime();

Dadurch erhalten Sie das gewünschte Ergebnis.

Drew Noakes
quelle
19
nur eine andere Möglichkeit, die Art zu spezifizieren:DateTime convertedTime = new DateTime(DateTime.Parse(dateStr).Ticks), DateTimeKind.Utc);
Brad
ist es nicht ToLocalTime ()? @Brad - deine Eltern passen nicht zusammen.
TrueWill
2
Berücksichtigt diese Lösung die Sommerzeit? Wenn ich es versuche, habe ich eine Stunde frei.
Bob Horn
7
Der Schritt des Wechsels Kindvon DateTimevon Unspecifiednach UTCist nicht erforderlich. Unspecifiedwird angenommen UTCfür die Zwecke von ToLocalTime: msdn.microsoft.com/en-us/library/…
CJ7
16
@ CJ7: Ja, aber explizit zu sein ist besonders hilfreich für andere Entwickler, die möglicherweise den Code pflegen müssen.
Ryan
121

Ich würde die Verwendung der System.TimeZoneInfo-Klasse prüfen, wenn Sie sich in .NET 3.5 befinden. Siehe http://msdn.microsoft.com/en-us/library/system.timezoneinfo.aspx . Dies sollte die Änderungen der Sommerzeit korrekt berücksichtigen.

// 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
quelle
Wenn Sie in Ihrer eigenen Zeitzone arbeiten (in diesem Fall en-NZ), müssen Sie sich nicht mit TimeZoneInfo befassen. Es ist nur unnötige Komplexität. Siehe meine Antwort für weitere Details.
Drew Noakes
11
Und wenn jemand braucht, hier ist die Liste der Zeitzonen, die ich für TimeZoneInfo.FindSystemTimeZoneById
nikib3ro
Brillant! Danke für diesen Beitrag Dan. Ich habe seit 3 ​​Tagen nach diesem Fix gesucht.
Kevin Moore
58
TimeZone.CurrentTimeZone.ToLocalTime(date);
coder1
quelle
8
Dies funktioniert nur, wenn das System weiß, dass das Datum, von dem konvertiert wird, in UTC ist. Bitte sehen Sie meine Antwort.
Drew Noakes
1
Aber UTC ist die Standardeinstellung, nicht wahr? Daher funktioniert es für "nicht spezifiziert" wie in der Antwort von CJ7.
NickG
25

DateTimeObjekte haben die Kindvon Unspecifiedder Standardeinstellung, die für die Zwecke der ToLocalTimeanzunehmen ist UTC.

Um die Ortszeit eines Unspecified DateTimeObjekts zu erhalten, müssen Sie daher nur Folgendes tun:

convertedDate.ToLocalTime();

Der Schritt des Wechsels Kindvon DateTimevon Unspecifiednach UTCist nicht erforderlich. Unspecifiedwird UTCfür folgende Zwecke angenommen ToLocalTime: http://msdn.microsoft.com/en-us/library/system.datetime.tolocaltime.aspx

CJ7
quelle
6
Und umgekehrt: convertedDate.FromLocalTime();wird konvertieren zu UTC.
R. Schreurs
16

Ich weiß, dass dies eine ältere Frage ist, aber ich bin in eine ähnliche Situation geraten und wollte mitteilen, was ich für zukünftige Suchende gefunden habe, möglicherweise auch für mich selbst :).

DateTime.Parse()kann schwierig sein - siehe hier zum Beispiel.

Wenn das DateTimevon einem Webdienst oder einer anderen Quelle mit einem bekannten Format stammt, sollten Sie etwas in Betracht ziehen

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

oder noch besser,

DateTime.TryParseExact(...)

Das AssumeUniversalFlag teilt dem Parser mit, dass Datum und Uhrzeit bereits UTC sind. Die Kombination von AssumeUniversalund AdjustToUniversalweist es an, das Ergebnis nicht in "lokale" Zeit zu konvertieren, was standardmäßig versucht wird. (Ich persönlich versuche sowieso, mich ausschließlich mit UTC in der Geschäfts- / Anwendungs- / Service-Schicht (en) zu befassen. Das Umgehen der Umstellung auf Ortszeit beschleunigt jedoch auch die Dinge - in meinen Tests um 50% oder mehr, siehe unten.)

Folgendes haben wir zuvor gemacht:

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

Wir hatten die App profiliert und festgestellt, dass DateTime.Parse einen signifikanten Prozentsatz der CPU-Auslastung darstellt. (Der CultureInfoKonstruktor trug übrigens nicht wesentlich zur CPU-Auslastung bei.)

Deshalb habe ich eine Konsolen-App eingerichtet, um eine Datums- / Zeitzeichenfolge 10000 Mal auf verschiedene Arten zu analysieren. Fazit:
Parse()10 Sekunden
ParseExact()(Konvertierung in lokal) 20-45 ms
ParseExact()(keine Konvertierung in lokal) 10-15 ms
... und ja, die Ergebnisse für Parse()sind in Sekunden angegeben , während die anderen in Millisekunden angegeben sind .

David
quelle
14

Ich möchte nur einen allgemeinen Hinweis zur Vorsicht hinzufügen.

Wenn Sie nur die aktuelle Uhrzeit von der internen Uhr des Computers abrufen, um ein Datum / eine Uhrzeit auf dem Display oder einen Bericht anzuzeigen, ist alles in Ordnung. Aber wenn Sie die Datums- / Uhrzeitinformationen zur späteren Bezugnahme speichern oder Datum / Uhrzeit berechnen , seien Sie vorsichtig!

Angenommen, Sie stellen fest, dass ein Kreuzfahrtschiff am 20. Dezember 2007 um 15:00 UTC in Honolulu eingetroffen ist. Und Sie möchten wissen, wie spät es war.
1. Es sind wahrscheinlich mindestens drei "Einheimische" beteiligt. Lokal kann Honolulu bedeuten, oder es kann bedeuten, wo sich Ihr Computer befindet, oder es kann bedeuten, wo sich Ihr Kunde befindet.
2. Wenn Sie die integrierten Funktionen verwenden, um die Konvertierung durchzuführen, ist dies wahrscheinlich falsch. Dies liegt daran, dass die Sommerzeit (wahrscheinlich) derzeit auf Ihrem Computer gültig ist, aber im Dezember NICHT in Kraft war. Aber Windows weiß das nicht ... alles, was es hat, ist ein Flag, um festzustellen, ob die Sommerzeit derzeit gültig ist. Und wenn es derzeit in Kraft ist, wird es sogar eine Stunde zu einem Datum im Dezember hinzufügen.
3.Die Sommerzeit wird in verschiedenen politischen Unterteilungen unterschiedlich (oder gar nicht) umgesetzt. Denken Sie nicht, dass andere Länder dies auch tun, nur weil sich Ihr Land an einem bestimmten Datum ändert.

Mark T.
quelle
6
Eigentlich ist # 2 nicht ganz richtig. Tatsächlich gibt es in jeder Zeitzone Regeln für die Sommerzeit, die Ihr Computer kennt, wenn die Informationen installiert (und aktualisiert) wurden. Für viele Zonen sind diese Regeln festgelegt. Andere implementieren "dynamische Sommerzeit". Brasilien ist mein Liebling dafür. Zusammenfassend kann Ihr Computer jedoch herausfinden, ob Ihre Ortszeit im Dezember Sommerzeit ist, vorausgesetzt, dass bis dahin keine Gesetzesänderungen in Kraft treten.
Roger Willcocks
Auch wenn Sie nicht in Brasilien leben, ist die Sommerzeit insofern "dynamisch", als die Politik sie jederzeit ändern kann (wie dies vor einigen Jahren in den USA geschehen ist). Da die meisten Softwareprodukte für die zukünftige Verwendung geschrieben wurden, ist es wichtig zu wissen, dass es KEINE praktische, vorhersehbare oder sogar theoretische Methode gibt, um zu wissen, welche DST-Regeln wirksam sein werden. Sie können sich nähern, aber sparen Sie sich etwas Frust, indem Sie die Perfektion aufgeben.
DaveWalley
6
@TimeZoneInfo.ConvertTimeFromUtc(timeUtc, TimeZoneInfo.Local)
Prinz Prasad
quelle
5

Vergessen Sie nicht, wenn Sie bereits ein DateTime-Objekt haben und nicht sicher sind, ob es sich um UTC oder Local handelt. Es ist einfach genug, die Methoden direkt für das Objekt zu verwenden:

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

Wie stellen wir uns auf die zusätzliche Stunde ein?

Sofern nicht anders angegeben, verwendet .net die lokalen PC-Einstellungen. Ich würde lesen von: http://msdn.microsoft.com/en-us/library/system.globalization.daylighttime.aspx

Wie es aussieht, könnte der Code ungefähr so ​​aussehen:

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

Überprüfen Sie, wie oben erwähnt, die Zeitzoneneinstellung Ihres Servers. Es gibt Artikel im Internet, wie Sie die Änderungen in IIS sicher beeinflussen können.

Brendan Kowitz
quelle
Das System wird diese Komplexität für Sie erledigen, vorausgesetzt, Sie teilen dem System mit, um welche Art von Datum es sich handelt (lokal / utc / nicht angegeben).
Drew Noakes
2

Als Antwort auf Danas Vorschlag:

Das Codebeispiel sieht nun so aus:

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

Das ursprüngliche Datum war der 20.08.08; Die Art war UTC.

Sowohl "convertDate" als auch "dt" sind gleich:

21/08/08 10:00:26; Die Art war lokal

rbrayb
quelle
Eine Erklärung hierzu finden Sie in meiner Antwort.
Drew Noakes
1

Ich hatte das Problem, dass es sich in einem Datensatz befand, der über die Leitung (Webservice zum Client) übertragen wurde, und dass es sich automatisch ändern würde, da das DateType-Feld der DataColumn auf local gesetzt war. Stellen Sie sicher, dass Sie den DateType überprüfen, wenn Sie DataSets übertragen.

Wenn Sie nicht möchten, dass es sich ändert, setzen Sie es auf Nicht angegeben

Meilen
quelle
1

Ich bin auf diese Frage gestoßen, als ich ein Problem mit den UTC-Daten hatte, die Sie über die Twitter-API zurückerhalten (Feld "created_at" für einen Status). Ich muss sie in DateTime konvertieren. Keines der Antworten / Codebeispiele in den Antworten auf dieser Seite war ausreichend, um zu verhindern, dass der Fehler "Zeichenfolge wurde nicht als gültige DateTime erkannt" angezeigt wird (aber es ist das Beste, was ich gefunden habe, um die richtige Antwort auf SO zu finden).

Wenn Sie diesen Link hier posten, falls dies jemand anderem hilft, wurde die Antwort, die ich brauchte, in diesem Blogbeitrag gefunden: http://www.wduffy.co.uk/blog/parsing-dates-when-aspnets-datetimeparse-doesnt-work/ - Verwenden Sie DateTime.ParseExact grundsätzlich mit einer Formatzeichenfolge anstelle von DateTime.Parse

DannykPowell
quelle