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?


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)));

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);

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);

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”.


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
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.