Як використовувати 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.