Перетворити UTC / GMT на місцевий час


301

Ми розробляємо додаток C # для клієнта веб-сервісу. Це працюватиме на комп'ютерах Windows XP.

Одним із полів, повернутих веб-службою, є поле DateTime. Сервер повертає поле у ​​форматі GMT, тобто з "Z" в кінці.

Однак ми виявили, що .NET, здається, робить якусь неявну конверсію, і час завжди було 12 годин.

Наступний зразок коду певною мірою вирішує це питання тим, що 12 годинна різниця минула, але це не враховує економію літнього часу в NZ.

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 годин

Як ми регулюємо додаткову годину? Це можна зробити програмно чи це якась установка на ПК?


2
Zчас відноситься до UTC, а НЕ по Грінвічу. Вони можуть відрізнятися до 0,9 секунди.
mc0e

Відповіді:


374

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

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

  • Не визначено
  • Місцеві
  • Utc

ПРИМІТКА Якщо ви хочете вказати дату / час, відмінний від 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
А якщо комусь потрібно, ось список часових поясів, які я знайшов для TimeZoneInfo.FindSystemTimeZoneById - codeproject.com/Messages/3867850/…
nikib3ro

Блискуче! Дякую за цю посаду Ден. Я шукав це виправлення 3 дні.
Кевін Мур

58
TimeZone.CurrentTimeZone.ToLocalTime(date);

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


6
І навпаки: convertedDate.FromLocalTime();перейде в UTC.
R. Schreurs

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, якщо припустити, що зміни до закону не приймаються між часом.
Роджер Віллкокс

Навіть якщо ви не живете в Бразилії, DST є "динамічним" тим, що політики можуть його змінити в будь-який час (як це було зроблено кілька років тому в США). Оскільки більшість програмного забезпечення написано з урахуванням подальшого використання, важливо усвідомити, що НІМАЄ практичного, передбачуваного або навіть теоретичного способу знати, які правила DST будуть діяти. Можна наблизитися, але врятувати себе від розчарування, відмовившись від досконалості.
DaveWalley


5

Не забувайте, якщо у вас вже є об'єкт DateTime і ви не впевнені, чи це UTC або Local, досить просто використовувати методи на об'єкті безпосередньо:

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; вид був місцевим


Будь ласка, дивіться моєї відповіді для пояснення цього.
Дрю Ноакс

1

У мене виникла проблема з тим, що в наборі даних, що пересувається через провід (веб-сервіс до клієнта), він автоматично змінюватиметься, оскільки для поля DataType колонки DataCype було встановлено місце. Переконайтеся, що ви перевіряєте, що таке тип типу DateType, якщо ваш штовхач наборів даних поперек.

Якщо ви не хочете, щоб він змінився, встановіть його на Невизначений


1

Я зіткнувся з цим питанням, оскільки у мене виникли проблеми з датами UTC, які ви повертаєтесь через API twitter (поле create_at у статусі); Мені потрібно конвертувати їх у DateTime. Жоден із зразків відповідей / кодів у відповідях на цій сторінці не був достатнім, щоб зупинити мені отримання помилки "Рядок не був визнаний дійсним DateTime" (але це найближче значення для пошуку правильної відповіді на SO)

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

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.