Как мне получить значение datetime.today () в Python, которое «учитывает часовой пояс»?


310

Я пытаюсь вычесть одно значение даты из значения, datetime.today()чтобы вычислить, как давно что-то было. Но это жалуется

TypeError: can't subtract offset-naive and offset-aware datetimes

Значение datetime.today()не похоже на «часовой пояс», в то время как мое другое значение даты. Как я могу получить значение datetime.today()этого с учетом часового пояса?

Прямо сейчас, это дает мне время по местному времени, которое бывает PST, то есть UTC - 8 часов. В худшем случае, есть ли способ, которым я могу вручную ввести значение часового пояса в datetimeобъект, возвращаемый datetime.today()и установить его на UTC-8?

Конечно, идеальным решением было бы автоматическое определение часового пояса.



10
Похоже, мы можем использовать datetime.now().astimezone()начиная с Python 3.6
johnchen902

Ответы:


362

В стандартной библиотеке нет кроссплатформенного способа создания осведомленных часовых поясов без создания собственного класса часовых поясов.

В Windows есть win32timezone.utcnow(), но это часть pywin32. Я бы предпочел использовать библиотеку pytz , которая имеет постоянно обновляемую базу данных большинства часовых поясов.

Работа с местными часовыми поясами может быть очень сложной (см. Ссылки «Дальнейшее чтение» ниже), поэтому вы, скорее всего, захотите использовать UTC во всем приложении, особенно для арифметических операций, таких как вычисление разницы между двумя временными точками.

Вы можете получить текущую дату / время следующим образом:

import pytz
from datetime import datetime
datetime.utcnow().replace(tzinfo=pytz.utc)

Помните об этом datetime.today()и datetime.now()верните местное время, а не время UTC, поэтому обращение .replace(tzinfo=pytz.utc)к ним будет неправильным.

Еще один хороший способ сделать это:

datetime.now(pytz.utc)

который немного короче и делает то же самое.


Дальнейшее чтение / просмотр, почему во многих случаях предпочитают UTC:


75
Как насчет datetime.now(pytz.utc)вместо datetime.utcnow().replace(tzinfo = pytz.utc)?
eumiro

5
now(utc)не возвращается сегодня (если в UTC нет полуночи), возвращает текущее время в UTC. Вы также .replace(hour=0, minute=0, ...)должны получить начало дня (как datetime.today())
JFS

1
В документы говорят , что today()возвращает текущее время, а не в полночь. Если есть случай использования, где требуется полночь, да, замена должна быть сделана соответственно. Поскольку первоначальный вопрос касался разницы между датой и временем, я не думаю, что требуется полночь.
AndiDog

1
@AndiDog: Мой комментарий подразумевает, что я думал (неправильно), что datetime.today()это так combine(date.today(), time()). datetimeимеет оба .now()и .today()методы, которые (как вы правильно указали) возвращают (почти) одно и то же. Там нет date.now()метода. dateи datetimeобъекты не являются взаимозаменяемыми. Использование объекта datetime вместо dateобъекта может привести к незначительным ошибкам; Я не вижу причин для datetime.today()существования, если это почти копия datetime.now().
Jfs

6
В добавление к этому ответу, если вы используете django, всегда используйте timezone.now()вместо, datetime.now()так как он будет использовать UTC автоматически, если USE_TZ = True. timezoneнаходится по адресу django.utils.timezone: документация: docs.djangoproject.com/en/1.11/topics/i18n/timezones
Райан,

107

Получить текущее время в определенном часовом поясе:

import datetime
import pytz
my_date = datetime.datetime.now(pytz.timezone('US/Pacific'))

2
Смотрите это .
Wim

1
Вы не должны использовать локализованное время, кроме как для вывода. При использовании даты и времени на основе часового пояса многие вещи идут не так, как надо: простая временная дельта не учитывает переход на летнее время, если только вы не в UTC. Всегда используйте информацию о часовом поясе на основе UTC. преобразовать в местный часовой пояс на выходе при необходимости.
MrE

4
Чтобы подтвердить несогласие с @MrE, которое я ранее озвучивал в комментариях к принятому ответу: есть совершенно веские причины для работы с локализованными датами и «вам НЕ следует использовать локализованное время, кроме выходных», это слишком широкий совет. Предположим, вы добавляете 1 день к дате за несколько часов до границы перехода на летнее время, когда часы возвращаются на один час назад. Какой результат вы хотите? Если вы считаете, что время должно быть одинаковым, используйте локализованные даты и время. Если вы думаете, что это должно быть на час раньше, используйте время по Гринвичу или наивные часы. Что имеет смысл, зависит от домена.
Марк Амери

@MarkAmery, насколько я могу согласиться с тем, что вы можете добавлять или вычитать количество дней или часов и не заботиться о проблемах с часовым поясом (например, ваш пример), этот комментарий относится к передаче клиенту исправленного часового пояса времени. Поскольку Python в основном используется для внутренних процессов, он передает время клиенту. Сервер должен всегда передавать дату / время в UTC, а клиент должен преобразовать его в свою собственную локальную дату / время / часовой пояс, в противном случае могут произойти плохие вещи: просто проверьте вывод datetime.datetime(2016, 11, 5, 9, 43, 45, tzinfo=pytz.timezone('US/Pacific'))и посмотрите, ожидали ли вы этого
MrE

«Сервер должен всегда передавать дату / время в UTC, а клиент должен преобразовывать его в свою собственную локальную дату / время / часовой пояс» - нет, это не всегда так. Иногда использование часового пояса клиента неуместно, и соответствующий часовой пояс необходимо передавать как часть данных. Если я, как лондонец, просматриваю время встреч в шахматном клубе Сан-Франциско на их веб-сайте, я должен видеть их в Сан-Франциско, а не в Лондоне.
Марк Амери

69

В Python 3 стандартная библиотека значительно упрощает определение UTC в качестве часового пояса:

>>> import datetime
>>> datetime.datetime.now(datetime.timezone.utc)
datetime.datetime(2016, 8, 26, 14, 34, 34, 74823, tzinfo=datetime.timezone.utc)

Если вам нужно решение, которое использует только стандартную библиотеку и работает как в Python 2, так и в Python 3, см. Ответ jfs .

Если вам нужен местный часовой пояс, а не UTC, см . Ответ Михая Капотэ


19

Вот решение stdlib, которое работает на Python 2 и 3:

from datetime import datetime

now = datetime.now(utc) # Timezone-aware datetime.utcnow()
today = datetime(now.year, now.month, now.day, tzinfo=utc) # Midnight

где today- осведомленный экземпляр datetime, представляющий начало дня (полночь) в UTC и utcпредставляющий собой объект tzinfo ( пример из документации ):

from datetime import tzinfo, timedelta

ZERO = timedelta(0)

class UTC(tzinfo):
    def utcoffset(self, dt):
        return ZERO

    def tzname(self, dt):
        return "UTC"

    def dst(self, dt):
        return ZERO

utc = UTC()

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


16

Другой метод для создания объекта datetime с информацией о часовом поясе, представляющего текущее время:

import datetime
import pytz

pytz.utc.localize( datetime.datetime.utcnow() )  

обратите внимание, что pytz.utcи pytz.UTCоба определены (и являются одинаковыми)
drevicko

2
Этот ответ лучше принятого, поскольку он более универсален: replace()часовой пояс обычно склонен к ошибкам в большинстве других применений, в то время localize()как предпочтительный способ присвоения часового пояса наивным временным меткам является предпочтительным.
Энтони Хэтчкинс

@AntonyHatchkins: .localize() метод терпит неудачу в течение неоднозначного локального времени (не входящий в UTC) Ответ @ philfreo, который использует,.now(pytz_timezone) продолжает работать в таких случаях.
JFS

Как указано в документации по Python, он .now(pytz_timezone)делает то же самое, что и localize(utcnow)- сначала он генерирует текущее время в UTC, затем назначает ему часовой пояс: "<...> В этом случае результат эквивалентен tz.fromutc(datetime.utcnow().replace(tzinfo=tz))". Оба ответа верны и работают всегда.
Энтони Хаткинс

1
Единственное наивное (не utc) время, которое можно безопасно сделать осведомленным о часовом поясе, теперь : базовая система должна знать значение UTC, а pytzчерез OLSON db должна знать, как преобразовать его в любой часовой пояс в мире. Осведомленность о любом другом наивном (не являющемся) часовом поясе времени затруднена из-за неоднозначности во время смены летнего времени. Это не проблема .localize(кормить егоis_dst значение, оно будет работать на любую дату). Это неотъемлемая проблема практики летнего времени.
Энтони Хэтчкинс

16

Однострочник, использующий только стандартную библиотеку, работает, начиная с Python 3.3. Вы можете получить datetimeобъект, осведомленный о местном часовом поясе, используя astimezone(как предложено johnchen902 )

from datetime import datetime, timezone

aware_local_now = datetime.now(timezone.utc).astimezone()

print(aware_local_now)
# 2020-03-03 09:51:38.570162+01:00

print(repr(aware_local_now))
# datetime.datetime(2020, 3, 3, 9, 51, 38, 570162, tzinfo=datetime.timezone(datetime.timedelta(0, 3600), 'CET'))

2
Документация - большая помощь, найденная здесь: docs.python.org/3.8/library/… . Эта невероятно базовая функциональность скрыта глубоко в неясном абзаце в документах, так что этот ответ на стекопоток - фактически единственное место во всем Интернете с этой информацией. Из документации также видно, что, начиная с Python 3.6, datetime.now()можно вызывать без каких-либо аргументов и возвращать правильный локальный результат ( datetimeпредполагается, что наивные s находятся в местном часовом поясе).
Атимхолт

8

Если вы используете Django , вы можете установить даты, не поддерживающие tz (только UTC ).

Прокомментируйте следующую строку в settings.py:

USE_TZ = True

8
Где вы видели Джанго, упомянутый в этом вопросе?
фон Петрушев

1
Какая-то добрая душа удалила мой предыдущий комментарий-извинение здесь, так что еще раз: позор мне, неправильный ответ, поскольку вопрос не является специфическим для Джанго. Я оставил его, потому что в любом случае это может помочь некоторым пользователям, но я удалю его, когда счет приблизится к 0. Если этот ответ неуместен, не стесняйтесь понижать голос.
Лаффуст

6

pytz - это библиотека Python, которая позволяет выполнять точные и кроссплатформенные вычисления часовых поясов с использованием Python 2.3 или выше.

С помощью stdlib это невозможно.

Смотрите аналогичный вопрос на SO .


6

Вот один из способов его создания с помощью stdlib:

import time
from datetime import datetime

FORMAT='%Y-%m-%dT%H:%M:%S%z'
date=datetime.strptime(time.strftime(FORMAT, time.localtime()),FORMAT)

date будет хранить локальную дату и смещение от UTC , а не дату в часовом поясе UTC, поэтому вы можете использовать это решение, если вам необходимо определить, в каком часовом поясе сгенерирована дата . В этом примере и в моем местном часовом поясе:

date
datetime.datetime(2017, 8, 1, 12, 15, 44, tzinfo=datetime.timezone(datetime.timedelta(0, 7200)))

date.tzname()
'UTC+02:00'

Ключ добавляет %zдирективу в представление FORMAT, чтобы указать смещение UTC сгенерированной временной структуры. С другими форматами представления можно ознакомиться в документации модуля datetime.

Если вам нужна дата в часовом поясе UTC, вы можете заменить time.localtime () на time.gmtime ()

date=datetime.strptime(time.strftime(FORMAT, time.gmtime()),FORMAT)

date    
datetime.datetime(2017, 8, 1, 10, 23, 51, tzinfo=datetime.timezone.utc)

date.tzname()
'UTC'

редактировать

Это работает только на python3 . Директива z недоступна в коде Python 2 _strptime.py


ValueError: 'z' - неправильная директива в формате '% Y-% m-% dT% H:% M:% S% z'
jno

Вы на Python 2, верно? К сожалению, похоже, что директива z недоступна на python 2. _strptime.py code
jcazor

6

Используйте dateutil, как описано в Python datetime.datetime.now (), который учитывает часовой пояс :

from dateutil.tz import tzlocal
# Get the current date/time with the timezone.
now = datetime.datetime.now(tzlocal())

1
Смотрите этот ответ Дж. Ф. Себастьяна для ситуации, когда это дает неверный результат.
Энтони Хэтчкинс

2
Я думаю, что ошибка в другом посте имеет отношение только к конкретным случаям использования. Эта tzlocal()функция по-прежнему является одним из самых простых решений, и ее обязательно стоит упомянуть здесь.
user8162

2

Получение даты с utcучетом часового пояса в часовом поясе достаточно для вычитания даты для работы.

Но если вам нужна дата с tzlocalучетом часового пояса в вашем текущем часовом поясе, можно пойти по следующему пути:

from tzlocal import get_localzone  # pip install tzlocal
from datetime import datetime
datetime.now(get_localzone())

PS dateutilимеет аналогичную функцию ( dateutil.tz.tzlocal). Но, несмотря на то, что он разделяет название, у него совершенно другая кодовая база, которая, как отметил Дж.Ф. Себастьян, может давать неверные результаты.


Python обычно используется на сервере. Местный часовой пояс на сервере обычно не имеет смысла и всегда должен быть установлен в UTC. Установка даты и времени tzinfo таким способом не работает в некоторых случаях. Лучше использовать UTC, а затем локализовать его в нужный часовой пояс только на выходе. любое вычисление временной дельты, например, не учитывает переход на летнее время, поэтому их следует выполнять в UTC, а затем локализовать.
MrE

@ Мистер Неправильно, оффтоп, примеры?
Энтони Хэтчкинс

попробуйте использовать объект datetime, локализованный в часовом поясе, который наблюдает за переходом на летнее время, добавьте количество дней для изменения состояния перехода на летнее время, и вы увидите, что работа с объектами datetime в локализованном часовом поясе дает сбой и не учитывает переход на летнее время. Отсюда мой комментарий о том, что вы ВСЕГДА должны выполнять любые операции с датой и временем в UTC.
MrE

Дело в том, что не делайте этого, делайте ваши операции в UTC, а затем используйте datetime.astimezone (часовой пояс) для преобразования в местный часовой пояс на выходе.
MrE

2

Вот решение с использованием читаемого часового пояса, которое работает с today ():

from pytz import timezone

datetime.now(timezone('Europe/Berlin'))
datetime.now(timezone('Europe/Berlin')).today()

Вы можете перечислить все часовые пояса следующим образом:

import pytz

pytz.all_timezones
pytz.common_timezones # or

1

Специально для часовых поясов без UTC:

Единственный часовой пояс, который имеет свой собственный метод, это timezone.utc, но вы можете пометить часовой пояс с любым смещением UTC, если вам нужно, используя timedelta& timezone, и принудительно используя его .replace.

In [1]: from datetime import datetime, timezone, timedelta

In [2]: def force_timezone(dt, utc_offset=0):
   ...:     return dt.replace(tzinfo=timezone(timedelta(hours=utc_offset)))
   ...:

In [3]: dt = datetime(2011,8,15,8,15,12,0)

In [4]: str(dt)
Out[4]: '2011-08-15 08:15:12'

In [5]: str(force_timezone(dt, -8))
Out[5]: '2011-08-15 08:15:12-08:00'

Использование timezone(timedelta(hours=n))в качестве часового пояса настоящей серебряной пули здесь, и у нее есть много других полезных приложений.


0

Если вы получите текущее время и дату в python, то импортируйте дату и время, пакет pytz в python после того, как вы получите текущую дату и время, например, как ..

from datetime import datetime
import pytz
import time
str(datetime.strftime(datetime.now(pytz.utc),"%Y-%m-%d %H:%M:%S%t"))

0

Другая альтернатива, на мой взгляд, лучшая, использует Pendulumвместо pytz. Рассмотрим следующий простой код:

>>> import pendulum

>>> dt = pendulum.now().to_iso8601_string()
>>> print (dt)
2018-03-27T13:59:49+03:00
>>>

Чтобы установить Pendulum и посмотреть их документацию, перейдите сюда . У него множество опций (например, простая поддержка ISO8601, RFC3339 и многих других форматов), лучшая производительность и, как правило, более простой код.


не уверен, почему здесь голосование, этот код работает в нескольких программах, которые работают 7/24 для меня :). Не то чтобы я возражал против другого мнения, но, пожалуйста, скажите, почему это не работает для вас, чтобы я мог это проверить. Заранее спасибо
ng10

0

Используйте часовой пояс, как показано ниже, для времени с учетом часового пояса. По умолчанию используется UTC:

from django.utils import timezone
today = timezone.now()

0

Тайлер из 'howchoo' сделал действительно замечательную статью, которая помогла мне лучше понять объекты Datetime, ссылка ниже

Работа с Datetime

по сути, я просто добавил следующее в конец обоих моих объектов datetime

.replace(tzinfo=pytz.utc)

Пример:

import pytz
import datetime from datetime

date = datetime.now().replace(tzinfo=pytz.utc)

0

попробуйте pnp_datetime , все время, которое использовалось и было возвращено, относится к часовому поясу и не вызовет каких-либо смещенных наивных и смещенных проблем.

>>> from pnp_datetime.pnp_datetime import Pnp_Datetime
>>>
>>> Pnp_Datetime.utcnow()
datetime.datetime(2020, 6, 5, 12, 26, 18, 958779, tzinfo=<UTC>)

0

Следует подчеркнуть, что начиная с Python 3.6 вам нужен только стандартный lib, чтобы получить объект datetime с информацией о часовом поясе, представляющий местное время (настройку вашей ОС). Использование astimezone ()

import datetime

datetime.datetime(2010, 12, 25, 10, 59).astimezone()
# e.g.
# datetime.datetime(2010, 12, 25, 10, 59, tzinfo=datetime.timezone(datetime.timedelta(seconds=3600), 'Mitteleuropäische Zeit'))

datetime.datetime(2010, 12, 25, 12, 59).astimezone().isoformat()
# e.g.
# '2010-12-25T12:59:00+01:00'

# I'm on CET/CEST

(см. комментарий @ johnchen902).

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.