Як я можу отримати значення 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
Додавши до цієї відповіді, якщо ви випадково використовуєте джанго, завжди використовуйте 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
Дивіться це .
Вім

1
НЕ слід використовувати локалізований час, крім виводу. При використанні дати на основі часового поясу багато помилок: проста тиммедельта не враховує літній час, якщо ви не почнете з UTC. Завжди використовуйте часовий пояс на основі UTC. перетворити на локальний часовий пояс на виході при необхідності.
MrE

4
Повторювати незгоду з @MrE, яку я раніше озвучував у коментарях до прийнятої відповіді: є цілком обґрунтовані причини для роботи з локалізованими датами, і "ви НЕ повинні використовувати локалізований час, окрім вихідних даних" - надто широка порада. Припустимо, ви додаєте 1 день до часу побачення за кілька годин до межі літнього часу, коли годинник повертається на годину. Якого результату ви хочете? Якщо ви вважаєте, що час повинен бути однаковим, використовуйте локалізовані дати. Якщо ви вважаєте, що це повинно бути на годину раніше, використовуйте терміни UTC або наївні дати. Що має сенс, залежить від домену.
Марк Амері

@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відомий екземпляр дати, який представляє початок дня (опівночі) в 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

Ще один метод побудови об'єкта дати, відомого про часовий пояс, що представляє поточний час:

import datetime
import pytz

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

зауважте, що pytz.utcі pytz.UTCобидва визначені (і однакові)
drevicko

2
Ця відповідь краща за прийняту, оскільки вона є більш універсальною: replace()часовий пояс, як правило, схильний до помилок у більшості інших застосувань, тоді як localize()ing є кращим способом призначення часового поясу наївним часовим позначкам.
Ентоні Хетчкінс

@AntonyHatchkins: .localize()метод виходить з ладу для неоднозначних локальних часів (не вхідний введення). @ philfreo відповідь, яка використовує,.now(pytz_timezone) продовжує працювати в таких випадках.
jfs

Як зазначено в документах python, .now(pytz_timezone)робить точно так само, як localize(utcnow)- спочатку він генерує поточний час у UTC, потім присвоює йому часовий пояс: "<...> У цьому випадку результат еквівалентний tz.fromutc(datetime.utcnow().replace(tzinfo=tz))". Обидві відповіді правильні і працюють завжди.
Ентоні Хетчкінс

1
Єдиний наївний час, про який можна безпечно усвідомити часовий пояс, зараз : основна система повинна знати значення UTC, а pytzчерез db 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 / ... . Цей неймовірно базовий фрагмент функціоналу закопаний у неясний абзац у документах, так що ця відповідь stackoverflow є єдиним місцем у всьому Інтернеті з цією інформацією. У документації також можна побачити, що, оскільки Python 3.6 datetime.now()можна викликати без будь-яких аргументів і повернути правильний локальний результат (наївні datetimeвважаються такими, що знаходяться в локальному часовому поясі).
atimholt

8

Якщо ви використовуєте Django , ви можете встановити дати, не відомі (лише UTC) ).

Прокоментуйте наступний рядок у settings.py:

USE_TZ = True

8
де ви бачили джанго, згадане в цьому питанні?
фонПетрушев

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

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)

дата буде зберігати локальну дату і зсув від 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 сформованої структури часу. Інші формати представлення можна отримати в документах модуля дати

Якщо вам потрібна дата в часовому поясі 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 не доступна в коді _strptime.py python 2


ValueError: 'г' погана директива в форматі '% Y-% m-% дт% H:% M:% S% г'
Jno

Ви знаходитесь на python 2, правда? На жаль, здається, що директива z не доступна на python 2. _strptime.py код
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, а потім локалізуйте потрібний часовий пояс лише на виході. будь-яке обчислення timedelta, наприклад, не враховує літній час, тому це потрібно зробити в UTC, а потім локалізувати.
MrE

@MrE Неправильно, офтопік, приклади?
Ентоні Хеткінс

спробуйте використовувати об’єкт дати, локалізований у часовому поясі, який спостерігає літній час, додайте кілька днів, щоб змінити стан літнього часу, і ви побачите, що робота з об’єктами дати в локалізованому часовому поясі не вдається і не поважатиме літнє світло. Звідси мій коментар про те, що ВИНАГА слід робити будь-яку операцію з датою в UTC час.
MrE

справа: не робіть цього, виконайте свої операції в UTC, а потім використовуйте datetime.astimezone (часовий пояс) для перетворення в локальний часовий пояс на виході.
MrE

2

Ось рішення з використанням читаного часового поясу, яке працює з сьогодні ():

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 :). не те, що я проти іншої думки, але скажіть, будь ласка, чому це не працює для вас, щоб дозволити мені це перевірити. Заздалегідь
дякую

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 вам потрібна лише стандартна ліб, щоб отримати об'єкт дати, знаючи часовий пояс, який представляє місцевий час (налаштування вашої ОС). Використання астимезону ()

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.