Jak korzystać z TimeZoneInfo, aby uzyskać czas lokalny w czasie letnim?

85

Staram się wykorzystać DateTimeOffsetdo przekazania określonego momentu w dowolnej strefie czasowej. Nie wiem, jak TimeZoneInforadzić sobie z czasem letnim.

var dt = DateTime.UtcNow;
Console.WriteLine(dt.ToLocalTime());

var tz = TimeZoneInfo.FindSystemTimeZoneById("Central Standard Time");
var utcOffset = new DateTimeOffset(dt, TimeSpan.Zero);
Console.WriteLine(utcOffset.ToOffset(tz.BaseUtcOffset));

To drukuje:

02.06.2010 16:37:19
6/2/2010 15:37:19 PM -06: 00

Jestem w centralnej strefie czasowej, a obecnie obowiązuje czas letni. Próbuję odczytać drugą linijkę:

02.06.2010 16:37:19 -05: 00

BaseUtcOffset najwyraźniej nie zmienia się na podstawie czasu letniego.

Jak uzyskać właściwy czas z odpowiednią wartością przesunięcia?

jaminto
źródło
13
+1 - doprowadza mnie do szaleństwa, że ​​TimeZoneInfo.ConvertTimeBySystemTimeZoneId nie działa po prostu do tego :)
James Manning
@JamesManning - Tak, zakładając, że dt.Kindjest ustawiony poprawnie.
Matt Johnson-Pint

Odpowiedzi:

62

Musisz pobrać UtcOffset z TimeZoneInfo, a następnie przekazać to do metody ToOffset ():

var dt = DateTime.UtcNow;
Console.WriteLine(dt.ToLocalTime());

var tz = TimeZoneInfo.FindSystemTimeZoneById("Central Standard Time");
var utcOffset = new DateTimeOffset(dt, TimeSpan.Zero);
Console.WriteLine(utcOffset.ToOffset(tz.GetUtcOffset(utcOffset)));
Paul Kearney - pk
źródło
rozumiem ... musisz uzyskać przesunięcie czasu UTC dla tej konkretnej daty w strefie czasowej. dzięki.
jaminto
5
Nie jest to najlepszy sposób w porównaniu z odpowiedzią Karla Gjertsena, który wykorzystuje pojedynczą funkcję .Net do wykonania zadania zamiast wyodrębniania przesunięcia poprzez tworzenie nowego, wyrzuconego materiału DateTimeOffset.
ErikE,
59

Możesz również użyć TimeZoneInfo.ConvertTimeFromUtc, co pozwoli na czas letni:

DateTime utc = DateTime.UtcNow;
TimeZoneInfo zone = TimeZoneInfo.FindSystemTimeZoneById("Central Standard Time");
DateTime localDateTime = TimeZoneInfo.ConvertTimeFromUtc(utc, zone);
Karl Gjertsen
źródło
A jeśli strefa czasowa mojego kraju to EEST latem i EET zimą? Jak to zadziała w przypadku czasu letniego?
Rami Zebian
1
System Windows zna informacje o strefie czasowej wraz z datami czasu letniego. Powinno to rozwiązać lub jeśli wybierzesz jedną z predefiniowanych stref czasowych.
Karl Gjertsen
11

Lub lepiej, jeśli nie chcesz na stałe zakodować identyfikatora strefy czasowej :

TimeZoneInfo tzi = TimeZoneInfo.FindSystemTimeZoneById(TimeZoneInfo.Local.Id);
DateTime localDateTime = TimeZoneInfo.ConvertTimeFromUtc(DateTime.UtcNow, tzi);
Pabinator
źródło
2
Nie mógłbyś po prostu użyć TimeZoneInfo.Localbezpośrednio? Dlaczego potrzebujesz telefonu FindSystemTimeZoneById?
CoderDennis
4
Masz rację, nie potrzebujemy tego. Możemy to zrobić w ten sposóbDateTime localDateTime = TimeZoneInfo.ConvertTimeFromUtc(networkDateTime, TimeZoneInfo.Local);
Pabinator
1
Ale wtedy jesteś przywiązany do jednej strefy czasowej. Chyba zależy od tego, jak chcesz użyć kodu. :-)
Karl Gjertsen
9
To nie jest zbyt bezpieczne. Jeśli outsourcujesz swoją aplikację do środowiska chmurowego, nie wiesz, która strefa czasowa jest lokalna per se.
Tom
5

Jestem początkującym zarówno w .NET, jak i stackoverflow, więc mogę się mylić, ale oto:

Korzystanie z TimeZoneInfo.ConvertTimeFromUtc pozwoli na czas letni i przekonwertuje go na prawidłowy czas zgodnie ze strefą czasową + możliwe przesunięcie czasu letniego. Jednak - samo przesunięcie w wynikowym obiekcie pokaże przesunięcie dla czasu standardowego i nie będzie uwzględniać czasu letniego. Więc jeśli chcesz wykonać ToString na obiekcie, skończysz z poprawnym czasem (w godzinach i minutach), ale z niewłaściwym przesunięciem w czasie czasu letniego, co może prowadzić do niewłaściwego momentu później w kodzie.

Jeśli zamiast tego użyjesz GetUtcOffset, aby uzyskać przesunięcie dla określonego czasu, a następnie wykonasz ToOffset w obiekcie DateTimeOffset, zarówno godziny / minuty, jak i samo przesunięcie zostaną poprawnie przekonwertowane i możesz bezpiecznie wykonać ToString.

string ExpectedDateTimePattern = "yyyy'-'MM'-'dd'T'HH':'mm':'ss''zzz";
string timeZoneId = "FLE Standard Time";
string dateTimestr = "2017-10-09T09:00:00+02:00";

DateTimeOffset dto = DateTimeOffset.Parse(dateTimeStr);
TimeZoneInfo zone = TimeZoneInfo.FindSystemTimeZoneById(timeZoneId);
TimeSpan offset = zone.GetUtcOffset(dto);
dto = dto.ToOffset(offset);
string localTime = dto.ToString(ExpectedDateTimePattern);

localTime zwróci „2017-10-09T10: 00: 00 + 03: 00”.

Niels Pein
źródło
Według PO to jest prawidłowa odpowiedź. Najważniejszą rzeczą do zrozumienia jest to, że UtcOffset dla strefy czasowej nie jest stała, a zamiast tego zależy od samej daty, o czym mówimy, ze względu na zasady czasu letniego . Więc jedynym błędem, jaki zrobił OP, jest użycie stałej BaseUtcOffset zamiast GetUtcOffset (myDate)
g.pickardou