Конвертировать время UTC / GMT по местному времени

301

Мы разрабатываем приложение на C # для клиента веб-сервиса. Это будет работать на ПК с Windows XP.

Одним из полей, возвращаемых веб-службой, является поле DateTime. Сервер возвращает поле в формате GMT, то есть с буквой «Z» в конце.

Тем не менее, мы обнаружили, что .NET, похоже, выполняет какое-то неявное преобразование, и время всегда было 12 часов.

Следующий пример кода разрешает это до некоторой степени, поскольку разница в 12 часов исчезла, но не учитывает переход на летнее время в Новой Зеландии.

CultureInfo ci = new CultureInfo("en-NZ");
string date = "Web service date".ToString("R", ci);
DateTime convertedDate = DateTime.Parse(date);            

Согласно этой дате сайт :

Смещение UTC / GMT

Стандартный часовой пояс: UTC / GMT +12 часов
Летнее время: +1 час
Текущее смещение часового пояса: UTC / GMT +13 часов

Как нам отрегулировать дополнительный час? Это можно сделать программно или это какая-то настройка на ПК?

rbrayb
источник
2
Zвремя относится к UTC, а не по Гринвичу. Два могут отличаться на 0,9 секунды.
mc0e

Ответы:

374

Для таких строк, как 2012-09-19 01:27:30.000, DateTime.Parseне могу сказать, из какого часового пояса дата и время.

DateTimeимеет свойство Kind , которое может иметь один из трех параметров часового пояса:

  • Неопределенные
  • Местный
  • Универсальное глобальное время

ПРИМЕЧАНИЕ Если вы хотите указать дату / время, отличное от UTC или вашего местного часового пояса, вам следует использовать DateTimeOffset.


Итак, для кода в вашем вопросе:

DateTime convertedDate = DateTime.Parse(dateStr);

var kind = convertedDate.Kind; // will equal DateTimeKind.Unspecified

Вы говорите, что знаете, какой это, так что скажите это.

DateTime convertedDate = DateTime.SpecifyKind(
    DateTime.Parse(dateStr),
    DateTimeKind.Utc);

var kind = convertedDate.Kind; // will equal DateTimeKind.Utc

Теперь, когда система знает время в UTC, вы можете просто позвонить ToLocalTime:

DateTime dt = convertedDate.ToLocalTime();

Это даст вам требуемый результат.

Дрю Ноакс
источник
19
просто еще один способ указать вид:DateTime convertedTime = new DateTime(DateTime.Parse(dateStr).Ticks), DateTimeKind.Utc);
Брэд
разве это не ToLocalTime ()? @Brad - ваши парни не совпадают.
TrueWill
2
Это решение учитывает летнее время? Когда я пытаюсь это сделать, я ухожу на час.
Боб Хорн
7
Шаг изменения Kindзначения DateTimeс Unspecifiedна UTCна ненужный. Unspecifiedпредполагается для следующих UTCцелей ToLocalTime: msdn.microsoft.com/en-us/library/…
CJ7
16
@ CJ7: Да, но ясность особенно полезна для других разработчиков, которым, возможно, придется поддерживать код.
Райан
121

Я хотел бы изучить использование класса System.TimeZoneInfo, если вы находитесь в .NET 3.5. См. Http://msdn.microsoft.com/en-us/library/system.timezoneinfo.aspx . Это должно учитывать изменения летнего времени правильно.

// Coordinated Universal Time string from 
// DateTime.Now.ToUniversalTime().ToString("u");
string date = "2009-02-25 16:13:00Z"; 
// Local .NET timeZone.
DateTime localDateTime = DateTime.Parse(date); 
DateTime utcDateTime = localDateTime.ToUniversalTime();

// ID from: 
// "HKEY_LOCAL_MACHINE\Software\Microsoft\Windows NT\CurrentVersion\Time Zone"
// See http://msdn.microsoft.com/en-us/library/system.timezoneinfo.id.aspx
string nzTimeZoneKey = "New Zealand Standard Time";
TimeZoneInfo nzTimeZone = TimeZoneInfo.FindSystemTimeZoneById(nzTimeZoneKey);
DateTime nzDateTime = TimeZoneInfo.ConvertTimeFromUtc(utcDateTime, nzTimeZone);
Дэниел Баллинджер
источник
Если вы работаете в своем собственном часовом поясе (в данном случае en-NZ), вам не нужно иметь дело с TimeZoneInfo. Это просто ненужная сложность. Смотрите мой ответ для более подробной информации.
Дрю Ноакс
11
И если кому-то нужно, вот список часовых
поясов, которые
Brilliant! Спасибо за этот пост Дэн. Я искал это исправление в течение 3 дней.
Кевин Мур
58
TimeZone.CurrentTimeZone.ToLocalTime(date);
coder1
источник
8
Это работает, только если система знает, что конвертируемая дата указана в UTC. Пожалуйста, посмотрите мой ответ.
Дрю Ноакс
1
Но UTC по умолчанию не так ли? Следовательно, это работает для «неопределенного», как в ответе CJ7.
NickG
25

DateTimeобъекты имеют Kindпо Unspecifiedпо умолчанию, которые для целей ToLocalTimeпредполагается UTC.

Чтобы получить местное время Unspecified DateTimeобъекта, вам нужно сделать следующее:

convertedDate.ToLocalTime();

Шаг изменения Kindзначения DateTimeс Unspecifiedна UTCна ненужный. Unspecifiedпредполагается для следующих UTCцелей ToLocalTime: http://msdn.microsoft.com/en-us/library/system.datetime.tolocaltime.aspx

CJ7
источник
6
И наоборот: convertedDate.FromLocalTime();перейдем в UTC.
Р.
16

Я знаю, что это более старый вопрос, но я столкнулся с подобной ситуацией, и я хотел поделиться тем, что я нашел для будущих поисковиков, возможно, включая себя :).

DateTime.Parse()может быть сложно - см. здесь, например.

Если DateTimeисходящий из веб-службы или другого источника с известным форматом, вы можете рассмотреть что-то вроде

DateTime.ParseExact(dateString, 
                   "MM/dd/yyyy HH:mm:ss", 
                   CultureInfo.InvariantCulture, 
                   DateTimeStyles.AssumeUniversal | DateTimeStyles.AdjustToUniversal)

или даже лучше,

DateTime.TryParseExact(...)

AssumeUniversalФлаг указывает анализатору , что дата / время уже UTC; комбинация AssumeUniversalи AdjustToUniversalговорит, что не следует преобразовывать результат в «местное» время, что он попытается сделать по умолчанию. (Я лично в любом случае стараюсь иметь дело исключительно с UTC на бизнес-уровне / уровне приложений / сервисов. Но обход конверсии в местное время также ускоряет процесс - на 50% и более в моих тестах, см. Ниже.)

Вот что мы делали раньше:

DateTime.Parse(dateString, new CultureInfo("en-US"))

Мы профилировали приложение и обнаружили, что DateTime.Parse представляет значительный процент использования процессора. (Между прочим, CultureInfoконструктор не вносил существенный вклад в использование ЦП.)

Поэтому я настроил консольное приложение для разбора строки даты / времени 10000 раз различными способами. Итог:
Parse()10 секунд
ParseExact()(преобразование в локальное) 20-45 мс
ParseExact()(не преобразование в локальное) 10-15 мс
... и да, результаты для Parse()приведены в секундах , в то время как остальные в миллисекундах .

Дэвид
источник
14

Я просто хотел бы добавить общее предупреждение.

Если все, что вы делаете, это получаете текущее время от внутренних часов компьютера, чтобы поместить дату / время на дисплей или отчет, то все в порядке. Но если вы сохраняете информацию о дате / времени для дальнейшего использования или рассчитываете дату / время, будьте осторожны!

Допустим, вы определили, что круизный лайнер прибыл в Гонолулу 20 декабря 2007 года в 15:00 UTC. И вы хотите знать, какое местное время это было.
1. Вероятно, в этом участвуют как минимум три местных жителя. Локальный может означать Гонолулу, или это может означать, где находится ваш компьютер, или это может означать, где находится ваш клиент.
2. Если вы используете встроенные функции для преобразования, это, вероятно, будет неправильно. Это связано с тем, что летнее время (вероятно) в настоящее время действует на вашем компьютере, но НЕ действует в декабре. Но Windows не знает этого ... все, что у нее есть, это один флаг, чтобы определить, действует ли в настоящее время летнее время. И если он в настоящее время действует, то он с радостью добавит час даже к дате в декабре.
3.Переход на летнее время осуществляется по-разному (или не осуществляется вообще) в различных политических подразделениях. Не думайте, что только из-за того, что ваша страна меняется на определенную дату, другие страны тоже.

Марк Т
источник
6
На самом деле, # 2 не совсем правильно. Фактически существуют правила о летнем времени в каждом часовом поясе, которые ваш компьютер будет знать, если информация была установлена ​​(и обновлена). Для многих зон эти правила являются фиксированными. Другие реализуют «динамический DST». Бразилия - моя любимая мозоль для этого. Таким образом, ваш компьютер может работать, если ваше местное время будет летнее время в декабре, при условии, что в настоящее время никакие изменения не переданы в закон.
Роджер Уиллкокс
Даже если вы не живете в Бразилии, DST «динамичен» в том, что политики могут изменить его в любое время (как это было сделано несколько лет назад в США). Поскольку большая часть программного обеспечения написана с учетом будущего использования, важно понимать, что НЕТ практического, предсказуемого или даже теоретического способа узнать, какие правила DST будут действовать. Вы можете приблизиться, но избавьте себя от разочарования, отказавшись от совершенства.
DaveWalley
6
@TimeZoneInfo.ConvertTimeFromUtc(timeUtc, TimeZoneInfo.Local)
Принц прасад
источник
5

Не забудьте, если у вас уже есть объект DateTime и вы не уверены, является ли он UTC или локальным, достаточно просто напрямую использовать методы объекта:

DateTime convertedDate = DateTime.Parse(date);
DateTime localDate = convertedDate.ToLocalTime();

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

Если не указано .net будет использовать локальные настройки компьютера. Я бы прочитал: http://msdn.microsoft.com/en-us/library/system.globalization.daylighttime.aspx

По внешнему виду код может выглядеть примерно так:

DaylightTime daylight = TimeZone.CurrentTimeZone.GetDaylightChanges( year );

И, как уже упоминалось выше, дважды проверьте настройку часового пояса на вашем сервере. В сети есть статьи о том, как безопасно повлиять на изменения в IIS.

Брендан Ковиц
источник
Система справится с этой сложностью для вас, при условии, что вы сообщите системе, какой «тип» вашей даты (местный / utc / не указан).
Дрю Ноакс
2

В ответ на предложение Даны:

Пример кода теперь выглядит так:

string date = "Web service date"..ToString("R", ci);
DateTime convertedDate = DateTime.Parse(date);            
DateTime dt = TimeZone.CurrentTimeZone.ToLocalTime(convertedDate);

Первоначальная дата была 20/08/08; вид был UTC.

Оба «convertDate» и «dt» одинаковы:

21/08/08 10:00:26; вид был местный

rbrayb
источник
Пожалуйста, смотрите мой ответ для объяснения этого.
Дрю Ноакс
1

У меня была проблема с тем, что он находился в наборе данных, передаваемом по проводам (от веб-службы к клиенту), и он автоматически менялся, потому что поле DateType в DataColumn было установлено как локальное. Убедитесь, что вы проверили, что такое DateType, если вы продвигаете DataSets.

Если вы не хотите, чтобы это изменилось, установите значение Unspecified

миль
источник
1

Я столкнулся с этим вопросом, так как у меня была проблема с датами UTC, которые вы возвращаете через твиттер API (поле made_at в статусе); Мне нужно конвертировать их в DateTime. Ни один из ответов / примеров кода в ответах на этой странице не был достаточным для того, чтобы я не получил ошибку «Строка не была распознана как действительный DateTime» (но это самый близкий способ найти правильный ответ на SO)

Размещение этой ссылки здесь на случай, если это поможет кому-то еще - нужный мне ответ был найден в этом блоге: http://www.wduffy.co.uk/blog/parsing-dates-when-aspnets-datetimeparse-doesnt-work/ - в основном используйте DateTime.ParseExact со строкой формата вместо DateTime.Parse

DannykPowell
источник