DateTime.Now против DateTime.UtcNow

225

Мне было интересно, каковы принципы работы этих двух свойств. Я знаю, что второй универсальный и в основном не имеет дело с часовыми поясами, но может ли кто-нибудь подробно объяснить, как они работают и какой из них следует использовать в каком сценарии?

Славо
источник
1
может быть уже слишком поздно, но я хочу указать на этот блог: blog.angeloflogic.com/2013/10/…
Кай Ван
небольшой стендовый тест rextester.com/QRDR82396
Даниэль Б

Ответы:

346

DateTime.UtcNow сообщает вам дату и время так, как это было бы в всемирном координированном времени, которое также называется часовым поясом среднего времени по Гринвичу - в основном, как если бы вы были в Лондоне, Англия, но не летом. DateTime.Now дает дату и время, которые будут отображаться для кого-либо в вашей текущей локали.

Я бы порекомендовал использовать DateTime.Nowкаждый раз, когда вы отображаете дату человеку - так, чтобы он чувствовал себя комфортно с ценностью, которую он видит, - это то, что они могут легко сравнить с тем, что они видят на своих часах или часах. Используйте, DateTime.UtcNowкогда вы хотите сохранить даты или использовать их для более поздних вычислений таким образом (в модели клиент-сервер) ваши вычисления не будут смущены клиентами в разных часовых поясах от вашего сервера или друг от друга.

Блэр Конрад
источник
84
отличный момент - при хранении дат в базе данных или файле обязательно храните их в формате UTC!
Джефф Этвуд
15
Следует отметить, что если вы хотите сохранить даты в формате UTC в базе данных, вы должны убедиться, что база данных не добавляет свой собственный часовой пояс к датам, которые не дают явных часовых поясов. Обратите внимание, что DateTime всегда будет использовать текущий часовой пояс, когда его попросят.
Омер ван Клотен
@OmervanKloeten дает очень хорошее замечание. Мне интересно, существует ли элегантное универсальное решение для правильного хранения и получения дат каждый раз, даже если ваш IIS и SQL-сервер находятся в разных часовых поясах.
TheGeekZn
1
@ JoshYates1980 Да, вы просто делаете DateTime.UtcNow.AddYears (1)
CathalMF
3
Используйте NodaTime - это заставит вас думать о времени более полезным способом и избежать таких проблем
aateeque
86

Это действительно довольно просто, так что я думаю, что это зависит от вашей аудитории и места, где они живут.

Если вы не используете Utc, вы должны знать часовой пояс человека, которому вы показываете даты и время - в противном случае вы скажете им, что что-то произошло в 15:00 по системному или серверному времени, когда это действительно произошло в 17:00, где они случаются, чтобы жить.

Мы используем это, DateTime.UtcNowпотому что у нас есть глобальная веб-аудитория, и потому что я предпочел бы не раздражать каждого пользователя, чтобы заполнить форму, указывающую, в каком часовом поясе они живут.

Мы также отображаем относительное время (2 часа назад, 1 день назад и т. Д.) До тех пор, пока сообщение не станет достаточно старым, чтобы время было «одинаковым» независимо от того, где на Земле вы живете.

Джефф Этвуд
источник
Во-вторых, я хочу, чтобы сохранение DateTime.UtcNow также было необходимо только тогда, когда для вычисления правильных часов были выполнены вычисления с двумя датами. Когда мне просто нужно отобразить RegisterAt Date, тогда Datetime.Now достаточно.
Элизабет
36

Также обратите внимание на разницу в производительности; DateTime.UtcNowэто где-то примерно в 30 раз быстрее DateTime.Now, потому что внутренне DateTime.Nowвыполняется много настроек часового пояса (вы можете легко проверить это с помощью Reflector).

Поэтому НЕ используйте DateTime.Nowдля измерения относительного времени.

Магнус Криселл
источник
Мне потребовалось трудное путешествие, просто я знаю, что UtcNow имеет лучшую производительность, и что простое сохранение дат в mysql и допущение, что это utc, и сравнение отображений, зависящих от даты, с UtcNow упрощает эти глобальные проблемы с
часовыми поясами
29

Одна из основных для понимания концепция в .NET является то , что в настоящее время не является в настоящее время по всей земле независимо от того , в каком часовом поясе вы находитесь в так что если вы загрузите переменную с. DateTime.NowИли DateTime.UtcNow-. Назначение идентично * Ваш DateTimeобъект знает , что часовой пояс , вы находитесь в и принимает это во внимание независимо от назначения.

Полезность DateTime.UtcNowполезна при расчете дат за пределами летнего времени. То есть в местах, которые участвуют в переходе на летнее время, иногда с полудня до полудня следующего дня бывает 25 часов, а иногда между полуднем и полднем следующего дня 23 часа. Если вы хотите правильно определить количество часов от времени A и времени B, вам необходимо сначала перевести каждый из них в их UTC-эквиваленты, прежде чем вычислять TimeSpan.

Это покрыто постом в блоге, который я написал, который объясняет далее TimeSpan, и включает ссылку на еще более обширную статью MS на эту тему.

* Уточнение: любое назначение будет хранить текущее время. Если вы должны были загрузить две переменных один через , DateTime.Now()а другие с помощью DateTime.UtcNow()к TimeSpanразнице между ними будут миллисекунды, а не часы , предполагающие вы находитесь в часовом поясе часов езды от GMT. Как отмечено ниже, распечатка их Stringзначений будет отображать разные строки.

Карл Камер
источник
1
Относительно «загрузить переменную с DateTime.Now или DateTime.UtcNow - назначение идентично»: это может потребоваться уточнить? Сидя здесь, в часовом поясе EDT (UTC-4), я назначил две переменные DateTime.UtcNow и DateTime.Now соответственно, а затем напечатал их значения с помощью ToString (). Отображаемые значения были с интервалом 4 часа - не «идентичны».
Джон Шнайдер
2
@JonSchneider, я верю, что ты прав. Утверждение: «присвоение идентично» не соответствует действительности. ToString (), вероятно, не лучший способ проверить это, потому что он может отображать одинаковые даты по-разному (как это делает Java). Функции сравнения - лучший тест, и показывают, что они действительно не равны.
Тед Бигхам
Разъяснение моего «идентичного» утверждения: загрузите одну переменную через DateTime.Now, а другую - с DateTime.UtcNow, а затем напечатайте разницу в TimeSpan. Разница будет в миллисекундах, а не в часах, если вы находитесь в часах от GMT.
Карл Камер
18

Это хороший вопрос. Я возрождаю его, чтобы немного подробнее рассказать о том, как .Net ведет себя с разными Kindзначениями. Как указывает @Jan Zich, это на самом деле критически важное свойство, и оно устанавливается по-разному в зависимости от того, используете ли вы Nowили UtcNow.

Внутри сохраняется дата, Ticksкоторая (в отличие от ответа @Carl Camera) отличается в зависимости от того, используете ли вы Nowили UtcNow.

DateTime.UtcNowведет себя как другие языки. Он устанавливает Ticksзначение на основе GMT. Это также устанавливает Kindк Utc.

DateTime.Nowизменяет Ticksзначение на то, что было бы, если бы это было ваше время суток в часовом поясе GMT . Это также устанавливает Kindк Local.

Если вы отстаете на 6 часов (GMT-6), вы получите время по Гринвичу 6 часов назад. .Net фактически игнорирует Kindи обрабатывает это время так, как будто это было 6 часов назад, хотя это должно быть "сейчас". Это ломает еще больше, если вы создаете DateTimeэкземпляр, затем меняете часовой пояс и пытаетесь его использовать.

Экземпляры DateTime с разными значениями «Kind» НЕ совместимы.

Давайте посмотрим на некоторый код ...

    DateTime utc = DateTime.UtcNow;
    DateTime now = DateTime.Now;
    Debug.Log (utc + " " + utc.Kind);  // 05/20/2015 17:19:27 Utc
    Debug.Log (now + " " + now.Kind);  // 05/20/2015 10:19:27 Local

    Debug.Log (utc.Ticks);  // 635677391678617830
    Debug.Log (now.Ticks);  // 635677139678617840

    now = now.AddHours(1);
    TimeSpan diff = utc - now;
    Debug.Log (diff);  // 05:59:59.9999990

    Debug.Log (utc <  now);  // false
    Debug.Log (utc == now);  // false
    Debug.Log (utc >  now);  // true

    Debug.Log (utc.ToUniversalTime() <  now.ToUniversalTime());  // true
    Debug.Log (utc.ToUniversalTime() == now.ToUniversalTime());  // false
    Debug.Log (utc.ToUniversalTime() >  now.ToUniversalTime());  // false
    Debug.Log (utc.ToUniversalTime() -  now.ToUniversalTime());  // -01:00:00.0000010

Как вы можете видеть здесь, сравнения и математические функции не конвертируются автоматически в совместимое время. Значение Timespanдолжно было составлять почти один час, но вместо этого было почти 6. «utc <now» должно было быть верным (я даже добавил час, чтобы быть уверенным), но все равно было ложным.

Вы также можете увидеть «обходной путь», который заключается в простом преобразовании в универсальное время в любом месте, Kindкоторое не совпадает.

Мой прямой ответ на вопрос согласуется с рекомендацией принятого ответа о том, когда использовать каждый из них. Вы должны всегда пытаться работать с DateTimeобъектами, которые имеют Kind=Utc, кроме как во время ввода-вывода (отображение и анализ). Это означает, что вы должны почти всегда использовать DateTime.UtcNow, за исключением случаев, когда вы создаете объект, просто чтобы отобразить его и сразу же отказаться от него.

Тед Бигхэм
источник
7

DateTime не знает, что такое часовые пояса. Это всегда предполагает, что вы в вашем местном времени. UtcNow означает только «Вычесть мой часовой пояс из времени».

Если вы хотите использовать даты с учетом часового пояса, используйте DateTimeOffset , который представляет дату / время с часовым поясом. Мне пришлось научиться этому нелегкому пути.

Омер ван Клоэтен
источник
9
Чтобы быть совершенно точным (и избегать использования Now по UtcNow по соображениям производительности), все наоборот: теперь часовой пояс добавляется к UtcNow и на самом деле на несколько медленнее.
Мафу
5

«Простой» ответ на вопрос:

DateTime.Now возвращает значение DateTime, представляющее текущее системное время (в любом часовом поясе, в котором работает система). Свойство DateTime.Kind будет DateTimeKind.Local

DateTime.UtcNow возвращает значение DateTime, представляющее текущее универсальное координированное время (или UTC), которое будет одинаковым независимо от часового пояса системы. Свойство DateTime.Kind будет DateTimeKind.Utc

PapillonUK
источник
4

Просто небольшое дополнение к пунктам, сделанным выше: структура DateTime также содержит малоизвестное поле, называемое Kind (по крайней мере, я давно об этом не знал). Это в основном просто флаг, указывающий, является ли время местным или UTC; он не указывает реальное смещение от UTC для местного времени. Помимо того факта, что он указывает, с какими намерениями была создана структура, он также влияет на то, как работают методы ToUniversalTime () и ToLocalTime () .

Ян Зич
источник
3

Немного опоздал на вечеринку, но я нашел эти две ссылки (4guysfromrolla) очень полезными:

Использование всемирного координированного времени (UTC) для хранения значений даты и времени

Рекомендации по хранению и отображению дат и времени в разных часовых поясах

Сорин Команеску
источник
1

DateTime.UtcNow - это непрерывный однозначный временной масштаб, тогда как DateTime.Now не является непрерывным или однозначным. Основной причиной является летнее время, которое не относится к UTC. Таким образом, UTC никогда не переходит вперед или назад на час, тогда как местное время (DateTime.Now) делает. И когда он прыгает назад, то же самое время встречается дважды.

user1315023
источник
1

DateTime.UtcNow - это универсальная шкала времени, в которой отсутствует летнее время. Таким образом, UTC никогда не меняется из-за летнего времени.

Но DateTime.Now не является непрерывным или однозначным, поскольку изменяется в соответствии с DST. Что означает DateTime.Now, одно и то же время может происходить дважды, оставляя клиентов в замешательстве.

ChaiVan
источник
0

Когда вам нужно местное время для компьютера, на котором работает ваше приложение (например, CEST для Европы), используйте Сейчас. Если вы хотите универсальное время - UtcNow. Это просто вопрос ваших предпочтений - возможно, создание локального веб-сайта / отдельного приложения, которое вы хотели бы использовать в то время, которое пользователь выбрал - так зависит от его / ее настройки часового пояса - DateTime.Now.

Помните, для веб-сайта это настройка часового пояса сервера. Поэтому, если вы отображаете время для пользователя, либо получите его предпочтительный часовой пояс и сместите время (просто сохраните время Utc в базе данных и измените его), либо укажите его UTC. Если вы забудете это сделать, пользователь увидит что-то вроде: опубликовал 3 минуса назад, а затем время в будущем рядом с ним :)

кендер
источник
0

Большая разница :) в том, что DateTime.Now не поддерживается в рабочем процессе SharePoint, вы должны использовать DateTime.UtcNow

Мишель Лаплан
источник