Как использовать 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 очевидно, не меняется в зависимости от летнего времени.

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

Jaminto
источник
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)));
Пол Кирни - pk
источник
я вижу ... вам нужно получить смещение 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