Как использовать TimeZoneInfo, чтобы узнать местное время в летнее время?


85

Я пытаюсь использовать, 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он установлен правильно.
— Мэтт Джонсон-

Ответы:


63

Вам нужно получить 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

6
Это не лучший способ по сравнению с ответом Карла Гьертсена, который использует одну функцию .Net для выполнения работы вместо извлечения смещения путем создания нового выброса DateTimeOffset.
— ErikE

60

Вы также можете использовать TimeZoneInfo.ConvertTimeFromUtc, который позволит перейти на летнее время:

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

Что, если часовой пояс моей страны EEST летом и EET зимой? Как это будет работать с переходом на летнее время?
— Rami Zebian

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);
— Пабинатор

1
Но тогда вы привязаны к одному часовому поясу. Я думаю, это зависит от того, как вы хотите использовать код. :-)
— Karl Gjertsen 05

9
Это не очень безопасно. Если вы переносите свое приложение в облачную среду, вы не знаете, какой часовой пояс по сути является локальным.
— Tom

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


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