Jak uświadamiać nieświadomą strefę czasową datetime w Pythonie
Co muszę zrobić
Mam obiekt datetime nieświadomy strefy czasowej, do którego muszę dodać strefę czasową, aby móc porównać go z innymi obiektami datetime znającymi strefę czasową. Nie chcę przekonwertować całej mojej aplikacji na strefę czasową.
Czego próbowałem
Po pierwsze, aby zademonstrować problem:
Python 2.6.1 (r261:67515, Jun 24 2010, 21:47:49)
[GCC 4.2.1 (Apple Inc. build 5646)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import datetime
>>> import pytz
>>> unaware = datetime.datetime(2011,8,15,8,15,12,0)
>>> unaware
datetime.datetime(2011, 8, 15, 8, 15, 12)
>>> aware = datetime.datetime(2011,8,15,8,15,12,0,pytz.UTC)
>>> aware
datetime.datetime(2011, 8, 15, 8, 15, 12, tzinfo=<UTC>)
>>> aware == unaware
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: can't compare offset-naive and offset-aware datetimes
Najpierw próbowałem astimezone:
>>> unaware.astimezone(pytz.UTC)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: astimezone() cannot be applied to a naive datetime
>>>
Nie dziwi mnie to, że się nie udało, ponieważ jest próbuję dokonać konwersji. Replace wydawało się lepszym wyborem (zgodnie z Python: jak uzyskać wartość datetime.dzisiaj() czyli"strefa czasowa"?):
>>> unaware.replace(tzinfo=pytz.UTC)
datetime.datetime(2011, 8, 15, 8, 15, 12, tzinfo=<UTC>)
>>> unaware == aware
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: can't compare offset-naive and offset-aware datetimes
>>>
Ale jak widzisz, replace wydaje się ustawiać tzinfo, ale nie uświadamiać obiektu. Przygotowuję się do powrotu do doctoringu łańcucha wejściowego, aby mieć strefę czasową przed parsowaniem (używam dateutil do parsowania, jeśli to ma znaczenie), ale wydaje się to niesamowicie kludgy.
Również, próbowałem tego w obu python 2.6 i python 2.7, z tymi samymi wynikami.
Context
Piszę parser dla niektórych plików danych. Istnieje stary format, który muszę obsługiwać, gdzie ciąg daty nie ma wskaźnika strefy czasowej. Naprawiłem już źródło danych, ale nadal muszę obsługiwać stary format danych. Jednorazowa konwersja starszych danych nie jest opcją z różnych powodów biznesowych. Chociaż ogólnie rzecz biorąc, nie podoba mi się pomysł hard-kodowania domyślnej strefy czasowej, w tym sprawa wydaje się najlepszą opcją. Wiem z rozsądną pewnością, że wszystkie starsze dane, o których mowa, są w UTC, więc jestem gotów zaakceptować ryzyko niewykonania tego w tym przypadku.
9 answers
Ogólnie rzecz biorąc, aby uświadomić sobie naiwną strefę czasową datetime, użyj metody localize :
import datetime
import pytz
unaware = datetime.datetime(2011, 8, 15, 8, 15, 12, 0)
aware = datetime.datetime(2011, 8, 15, 8, 15, 12, 0, pytz.UTC)
now_aware = pytz.utc.localize(unaware)
assert aware == now_aware
Dla strefy czasowej UTC nie jest konieczne używanie localize
, ponieważ nie ma obliczeń czasu letniego do obsługi:
now_aware = unaware.replace(tzinfo=pytz.UTC)
Działa. (.replace
zwraca nową datetime; nie modyfikuje unaware
.)Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-04-08 02:47:03
Wszystkie te przykłady używają zewnętrznego modułu, ale możesz osiągnąć ten sam wynik używając tylko modułu datetime.
from datetime import datetime
from datetime import timezone
dt = datetime.now()
dt.replace(tzinfo=timezone.utc)
print(dt.replace(tzinfo=timezone.utc).isoformat())
'2017-01-12T22:11:31+00:00'
Mniej zależności i brak problemów z pytz.
Uwaga: jeśli chcesz użyć tego z python3 i python2, możesz użyć tego również do importu strefy czasowej (na twardo dla UTC):
try:
from datetime import timezone
utc = timezone.utc
except ImportError:
#Hi there python2 user
class UTC(tzinfo):
def utcoffset(self, dt):
return timedelta(0)
def tzname(self, dt):
return "UTC"
def dst(self, dt):
return timedelta(0)
utc = UTC()
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-12-10 13:18:31
Użyłem z dt_aware do dt_unware
dt_unaware = dt_aware.replace(tzinfo=None)
I dt_unware do dt_aware
from pytz import timezone
localtz = timezone('Europe/Lisbon')
dt_aware = localtz.localize(dt_unware)
Ale odpowiedź przed jest również dobrym rozwiązaniem.Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-05-15 21:00:12
Używam tego stwierdzenia w Django do konwersji nieświadomego czasu na świadomego:
from django.utils import timezone
dt_aware = timezone.make_aware(dt_unaware, timezone.get_current_timezone())
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-10-19 10:10:01
To kodyfikuje odpowiedzi @Sérgio i @unutbu . Będzie "po prostu działać" z obiektem pytz.timezone
lub łańcuchemIANA Time Zone .
def make_tz_aware(dt, tz='UTC', is_dst=None):
"""Add timezone information to a datetime object, only if it is naive."""
tz = dt.tzinfo or tz
try:
tz = pytz.timezone(tz)
except AttributeError:
pass
return tz.localize(dt, is_dst=is_dst)
Wygląda na to, co datetime.localize()
(lub .inform()
lub .awarify()
) powinno robić, akceptować zarówno ciągi znaków, jak i Obiekty strefy czasowej dla argumentu tz i domyślnie ustawiać na UTC, jeśli nie podano strefy czasowej.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:18:25
Zgadzam się z poprzednimi odpowiedziami i jest w porządku, jeśli możesz zacząć w UTC. Ale myślę, że jest to również powszechny scenariusz dla ludzi do pracy z wartością świadomą TZ, która ma datetime, która ma lokalną strefę czasową non UTC.
Jeśli miałbyś używać nazwy, prawdopodobnie można by wywnioskować, że replace() będzie miało zastosowanie i wytworzy właściwy obiekt DateTime aware. Tak nie jest.
Replace (tzinfo=... ) wydaje się być przypadkowy w swoim zachowaniu . Jest zatem bezużyteczne. Nie używaj tego!
Localize jest właściwą funkcją do użycia. Przykład:
localdatetime_aware = tz.localize(datetime_nonaware)
Lub bardziej kompletny przykład:
import pytz
from datetime import datetime
pytz.timezone('Australia/Melbourne').localize(datetime.now())
Podaje mi wartość DateTime strefy czasowej bieżącego czasu lokalnego:
datetime.datetime(2017, 11, 3, 7, 44, 51, 908574, tzinfo=<DstTzInfo 'Australia/Melbourne' AEDT+11:00:00 DST>)
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-11-02 21:29:06
W celu dodania lokalnej strefy czasowej; (używając dateutil)
from dateutil import tz
import datetime
dt_unaware = datetime.datetime(2017, 6, 24, 12, 24, 36)
dt_aware = dt_unaware.replace(tzinfo=tz.tzlocal())
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-07-22 00:15:14
Two method I know purely
from datetime import datetime
import pytz
naive = datetime.now()
aware = naive.replace(tzinfo=pytz.UTC)
Lub
aware = pytz.UTC.localize(naive)
Oczywiście możesz użyć dowolnej strefy czasowej zamiast UTC,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-08-22 02:46:44
W formacie odpowiedzi unutbu; zrobiłem moduł narzędziowy, który obsługuje takie rzeczy, z bardziej intuicyjną składnią. Może być zainstalowany za pomocą pip.
import datetime
import saturn
unaware = datetime.datetime(2011, 8, 15, 8, 15, 12, 0)
now_aware = saturn.fix_naive(unaware)
now_aware_madrid = saturn.fix_naive(unaware, 'Europe/Madrid')
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-09-25 23:31:51