Mengapa datetime.datetime.utcnow () tidak mengandung informasi zona waktu?

285
datetime.datetime.utcnow()

Mengapa ini datetimetidak memiliki info zona waktu mengingat bahwa itu adalah UTC secara eksplisit datetime?

Saya berharap ini akan mengandung tzinfo.

Vitaly Babiy
sumber
Bagaimana cara mengubah bidang format tanggal iso normal yang bertipe string ke format utc?
Navi

Jawaban:

192

Itu berarti zona waktu itu naif, jadi Anda tidak bisa menggunakannya datetime.astimezone

Anda bisa memberikan zona waktu seperti ini

import pytz  # 3rd party: $ pip install pytz

u = datetime.utcnow()
u = u.replace(tzinfo=pytz.utc) #NOTE: it works only with a fixed utc offset

sekarang Anda dapat mengubah zona waktu

print(u.astimezone(pytz.timezone("America/New_York")))

Untuk mendapatkan waktu saat ini di zona waktu tertentu, Anda bisa mengirimkan tzinfo ke datetime.now()langsung:

#!/usr/bin/env python
from datetime import datetime
import pytz # $ pip install pytz

print(datetime.now(pytz.timezone("America/New_York")))

Ia bekerja untuk zona waktu apa pun termasuk yang mengamati daylight saving time (DST) yaitu, ia bekerja untuk zona waktu yang mungkin memiliki offset utc yang berbeda pada waktu yang berbeda (offset utc yang tidak tetap). Jangan gunakan tz.localize(datetime.now())- ini mungkin gagal selama transisi akhir-DST ketika waktu setempat ambigu.

John La Rooy
sumber
216
Tetapi tidak ada alasan yang baik untuk menjadi naif zona waktu - ini ditentukan sebagai UTC. Mengapa Anda perlu mencari perpustakaan pihak ketiga untuk membuatnya berfungsi dengan baik?
Mark Ransom
4
Saya setuju; bagi saya masa 'naif' sama sekali tidak berguna. Ada diskusi pada daftar python saat ini tentang menambahkan pytz ke stdlib; masalahnya bukan perizinan tetapi fakta bahwa data zona waktu sering diperbarui (yang tidak bisa dilakukan oleh Python sendiri). Juga pytz tidak mengimplementasikan antarmuka tzinfo dengan cara yang diharapkan sehingga Anda bisa mendapatkan kesalahan jika Anda mencoba menggunakan beberapa zona waktu kota di astimezone. Jadi datetime tidak hanya tidak memiliki zona waktu asli, tetapi satu-satunya implementasi tzinfo yang tersedia secara luas adalah tidak sesuai dengan standar yang seharusnya.
bobince
5
@obobince Mengapa pytz dan pustaka datetime standar tidak cocok untuk Anda? Inti Python dan pytz berkembang sebagai proyek independen mengurangi kompleksitas logistik untuk tim inti. Ya, mengurangi kompleksitas untuk tim inti Python meningkatkan kompleksitas untuk semua pengguna Python yang perlu berurusan dengan zona waktu tetapi, saya percaya mereka membuat keputusan ini untuk alasan yang bagus. Aturan "Perpustakaan standar tidak memiliki instance tzinfo ..." bagus karena sederhana, mengapa membuat pengecualian di sini?
Derek Litz
15
Bagaimana kalauu=datetime.now(pytz.utc)
Craig McQueen
4
@ Bain: jangan gunakan tz.localize(datetime.now()); gunakan datetime.now(tz)saja.
jfs
142

Perhatikan bahwa untuk Python 3.2 dan seterusnya, datetimemodul berisi datetime.timezone. Dokumentasi untuk datetime.utcnow()mengatakan:

Datetime UTC terkini yang sadar dapat diperoleh dengan menelepon .datetime.now(timezone.utc)

Jadi kamu bisa melakukan:

>>> import datetime
>>> datetime.datetime.now(datetime.timezone.utc)
datetime.datetime(2014, 7, 10, 2, 43, 55, 230107, tzinfo=datetime.timezone.utc)
Craig McQueen
sumber
2
Yang mana yang lebih disukai? datetime.now(timezone.utc)atau datetime.utcnow(timezone.utc)?
Jesse Webb
8
datetime.utcnow()tidak membutuhkan argumen. Jadi harus begitu datetime.now(timezone.utc).
Craig McQueen
1
datetime.now()akan mengembalikan waktu mesin tetapi datetime.utcnow()akan mengembalikan waktu UTC yang sebenarnya.
Babu
13
@Babu: datetime.utcnow()tidak disetel tzinfountuk menunjukkan bahwa itu UTC. Tetapi datetime.now(datetime.timezone.utc)mengembalikan waktu UTC dengan tzinfo set.
Craig McQueen
@CraigMcQueen Jadi, jika kita melewatkan tzobjek di konstruktor sekarang, ia akan mengembalikan waktu zona waktu itu? Baik! Terima kasih telah menunjukkan.
Babu
71

Pustaka Python standar tidak menyertakan kelas tzinfo (tetapi lihat pep 431 ). Saya hanya bisa menebak alasannya. Secara pribadi saya pikir itu adalah kesalahan untuk tidak memasukkan kelas tzinfo untuk UTC, karena yang satu itu cukup tidak kontroversial untuk memiliki implementasi standar.

Sunting: Meskipun tidak ada implementasi di perpustakaan, ada satu yang diberikan sebagai contoh dalam tzinfodokumentasi .

from datetime import timedelta, tzinfo

ZERO = timedelta(0)

# A UTC class.

class UTC(tzinfo):
    """UTC"""

    def utcoffset(self, dt):
        return ZERO

    def tzname(self, dt):
        return "UTC"

    def dst(self, dt):
        return ZERO

utc = UTC()

Untuk menggunakannya, untuk mendapatkan waktu saat ini sebagai objek datetime sadar:

from datetime import datetime 

now = datetime.now(utc)

Ada datetime.timezone.utcdalam Python 3.2+:

from datetime import datetime, timezone 

now = datetime.now(timezone.utc)
Mark tebusan
sumber
8
Cari tahu mengapa kelas ini tidak disediakan di tempat pertama (dan, yang lebih penting, digunakan untuk datetimeobjek yang dibuat oleh utcnow()) ...
André Caron
17
Objek zona waktu timezone.utcakhirnya telah ditambahkan ke Python 3.2. Untuk kompatibilitas mundur, utcnow()masih mengembalikan objek waktu kurang zona, tetapi Anda bisa mendapatkan apa yang Anda inginkan dengan menelepon now(timezone.utc).
mhsmith
4
@rgove, itulah jenis kesalahan yang seharusnya menjadi permainan adil bagi Python 3. Mereka seharusnya tidak khawatir tentang kompatibilitas ke belakang. Ada contoh lain yang saya baca dalam beberapa hari terakhir - structmodul akan melakukan konversi otomatis dari Unicode ke bytestring, dan keputusan terakhir adalah untuk memutus kompatibilitas dengan versi Python 3 sebelumnya untuk mencegah keputusan buruk dari maju.
Mark Ransom
2
Saya heran bahwa tzinfodokumentasi Python termasuk contoh kode untuk mengimplementasikannya, tetapi mereka tidak memasukkan fungsi itu dalam datetime itu sendiri! docs.python.org/2/library/datetime.html#datetime.tzinfo.fromutc
LS
1
@ LS ya, pytzadalah sumber yang bagus. Pada saat saya mengedit jawaban saya untuk dimasukkan ke dalam kode contoh, orang lain telah menyarankannya dan saya tidak ingin mencuri guntur mereka.
Mark Ransom
20

The pytzModul adalah salah satu pilihan, dan ada lagi python-dateutil, yang meskipun juga paket pihak ketiga, mungkin sudah tersedia, tergantung pada dependensi lain dan sistem operasi.

Saya hanya ingin memasukkan metodologi ini untuk referensi - jika Anda sudah menginstal python-dateutiluntuk tujuan lain, Anda dapat menggunakannya tzinfodaripada menduplikasipytz

import datetime
import dateutil.tz

# Get the UTC time with datetime.now:
utcdt = datetime.datetime.now(dateutil.tz.tzutc())

# Get the UTC time with datetime.utcnow:
utcdt = datetime.datetime.utcnow()
utcdt = utcdt.replace(tzinfo=dateutil.tz.tzutc())

# For fun- get the local time
localdt = datetime.datetime.now(dateutil.tz.tzlocal())

Saya cenderung setuju bahwa panggilan utcnowharus mencakup informasi zona waktu UTC. Saya menduga bahwa ini tidak termasuk karena pustaka datetime bawaan default ke naet data untuk kompatibilitas lintas.

bbengfort
sumber
1
NameError: nama 'dt' tidak didefinisikan
xApple
Saya menggunakan panggilan datetime.datetime.utcfromtimestamp (), dan perlu menambahkan tzinfo, Solusi kedua bekerja untuk saya: utcdt = datetime.datetime.utcfromtimestamp(1234567890).replace(dateutil.tz.tzutc())
Ian Lee
1
Catatan: tidak seperti datetime.now(pytz_tz)itu selalu berhasil; datetime.now(dateutil.tz.tzlocal())mungkin gagal selama transisi DST . PEP 495 - Disambiguasi Waktu Lokal dapat memperbaiki dateutilsituasi di masa depan.
jfs
@IanLee: Anda bisa menggunakan utc_dt = datetime.fromtimestamp(1234567890, dateutil.tz.tzutc())(catatan: dateutildengan utc non-fixed offset (seperti dateutil.tz.tzlocal()) mungkin gagal di sini , menggunakan sebuah pytzsolusi berbasis gantinya ).
jfs
Sejak program saya sudah mengimpor dateutiluntuk dateutil.parser, aku suka solusi ini yang terbaik. Itu yang sederhana seperti: utcCurrentTime = datetime.datetime.now(tz=dateutil.tz.tzutc()). Biola!!
LS
11

Julien Danjou menulis artikel bagus yang menjelaskan mengapa Anda tidak boleh berurusan dengan zona waktu . Kutipan:

Memang, API datetime Python selalu mengembalikan objek datetime yang tidak disadari, yang sangat disayangkan. Memang, begitu Anda mendapatkan salah satu objek ini, tidak ada cara untuk mengetahui zona waktu itu, oleh karena itu objek-objek ini cukup "tidak berguna" sendiri.

Sayangnya, meskipun Anda dapat menggunakan utcnow(), Anda masih tidak akan melihat info zona waktu, seperti yang Anda temukan.

Rekomendasi:

  • Selalu gunakan datetimeobjek sadar , yaitu dengan informasi zona waktu. Itu memastikan Anda dapat membandingkannya secara langsung (objek sadar dan tidak sadar datetime tidak sebanding) dan akan mengembalikannya dengan benar kepada pengguna. Leverage pytz untuk memiliki objek zona waktu.

  • Gunakan ISO 8601 sebagai format string input dan output. Gunakan datetime.datetime.isoformat()untuk mengembalikan cap waktu sebagai string yang diformat menggunakan format itu, yang mencakup informasi zona waktu.

  • Jika Anda perlu mengurai string yang berisi cap waktu berformat ISO 8601, Anda dapat mengandalkan iso8601, yang mengembalikan cap waktu dengan informasi zona waktu yang benar. Ini membuat cap waktu secara langsung dapat dibandingkan.

Joe D'Andrea
sumber
1
Rekomendasi ini sedikit menyesatkan. Aturan praktisnya adalah, jangan pernah berurusan dengan zona waktu. Selalu simpan dan kirimkan benda-benda utc yang tidak disadari (objek zaman). Zona waktu seharusnya hanya dihitung pada saat representasi di UI
nehem
1
Kedengarannya sudah cocok dengan pikiran Julien dengan cukup baik. Manakah dari rekomendasi spesifiknya (sebagaimana dirujuk di atas) yang menyesatkan?
Joe D'Andrea
10

Untuk menambahkan timezoneinformasi dalam Python 3.2+

import datetime

>>> d = datetime.datetime.now(tz=datetime.timezone.utc)
>>> print(d.tzinfo)
'UTC+00:00'
tidak lahir
sumber
1
AttributeError: 'module' object has no attribute 'timezone' Python 2.7.13 (default, 19 Jan 2017, 14:48:08)
Marcin Owsiany
-6
from datetime import datetime 
from dateutil.relativedelta import relativedelta
d = datetime.now()
date = datetime.isoformat(d).split('.')[0]
d_month = datetime.today() + relativedelta(months=1)
next_month = datetime.isoformat(d_month).split('.')[0]
Mrudula Athuluri
sumber
-13

Tanggal UTC tidak memerlukan info zona waktu karena itu UTC, yang menurut definisi berarti mereka tidak memiliki offset.

Ignacio Vazquez-Abrams
sumber
10
Sejauh yang saya tahu dari docs.python.org/library/datetime.html , sebuah datetime tanpa tzinfo adalah satu di mana zona waktu tidak ditentukan. Berikut zona waktu telah ditentukan, sehingga secara logis harus hadir. Ada perbedaan besar antara tanggal / waktu tanpa zona waktu yang terkait dan yang pasti dalam UTC. (Idealnya mereka harus tipe IMO yang berbeda, tapi itu masalah lain ...)
Jon Skeet
@ JonSkeet Saya pikir Anda kehilangan titik Ignacio bahwa UTC bukan zona waktu. Luar biasa bahwa jawaban ini memiliki skor -9 saat saya mengetik ini ...
CS
3
@CS: Yah, Ignacio tidak pernah menyatakan bahwa ... dan walaupun secara tegas UTC bukan zona waktu, biasanya diperlakukan sebagai salah satu untuk membuat hidup lebih sederhana (termasuk dalam Python, misalnya dengan pytz.utc). Perhatikan bahwa ada perbedaan besar antara nilai yang offsetnya dari UTC tidak diketahui dan nilai di mana nilai itu dikenal sebagai 0. Yang terakhir adalah yang utcnow() harus dikembalikan, IMO. Itu akan cocok dengan "Objek sadar digunakan untuk mewakili momen tertentu dalam waktu yang tidak terbuka untuk interpretasi" sesuai dokumentasi.
Jon Skeet