Як використовувати TimeZoneInfo для отримання місцевого часу в перехід на літній та зимовий час?


84

Я намагаюся використати, DateTimeOffsetщоб передати конкретний момент часу в будь-якому часовому поясі. Я не можу зрозуміти, як це використовувати TimeZoneInfoдля переходу на літній час.

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

Це роздруковує:

02.06.2010 16:37:19
02.06.2010 15:37:19 -06: 00

Я перебуваю в центральному часовому поясі, і зараз ми переходимо на літній час. Я намагаюся отримати другий рядок для читання:

02.06.2010 16:37:19 -05: 00

BaseUtcOffset мабуть, не змінюється на основі літнього часу.

Як я можу отримати правильний час із належним значенням зміщення?


13
+1 - це зводить мене з розуму, що TimeZoneInfo.ConvertTimeBySystemTimeZoneId не просто працює для цього :)
Джеймс Меннінг

@JamesManning - Це робить, припускаючи, що dt.Kindвстановлено правильно.
Matt Johnson-Pint

Відповіді:


61

Вам потрібно отримати UtcOffset з TimeZoneInfo, а потім передати його методу 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)));

я бачу ... ви повинні отримати зміщення UTC для тієї конкретної дати в часовому поясі. Дякую.
jaminto

5
Це не найкращий спосіб порівняно з відповіддю Карла Gерцена, який використовує одну функцію .Net для виконання роботи, замість того, щоб витягувати зсув шляхом створення нового викидання DateTimeOffset.
ErikE

59

Ви також можете використовувати TimeZoneInfo.ConvertTimeFromUtc, що дозволить переходити на літній час:

DateTime utc = DateTime.UtcNow;
TimeZoneInfo zone = TimeZoneInfo.FindSystemTimeZoneById("Central Standard Time");
DateTime localDateTime = TimeZoneInfo.ConvertTimeFromUtc(utc, zone);

Що робити, якщо часовий пояс моєї країни EEST влітку і EET взимку? Як би це працювало з переходом на літній час?
Рамі Зебіан,

1
Windows знає інформацію про часовий пояс, а також літній час. Це має вирішити це, або ви, якщо ви оберете один із заздалегідь визначених часових поясів.
Karl Gjertsen

11

Або краще, якщо ви не хочете жорстко кодувати ідентифікатор часового поясу :

TimeZoneInfo tzi = TimeZoneInfo.FindSystemTimeZoneById(TimeZoneInfo.Local.Id);
DateTime localDateTime = TimeZoneInfo.ConvertTimeFromUtc(DateTime.UtcNow, tzi);

2
Не могли б ви просто використовувати TimeZoneInfo.Localбезпосередньо? Навіщо потрібен дзвінок FindSystemTimeZoneById?
CoderDennis

4
Ви маєте рацію, нам це не потрібно. Ми можемо зробити це такDateTime localDateTime = TimeZoneInfo.ConvertTimeFromUtc(networkDateTime, TimeZoneInfo.Local);
Pabinator

1
Але тоді ви фіксуєтеся в одному часовому поясі. Я думаю, це залежить від того, як ви хочете використовувати код. :-)
Karl Gjertsen

9
Це не дуже безпечно. Якщо ви передаєте програму в хмарне середовище, ви не знаєте, який часовий пояс є локальним як такий.
Том

5

Я новачок як у .NET, так і у stackoverflow, тому я можу помилитися, але тут йдеться:

Використання TimeZoneInfo.ConvertTimeFromUtc дозволить переходити на літній час та перетворити на правильний час відповідно до часового поясу + можливий зсув літнього часу. Однак - саме зміщення в отриманому об'єкті відображатиме зміщення за стандартний час, а не враховуватиме перехід на літній час. Отже, якщо ви хочете зробити ToString на об’єкті, ви отримаєте правильний час (у годинах та хвилинах), але неправильний зсув у перехід на літній час, що може призвести до неправильного моменту часу в коді.

Якщо ви замість цього використовуєте GetUtcOffset для отримання зміщення на певний час, а потім робите ToOffset на об'єкті DateTimeOffset, як години / хвилини, так і сам зсув будуть правильно перетворені, і ви можете сміливо робити 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 поверне "2017-10-09T10: 00: 00 + 03: 00".


Відповідно до ОП це правильна відповідь. Головне зрозуміти, що UtcOffset для часового поясу не є постійним, натомість це залежить від самої дати, про яку мова йде, через правила переходу на літній час . Отже, єдиною помилкою, яку зробив ОП, є те, що він використав константу BaseUtcOffset замість GetUtcOffset (myDate)
g.pickardou
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.