Cara menggunakan python timezone from string


Apa yang harus saya lakukan

Saya memiliki objek waktu-waktu tidak sadar zona waktu, yang saya perlu menambahkan zona waktu agar dapat membandingkannya dengan objek waktu-waktu sadar-waktu-zona waktu lainnya. Saya tidak ingin mengonversi seluruh aplikasi saya ke zona waktu tanpa mengetahui untuk kasus warisan yang satu ini.

Apa yang saya coba

Pertama, untuk menunjukkan masalahnya:

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

Pertama, saya mencoba 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
>>>

Tidak terlalu mengejutkan bahwa ini gagal, karena sebenarnya mencoba melakukan konversi. Ganti sepertinya pilihan yang lebih baik (sesuai Python: Cara mendapatkan nilai datetime.today () yaitu "timezone aware"? ):

>>> 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
>>> 

Tapi seperti yang Anda lihat, ganti tampaknya mengatur tzinfo, tetapi tidak membuat objek sadar. Saya sedang bersiap-siap untuk kembali ke doctoring string input untuk memiliki zona waktu sebelum parsing (saya menggunakan dateutil untuk parsing, jika itu penting), tapi itu tampaknya sangat muram.

Juga, saya sudah mencoba ini di kedua python 2.6 dan python 2.7, dengan hasil yang sama.

Konteks

Saya menulis parser untuk beberapa file data. Ada format lama yang saya perlukan untuk mendukung di mana string tanggal tidak memiliki indikator zona waktu. Saya sudah memperbaiki sumber data, tetapi saya masih perlu mendukung format data lawas. Konversi satu kali data legacy bukan pilihan untuk berbagai alasan BS bisnis. Sementara secara umum, saya tidak suka gagasan hard-coding zona waktu default, dalam hal ini sepertinya pilihan terbaik. Saya tahu dengan keyakinan yang masuk akal bahwa semua data lawas yang dimaksud adalah dalam UTC, jadi saya siap untuk menerima risiko gagal bayar itu dalam hal ini.




Jawaban:


Secara umum, untuk membuat zona waktu sadar-naif yang naif, gunakan metode pelokalan :

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

Untuk zona waktu UTC, itu tidak perlu digunakan localizekarena tidak ada perhitungan waktu musim panas untuk menangani:

now_aware = unaware.replace(tzinfo=pytz.UTC)

bekerja. ( .replacemengembalikan datetime baru; tidak diubah unaware.)







Semua contoh ini menggunakan modul eksternal, tetapi Anda dapat mencapai hasil yang sama hanya menggunakan modul datetime, seperti juga disajikan dalam jawaban SO ini :

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'

Lebih sedikit ketergantungan dan tidak ada masalah pytz.

CATATAN: Jika Anda ingin menggunakan ini dengan python3 dan python2, Anda dapat menggunakan ini juga untuk impor zona waktu (hardcode untuk 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()







Saya telah menggunakan dari dt_aware ke dt_unaware

dt_unaware = dt_aware.replace(tzinfo=None)

dan dt_unware ke dt_aware

from pytz import timezone
localtz = timezone('Europe/Lisbon')
dt_aware = localtz.localize(dt_unware)

tetapi jawaban sebelumnya juga merupakan solusi yang baik.





Saya menggunakan pernyataan ini di Django untuk mengubah waktu yang tidak disadari menjadi sadar:

from django.utils import timezone

dt_aware = timezone.make_aware(dt_unaware, timezone.get_current_timezone())




Saya setuju dengan jawaban sebelumnya, dan tidak masalah jika Anda baik-baik saja memulai di UTC. Tetapi saya pikir ini juga merupakan skenario umum bagi orang untuk bekerja dengan nilai sadar yang memiliki waktu yang memiliki zona waktu lokal non UTC.

Jika Anda hanya pergi dengan nama, seseorang mungkin akan menyimpulkan ganti () akan berlaku dan menghasilkan objek sadar datetime yang tepat. Ini bukan kasusnya.

ganti (tzinfo = ...) tampaknya acak dalam perilakunya . Karena itu tidak berguna. Jangan gunakan ini!

pelokalan adalah fungsi yang benar untuk digunakan. Contoh:

localdatetime_aware = tz.localize(datetime_nonaware)

Atau contoh yang lebih lengkap:

import pytz
from datetime import datetime
pytz.timezone('Australia/Melbourne').localize(datetime.now())

memberi saya nilai data waktu sadar zona waktu saat ini:

datetime.datetime(2017, 11, 3, 7, 44, 51, 908574, tzinfo=<DstTzInfo 'Australia/Melbourne' AEDT+11:00:00 DST>)




Gunakan dateutil.tz.tzlocal()untuk mendapatkan zona waktu dalam penggunaan datetime.datetime.now()dan datetime.datetime.astimezone():

from datetime import datetime
from dateutil import tz

unlocalisedDatetime = datetime.now()

localisedDatetime1 = datetime.now(tz = tz.tzlocal())
localisedDatetime2 = datetime(2017, 6, 24, 12, 24, 36, tz.tzlocal())
localisedDatetime3 = unlocalisedDatetime.astimezone(tz = tz.tzlocal())
localisedDatetime4 = unlocalisedDatetime.replace(tzinfo = tz.tzlocal())

Perhatikan bahwa datetime.astimezonepertama-tama akan mengonversi datetimeobjek Anda ke UTC kemudian menjadi zona waktu, yang sama dengan menelepon datetime.replacedengan informasi zona waktu yang asli None.




Ini mengkodifikasikan jawaban @ Sérgio dan @ unutbu . Ini akan "hanya bekerja" dengan pytz.timezoneobjek atau string Zona Waktu IANA .

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) 

Ini terlihat seperti apa yang datetime.localize()(atau .inform()atau .awarify()) harus dilakukan, menerima objek string dan zona waktu untuk argumen tz dan default ke UTC jika tidak ada zona waktu yang ditentukan.



Python 3.9 menambahkan zoneinfomodul jadi sekarang hanya perpustakaan standar yang diperlukan!

from zoneinfo import ZoneInfo
from datetime import datetime
unaware = datetime(2020, 10, 31, 12)

Lampirkan zona waktu:

>>> unaware.replace(tzinfo=ZoneInfo('Asia/Tokyo'))
datetime.datetime(2020, 10, 31, 12, 0, tzinfo=zoneinfo.ZoneInfo(key='Asia/Tokyo'))
>>> str(_)
'2020-10-31 12:00:00+09:00'

Pasang zona waktu lokal sistem:

>>> unaware.replace(tzinfo=ZoneInfo('localtime'))
datetime.datetime(2020, 10, 31, 12, 0, tzinfo=zoneinfo.ZoneInfo(key='localtime'))
>>> str(_)
'2020-10-31 12:00:00+01:00'

Selanjutnya dikonversi dengan benar ke zona waktu lain:

>>> unaware.replace(tzinfo=ZoneInfo('localtime')).astimezone(ZoneInfo('Asia/Tokyo'))
datetime.datetime(2020, 10, 31, 20, 0, tzinfo=backports.zoneinfo.ZoneInfo(key='Asia/Tokyo'))
>>> str(_)
'2020-10-31 20:00:00+09:00'

Daftar zona waktu Wikipedia yang tersedia


Ada backport yang memungkinkan penggunaan dalam Python 3.6 hingga 3.8 :

sudo pip install backports.zoneinfo

Kemudian:

from backports.zoneinfo import ZoneInfo





Dalam format jawaban unutbu; Saya membuat modul utilitas yang menangani hal-hal seperti ini, dengan sintaks yang lebih intuitif. Dapat diinstal dengan 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')


bagi mereka yang hanya ingin membuat waktu sadar zona waktu

import datetime
import pytz

datetime.datetime(2019, 12, 7, tzinfo=pytz.UTC)


cukup baru untuk Python dan saya mengalami masalah yang sama. Saya menemukan solusi ini cukup sederhana dan bagi saya itu berfungsi dengan baik (Python 3.6):

unaware=parser.parse("2020-05-01 0:00:00")
aware=unaware.replace(tzinfo=tz.tzlocal()).astimezone(tz.tzlocal())


Berganti di antara zona waktu

import pytz
from datetime import datetime

other_tz = pytz.timezone('Europe/Madrid')

# From random aware datetime...
aware_datetime = datetime.utcnow().astimezone(other_tz)
>> 2020-05-21 08:28:26.984948+02:00

# 1. Change aware datetime to UTC and remove tzinfo to obtain an unaware datetime
unaware_datetime = aware_datetime.astimezone(pytz.UTC).replace(tzinfo=None)
>> 2020-05-21 06:28:26.984948

# 2. Set tzinfo to UTC directly on an unaware datetime to obtain an utc aware datetime
aware_datetime_utc = unaware_datetime.replace(tzinfo=pytz.UTC)
>> 2020-05-21 06:28:26.984948+00:00

# 3. Convert the aware utc datetime into another timezone
reconverted_aware_datetime = aware_datetime_utc.astimezone(other_tz)
>> 2020-05-21 08:28:26.984948+02:00

# Initial Aware Datetime and Reconverted Aware Datetime are equal
print(aware_datetime1 == aware_datetime2)
>> True