Bagaimana cara mendapatkan nilai datetime.today () dengan Python yaitu "timezone aware"?


310

Saya mencoba mengurangi satu nilai tanggal dari nilai datetime.today()untuk menghitung berapa lama sesuatu itu terjadi. Tapi itu mengeluh:

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

Nilai datetime.today()tampaknya tidak "sadar zona waktu", sedangkan nilai tanggal saya yang lain adalah. Bagaimana cara saya mendapatkan nilai datetime.today()yang diketahui oleh zona waktu?

Saat ini, ini memberi saya waktu dalam waktu lokal, yang kebetulan adalah PST, yaitu UTC - 8 jam. Kasus terburuk, adakah cara saya bisa secara manual memasukkan nilai zona waktu ke datetimeobjek yang dikembalikan oleh datetime.today()dan mengaturnya ke UTC-8?

Tentu saja, solusi ideal adalah dengan mengetahui zona waktu secara otomatis.



10
Sepertinya kita dapat menggunakan datetime.now().astimezone()sejak Python 3.6
johnchen902

Jawaban:


362

Di perpustakaan standar, tidak ada cara lintas platform untuk membuat zona waktu sadar tanpa membuat kelas zona waktu Anda sendiri.

Di Windows, ada win32timezone.utcnow(), tapi itu bagian dari pywin32. Saya lebih suka menyarankan untuk menggunakan pytz library , yang memiliki basis data yang diperbarui secara konstan di sebagian besar zona waktu.

Bekerja dengan zona waktu lokal bisa sangat rumit (lihat tautan "Bacaan lebih lanjut" di bawah), jadi Anda mungkin ingin menggunakan UTC di seluruh aplikasi Anda, terutama untuk operasi aritmatika seperti menghitung perbedaan antara dua titik waktu.

Anda bisa mendapatkan tanggal / waktu saat ini seperti:

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

Pikirkan itu datetime.today()dan datetime.now()kembalikan waktu setempat , bukan waktu UTC, jadi melamar .replace(tzinfo=pytz.utc)mereka tidak akan benar.

Cara lain yang baik untuk melakukannya adalah:

datetime.now(pytz.utc)

yang sedikit lebih pendek dan melakukan hal yang sama.


Bacaan lebih lanjut / menonton mengapa lebih suka UTC dalam banyak kasus:


75
Bagaimana kalau datetime.now(pytz.utc)bukan datetime.utcnow().replace(tzinfo = pytz.utc)?
eumiro

5
now(utc)tidak kembali hari ini (kecuali tengah malam di UTC), itu mengembalikan waktu saat ini dalam UTC. Anda juga perlu .replace(hour=0, minute=0, ...)mendapatkan awal hari (seperti datetime.today())
jfs

1
The docs mengatakan bahwa today()pengembalian waktu saat ini, tidak tengah malam. Jika ada kasus penggunaan di mana tengah malam diperlukan, ya, penggantian harus dilakukan sesuai. Karena pertanyaan aslinya adalah tentang perbedaan waktu, saya tidak berpikir bahwa tengah malam diperlukan.
AndiDog

1
@AndiDog: Komentar saya menyiratkan bahwa saya pikir (salah) yang datetime.today()adalah combine(date.today(), time()). datetimememiliki keduanya .now()dan .today()metode yang (seperti yang Anda tunjukkan dengan benar) mengembalikan (hampir) hal yang sama. Tidak ada date.now()metode. datedan datetimeobjek tidak bisa saling dipertukarkan. Menggunakan objek datetime alih-alih dateobjek dapat menghasilkan bug halus; Saya tidak melihat alasan untuk datetime.today()ada jika itu adalah duplikat dekat datetime.now().
jfs

6
Menambahkan ke jawaban ini, jika Anda kebetulan menggunakan Django, selalu gunakan timezone.now()bukan datetime.now()karena akan menggunakan UTC secara otomatis jika USE_TZ = True. timezonemencari di django.utils.timezone, dokumentasi: docs.djangoproject.com/en/1.11/topics/i18n/timezones
Ryan

107

Dapatkan waktu saat ini, di zona waktu tertentu:

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

2
Lihat ini .
wim

1
Anda TIDAK boleh menggunakan waktu lokal kecuali untuk output. Banyak hal yang salah ketika menggunakan datetime berdasarkan zona waktu: timedelta sederhana tidak memperhitungkan penghematan waktu siang hari kecuali Anda berada di waktu UTC untuk memulai. Selalu gunakan zona waktu sadar berdasarkan UTC. konversi ke zona waktu lokal pada output saat dibutuhkan.
MrE

4
Untuk menegaskan kembali ketidaksetujuan dengan @MrE yang sebelumnya saya telah menyuarakan dalam komentar pada jawaban yang diterima: ada alasan yang sah untuk bekerja dengan datetimes lokal dan "Anda tidak boleh menggunakan waktu lokal kecuali untuk output" adalah saran yang terlalu luas. Misalkan Anda menambahkan 1 hari ke waktu beberapa jam sebelum batas waktu siang hari di mana jam kembali satu jam. Apa hasil yang Anda inginkan? Jika menurut Anda waktunya harus sama, gunakan data lokal. Jika menurut Anda seharusnya satu jam lebih awal, gunakan UTC atau data zona waktu naif. Yang masuk akal adalah tergantung domain.
Mark Amery

@MarkAmery sebanyak yang saya bisa setujui bahwa Anda mungkin ingin MENAMBAHKAN atau MENGUNDANG beberapa hari atau jam dan tidak peduli dengan masalah zona waktu (seperti contoh Anda), komentar ini berkaitan dengan melewati zona waktu yang dikoreksi waktu untuk klien. Karena Python terutama digunakan untuk proses back end, ia melewati waktu ke klien. Server harus selalu melewati tanggal / waktu dalam UTC dan klien harus mengubahnya ke tanggal / waktu / zona waktu setempat, jika tidak hal-hal buruk terjadi: cukup periksa output datetime.datetime(2016, 11, 5, 9, 43, 45, tzinfo=pytz.timezone('US/Pacific'))dan lihat apakah itu yang Anda harapkan
MrE

"Server harus selalu melewati tanggal / waktu dalam UTC dan klien harus mengubahnya ke tanggal / waktu / zona waktu setempat sendiri" - tidak, ini tidak secara universal benar. Terkadang menggunakan zona waktu klien tidak sesuai dan zona waktu yang tepat perlu ditransmisikan sebagai bagian dari data. Jika, sebagai warga London, saya melihat waktu pertemuan klub catur San Francisco di situs web mereka, saya akan melihatnya di waktu San Francisco, bukan di waktu London.
Mark Amery

69

Dalam Python 3, perpustakaan standar membuatnya lebih mudah untuk menentukan UTC sebagai zona waktu:

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

Jika Anda menginginkan solusi yang hanya menggunakan pustaka standar dan yang berfungsi di Python 2 dan Python 3, lihat jawaban jfs .

Jika Anda membutuhkan zona waktu lokal, bukan UTC, lihat jawaban Mihai Capotă


19

Inilah solusi stdlib yang bekerja pada Python 2 dan 3:

from datetime import datetime

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

di mana todayinstance datetime sadar mewakili awal hari (tengah malam) dalam UTC dan utcmerupakan objek tzinfo ( contoh dari dokumentasi ):

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

Terkait: perbandingan kinerja beberapa cara untuk mendapatkan tengah malam (awal hari) untuk waktu UTC yang diberikan . Catatan: lebih kompleks, untuk mendapatkan tengah malam untuk zona waktu dengan offset UTC yang tidak tetap .


16

Metode lain untuk membangun objek datetime sadar zona waktu mewakili waktu saat ini:

import datetime
import pytz

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

perhatikan bahwa pytz.utcdan pytz.UTCkeduanya didefinisikan (dan sama)
drevicko

2
Jawaban ini lebih baik daripada yang diterima karena lebih universal: replace()ing zona waktu umumnya rawan kesalahan dalam sebagian besar penggunaan lainnya, sementara localize()ing adalah cara yang lebih disukai untuk menetapkan zona waktu untuk cap waktu naif.
Antony Hatchkins

@AntonyHatchkins: .localize()metode gagal untuk waktu lokal yang ambigu (input non-utc). @ philfreo yang menggunakan jawaban.now(pytz_timezone) terus bekerja dalam kasus seperti itu.
jfs

Seperti yang ditentukan dalam python docs, .now(pytz_timezone)melakukan persis sama localize(utcnow)- pertama menghasilkan waktu saat ini dalam UTC, kemudian menetapkannya zona waktu: "<...> dalam hal ini hasilnya setara dengan tz.fromutc(datetime.utcnow().replace(tzinfo=tz))". Kedua jawaban itu benar dan selalu berfungsi.
Antony Hatchkins

1
Satu-satunya waktu naif (non-utc) yang dapat membuat zona waktu dengan aman disadari adalah sekarang : sistem yang mendasarinya seharusnya mengetahui nilai UTC dan pytzmelalui OLSON db seharusnya tahu cara mengubahnya ke zona waktu mana pun di dunia. Membuat sadar zona waktu naif (non-utc) lainnya sulit karena ambiguitas selama shift siang hari. Itu bukan masalah .localize(memberinya is_dstnilai membuatnya berfungsi untuk tanggal apa pun). Itu masalah yang melekat pada praktik penghematan siang hari.
Antony Hatchkins

16

Satu-liner hanya menggunakan karya perpustakaan standar dimulai dengan Python 3.3. Anda bisa mendapatkan datetimeobjek sadar zona waktu menggunakan astimezone(seperti yang disarankan oleh 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
Dokumentasi ini sangat membantu, ditemukan di sini: docs.python.org/3.8/library/… . Bagian fungsionalitas yang sangat mendasar ini terkubur dalam paragraf yang tidak jelas dalam dokumen, sehingga jawaban stackoverflow ini secara efektif menjadi satu-satunya tempat di seluruh internet dengan informasi ini. Dalam dokumentasi, juga dapat dilihat bahwa, karena Python 3.6, datetime.now()dapat dipanggil tanpa argumen dan mengembalikan hasil lokal yang benar (naif datetimes dianggap berada di zona waktu lokal).
atimholt

8

Jika Anda menggunakan Django , Anda dapat mengatur tanggal tanpa kesadaran (hanya UTC ).

Komentari baris berikut di settings.py:

USE_TZ = True

8
di mana Anda melihat Django disebutkan dalam pertanyaan ini?
vonPetrushev

1
Seseorang yang baik hati menghapus permintaan maaf komentar saya sebelumnya di sini, jadi sekali lagi: malu pada saya, jawaban yang salah karena pertanyaannya tidak spesifik untuk Django. Saya meninggalkannya karena mungkin membantu beberapa pengguna tetapi saya akan menghapusnya ketika skor mendekati 0. Jika jawaban ini tidak sesuai, jangan ragu untuk downvote.
laffuste

6

pytz adalah pustaka Python yang memungkinkan perhitungan zona waktu yang akurat dan lintas platform menggunakan Python 2.3 atau lebih tinggi.

Dengan stdlib, ini tidak mungkin.

Lihat pertanyaan serupa di SO .


6

Ini adalah salah satu cara untuk membuatnya dengan 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 akan menyimpan tanggal lokal dan offset dari UTC , bukan tanggal di zona waktu UTC, jadi Anda dapat menggunakan solusi ini jika Anda perlu mengidentifikasi zona waktu tempat tanggal dihasilkan . Dalam contoh ini dan di zona waktu lokal saya:

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

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

Kuncinya adalah menambahkan %zarahan ke FORMAT representasi, untuk menunjukkan offset UTC dari struct waktu yang dihasilkan. Format representasi lainnya dapat dikonsultasikan dalam modul datetime dokumen

Jika Anda memerlukan tanggal di zona waktu UTC, Anda dapat mengganti time.localtime () dengan 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'

Edit

Ini hanya berfungsi pada python3 . Arahan z tidak tersedia pada kode python 2 _strptime.py


ValueError: 'z' adalah arahan yang buruk dalam format '% Y-% m-% dT% H:% M:% S% z'
jno

Anda menggunakan python 2, bukan? Sayangnya, tampaknya arahan z tidak tersedia di python 2. kode
_strptime.py

6

Gunakan dateutil seperti yang dijelaskan dalam Python datetime.datetime.now () yang sadar zona waktu :

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

1
Lihat jawaban ini oleh JF Sebastian untuk situasi di mana ini memberikan hasil yang salah.
Antony Hatchkins

2
Saya pikir kesalahan di posting lain hanya relevan dalam kasus penggunaan khusus. The tzlocal()Fungsi masih merupakan salah satu solusi yang paling sederhana dan pasti harus disebutkan di sini.
user8162

2

Mendapatkan tanggal sadar utczona waktu di zona waktu sudah cukup untuk pengurangan tanggal untuk bekerja.

Tetapi jika Anda menginginkan tanggal yang diketahui zona waktu di zona waktu Anda saat ini, tzlocaladalah caranya:

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

PS dateutilmemiliki fungsi serupa ( dateutil.tz.tzlocal). Tetapi meskipun berbagi nama itu memiliki basis kode yang sama sekali berbeda, yang seperti dicatat oleh JF Sebastian dapat memberikan hasil yang salah.


python biasanya digunakan di server. Zona waktu lokal pada server biasanya tidak ada gunanya dan harus selalu diatur ke UTC. Mengatur datetime tzinfo dengan cara ini gagal dalam beberapa kasus. lebih baik menggunakan UTC, lalu melokalkan ke zona waktu yang diinginkan hanya pada output. perhitungan timedelta misalnya tidak mempertimbangkan penghematan siang hari, jadi ini harus dilakukan dalam UTC, lalu dilokalkan.
MrE

@ Mr Salah, offtopic, contoh?
Antony Hatchkins

coba gunakan objek datetime yang dilokalkan dalam zona waktu yang mengamati daylight saving, tambahkan beberapa hari untuk mengubah kondisi daylight saving, dan Anda akan melihat bahwa beroperasi pada objek datetime di zona waktu lokal gagal dan tidak akan menghargai daylight saving. Oleh karena itu komentar saya bahwa Anda harus SELALU melakukan operasi setiap waktu dalam waktu UTC.
MrE

intinya adalah: jangan lakukan ini, lakukan operasi Anda di UTC, dan kemudian gunakan datetime.astimezone (zona waktu) untuk mengkonversi ke zona waktu lokal pada output.
MrE

2

Berikut adalah solusi menggunakan zona waktu yang dapat dibaca dan berfungsi dengan hari ini ():

from pytz import timezone

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

Anda dapat membuat daftar semua zona waktu sebagai berikut:

import pytz

pytz.all_timezones
pytz.common_timezones # or

1

Khusus untuk zona waktu non-UTC:

Satu-satunya zona waktu yang memiliki metode sendiri adalah timezone.utc, tetapi Anda dapat memalsukan zona waktu dengan offset UTC apa pun jika Anda perlu menggunakan timedelta& timezone, dan memaksanya menggunakan .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'

Menggunakan timezone(timedelta(hours=n))sebagai zona waktu adalah peluru perak asli di sini, dan memiliki banyak aplikasi berguna lainnya.


0

Jika Anda mendapatkan waktu dan tanggal saat ini di python maka impor tanggal dan waktu, paket pytz di python setelah Anda akan mendapatkan tanggal dan waktu saat ini seperti ..

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

0

Alternatif lain, menurut saya yang lebih baik, adalah menggunakan Pendulumdaripada pytz. Pertimbangkan kode sederhana berikut ini:

>>> import pendulum

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

Untuk menginstal Pendulum dan melihat dokumentasinya, buka di sini . Ini memiliki banyak pilihan (seperti ISO8601 sederhana, RFC3339 dan banyak lainnya mendukung format), kinerja yang lebih baik dan cenderung menghasilkan kode yang lebih sederhana.


tidak yakin mengapa memilih di sini, kode ini berfungsi di banyak program yang menjalankan 7/24 untuk saya :). Bukan karena saya keberatan dengan pendapat lain, tapi tolong katakan mengapa itu tidak bekerja untuk Anda, untuk memungkinkan saya memeriksanya. Terima kasih sebelumnya
ng10

0

Gunakan zona waktu seperti yang ditunjukkan di bawah ini untuk waktu tanggal sadar zona waktu. Standarnya adalah UTC:

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

0

Tyler dari 'howchoo' membuat artikel yang sangat bagus yang membantu saya mendapatkan ide yang lebih baik tentang Datetime Objects, tautan di bawah ini

Bekerja dengan Datetime

intinya, saya baru saja menambahkan yang berikut ke akhir dari kedua objek datetime saya

.replace(tzinfo=pytz.utc)

Contoh:

import pytz
import datetime from datetime

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

0

coba pnp_datetime , semua waktu telah digunakan dan dikembalikan adalah dengan zona waktu, dan tidak akan menyebabkan masalah naive dan offset-aware.

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

0

Harus ditekankan bahwa sejak Python 3.6, Anda hanya perlu lib standar untuk mendapatkan objek sadar waktu zona waktu yang mewakili waktu lokal (pengaturan OS Anda). Menggunakan 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

(lihat komentar @ johnchen902).

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.