Конвертировать время UTC/GMT по местному времени
Мы разрабатываем приложение на 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 часов
Как нам отрегулировать дополнительный час? Это можно сделать программно или это какая-то настройка на ПК?
14 ответов
Для строк, таких как 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();
Это даст вам требуемый результат.
Я хотел бы изучить использование класса 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);
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
Я знаю, что это более старый вопрос, но я столкнулся с подобной ситуацией, и я хотел поделиться тем, что я нашел для будущих поисковиков, возможно, включая меня самого:).
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()
в секундах, в то время как остальные в миллисекундах.
Я просто хотел бы добавить общее предупреждение.
Если все, что вы делаете, это получаете текущее время от внутренних часов компьютера, чтобы поместить дату / время на дисплей или отчет, то все в порядке. Но если вы сохраняете информацию о дате / времени для дальнейшего использования или рассчитываете дату / время, будьте осторожны!
Допустим, вы определили, что круизный лайнер прибыл в Гонолулу 20 декабря 2007 года в 15:00 UTC. И вы хотите знать, какое это было местное время.
1. Вероятно, в этом участвуют как минимум три местных жителя. Локальный может означать Гонолулу, или это может означать, где находится ваш компьютер, или это может означать, где находится ваш клиент.
2. Если вы используете встроенные функции для преобразования, это, вероятно, будет неправильно. Это связано с тем, что летнее время (вероятно) в настоящее время действует на вашем компьютере, но НЕ действует в декабре. Но Windows не знает этого... все, что у нее есть, - это один флаг, чтобы определить, действует ли в настоящее время летнее время. И если он в настоящее время действует, то он с радостью добавит час даже к дате в декабре.
3. Переход на летнее время осуществляется по-разному (или не осуществляется вообще) в различных политических подразделениях. Не думайте, что из-за того, что ваша страна изменится в конкретную дату, другие страны тоже.
@TimeZoneInfo.ConvertTimeFromUtc(timeUtc, TimeZoneInfo.Local)
Не забудьте, если у вас уже есть объект 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.
В ответ на предложение Даны:
Пример кода теперь выглядит так:
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; вид был местный
У меня была проблема с тем, что он находился в наборе данных, передаваемом по проводам (от веб-сервиса к клиенту), и он автоматически менялся, потому что поле DateType в DataColumn было установлено как локальное. Убедитесь, что вы проверили, что такое DateType, если вы продвигаете DataSets.
Если вы не хотите, чтобы это изменилось, установите значение Unspecified
Я столкнулся с этим вопросом, так как у меня была проблема с датами UTC, которые вы возвращаете через Twitter API (поле made_at в статусе); Мне нужно конвертировать их в DateTime. Ни один из ответов / примеров кода в ответах на этой странице не был достаточным, чтобы я не получил ошибку "Строка не была распознана как действительный DateTime" (но я ближе всего нашел правильный ответ на SO)
Размещение этой ссылки здесь на случай, если это поможет кому-то еще - нужный мне ответ был найден в этом блоге: http://www.wduffy.co.uk/blog/parsing-dates-when-aspnets-datetimeparse-doesnt-work/ - в основном используйте DateTime.ParseExact со строкой формата вместо DateTime.Parse
у меня была похожая ситуация вASP.NET Core 7.0 Web API
и я решил это следующим образом:
- Установить
NodaTime
Нугет. - Получите код страны из запроса пользователя, используя
CultureInfo.CurrentCulture.Name[^2..]
. В моем случае этоIN
и это вернетсяAsia/Kolkatta
. - Если это
not null
тогда возьмиTimeZoneInfo
с использованиемZoneId
else будет экспортировано как . - Теперь мы можем использовать
TimeZoneInfo.ConvertTimeFromUtc(UTCDateTime, TimeZoneInfo)
для преобразованияUTC
пользователюLocal
DateTime
.
var timeZone = TzdbDateTimeZoneSource.Default.ZoneLocations!.FirstOrDefault(x => x.CountryCode == CultureInfo.CurrentCulture.Name[^2..]);
if (timeZone is not null)
{
var timeZoneInfo = TimeZoneInfo.FindSystemTimeZoneById(timeZone.ZoneId);
foreach (var order in orders)
{
order.OrderDate = TimeZoneInfo.ConvertTimeFromUtc(order.OrderDate.DateTime, timeZoneInfo);
}
}
Обратите внимание, что я беруFirstOrDefault()
а это значит, что для стран, имеющих единый, этого будет достаточно. Но для стран с несколькими часовыми поясами нам нужно иметьtimezone
данные пользователя хранятся в базе данных для преобразования.
Этот блок кода использует универсальное время для преобразования текущего объекта DateTime, а затем преобразует его обратно в локальное DateTime. Работает идеально для меня, я надеюсь, что это поможет!
CreatedDate.ToUniversalTime().ToLocalTime();
Если у вас есть строка dateTime, которая считается не в часовом поясе UTC, вы можете использовать DateTimeOffset , чтобы просто установить информацию о часовом поясе, не меняя время из строки, следующим образом:
// Input datetime string
string datetimeString = "2023-07-11 12:42:56";
// Specify the timezone as "Europe/Prague"
TimeZoneInfo pragueTimeZone = TimeZoneInfo.FindSystemTimeZoneById("Europe/Prague");
// Parse the datetime string with the specified format
DateTime datetimeObj = DateTime.ParseExact(datetimeString, "yyyy-MM-dd HH:mm:ss", CultureInfo.InvariantCulture);
// Create a DateTimeOffset with the parsed datetime and the "Europe/Prague" timezone
DateTimeOffset pragueDateTimeOffset = new DateTimeOffset(datetimeObj, pragueTimeZone.GetUtcOffset(datetimeObj));
Вы также можете отформатировать строку DateTimeOffset, как обычно это делаете с экземпляром DateTime.
// Print the resulting DateTimeOffset object
Console.WriteLine(pragueDateTimeOffset.ToString("O"));