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.

mindthief
sumber
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:

AndiDog
sumber
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'))
philfreo
sumber
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ă

Flimm
sumber
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 .

jfs
sumber
16

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

import datetime
import pytz

pytz.utc.localize( datetime.datetime.utcnow() )  
Dariusz Walczak
sumber
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'))
Mihai Capotă
sumber
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
nafsu birahi
sumber
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 .

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

jcazor
sumber
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())
G. Führ
sumber
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.

Antony Hatchkins
sumber
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
JohnAndrews
sumber
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.

tmck-code
sumber
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"))
jigar vagadiya
sumber
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.

ng10
sumber
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()
Anupama V Iyengar
sumber
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)
Jose
sumber
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>)
cloudup
sumber
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).

MrFuppes
sumber