Python'da “saat dilimi farkında” olan datetime.today () değerini nasıl alabilirim?


310

Bir datetime.today()şey ne kadar önce olduğunu hesaplamak için değerinden bir tarih değeri çıkarmaya çalışıyorum . Ama şikayet ediyor:

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

Değer datetime.today()benim diğer tarih değeri ise, "saat dilimi farkında" olmak görünmüyor. datetime.today()Saat dilimi farkında olan bir değeri nasıl alabilirim ?

Şu anda, bana yerel saatte zaman veriyor, bu da PST, yani UTC - 8 saat. En kötü durumda, datetimedöndürülen nesneye manuel olarak bir saat dilimi değeri girip datetime.today()UTC-8 olarak ayarlamamın bir yolu var mı?

Tabii ki, ideal çözüm, saat dilimini otomatik olarak tanıması olacaktır.



10
datetime.now().astimezone()Python 3.6'dan beri kullanabileceğimiz
anlaşılıyor

Yanıtlar:


362

Standart kütüphanede, kendi saat dilimi sınıfınızı oluşturmadan bilinçli zaman dilimleri oluşturmanın platformlar arası bir yolu yoktur.

Windows'da var win32timezone.utcnow(), ama bu pywin32'nin bir parçası. Çoğu zaman dilimlerinin sürekli güncellenen bir veritabanına sahip olan pytz kütüphanesini kullanmayı tercih ederim .

Yerel saat dilimleriyle çalışmak çok zor olabilir (aşağıdaki "Daha fazla okuma" bağlantısına bakın), bu nedenle uygulamanız boyunca UTC'yi kullanmak isteyebilirsiniz, özellikle iki zaman noktası arasındaki farkı hesaplamak gibi aritmetik işlemler için.

Geçerli tarihi / saati şu şekilde alabilirsiniz:

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

O Zihin datetime.today()ve datetime.now()dönüş yerel böylece uygulayarak değil, UTC zamanı süresini .replace(tzinfo=pytz.utc)onlara doğru olmaz.

Bunu yapmanın bir başka güzel yolu:

datetime.now(pytz.utc)

ki bu biraz daha kısa ve aynı şeyi yapıyor.


Birçok durumda UTC'yi neden tercih edeceğiniz hakkında daha fazla okuma / izleme:


75
datetime.now(pytz.utc)Bunun yerine nasıl datetime.utcnow().replace(tzinfo = pytz.utc)?
eumiro

5
now(utc)bugün dönmez (UTC'de gece yarısı değilse), UTC'de geçerli saati döndürür. Ayrıca .replace(hour=0, minute=0, ...)günün başlangıcınıdatetime.today()
jfs

1
Dokümanlar söylemek today()döndürür geçerli zamanı değil, gece yarısı. Gece yarısının gerekli olduğu bir kullanım durumu varsa, evet, değiştirmenin buna göre yapılması gerekir. Orijinal soru datetime farkı ile ilgili olduğu için gece yarısı gerektiğini düşünüyorum.
AndiDog

1
@AndiDog: Yorumum O (yanlış) düşünce ima datetime.today()olduğunu combine(date.today(), time()). datetimehem sahiptir .now()ve .today()yöntemler ki (doğru işaret ettik gibi) dönüş (neredeyse) aynı şey. Bir date.now()yöntem yok . dateve datetimenesneler birbirinin yerine kullanılamaz. Bir nesne yerine datetime nesnesi kullanmak, dateküçük hatalar üretebilir; datetime.today()Neredeyse bir kopyası ise var olmak için herhangi bir neden göremiyorum datetime.now().
jfs

6
Bu yanıta ek olarak, django kullanıyorsanız, her zaman timezone.now()bunun yerine kullanın, datetime.now()çünkü eğer otomatik olarak UTC'yi kullanacaktır USE_TZ = True. timezoneyer bulmak olduğunu django.utils.timezonedokümantasyon,: docs.djangoproject.com/en/1.11/topics/i18n/timezones
Ryan

107

Belirli bir saat diliminde geçerli saati alın:

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

2
Bkz bu .
wim

1
çıktı dışında yerelleştirilmiş zaman KULLANMAMALISINIZ. Saat dilimi tabanlı datetime kullanılırken pek çok şey ters gider: Basit bir timedelta, UTC saatinde başlamadığınız sürece gün ışığından tasarruf etmeyi dikkate almaz. Her zaman UTC'ye dayalı saat dilimi farkında kullanın. gerektiğinde yerel saat dilimine dönüştürün.
MrE

4
@MrE ile daha önce kabul edilen cevabın yorumlarında dile getirdiğim bir anlaşmazlığı tekrarlamak için: yerelleştirilmiş tarihlerle çalışmak için mükemmel geçerli nedenler vardır ve "çıktı dışında yerelleştirilmiş zamanı KULLANMAMALISINIZ" aşırı geniş tavsiyedir. Saatlerin bir saat geri gittiği gün ışığından yararlanma sınırından birkaç saat önce bir tarih saatine 1 gün eklediğinizi varsayalım. Hangi sonucu istiyorsun? Zamanın aynı olması gerektiğini düşünüyorsanız, yerelleştirilmiş tarihler kullanın. Bir saat önce olması gerektiğini düşünüyorsanız, UTC veya saat dilimi naif tarihlerini kullanın. Bu mantıklı, etki alanına bağlıdır.
Mark Amery

@MarkAmery, birkaç gün veya saat EKLEMEK veya SUBTRACT yapmak isteyebileceğiniz ve saat dilimi sorunlarını (örneğin gibi) önemsememenizi kabul edebildiğim kadarıyla, bu yorum saat dilimi düzeltilmiş süreyi bir müşteriye iletmekle ilgilidir. Python öncelikle arka uç işlemleri için kullanıldığından, istemciye zaman geçirir. Sadece çıktısını kontrol edin: Aksi kötü şeyler olur, sunucu her zaman UTC tarih / zaman geçirmek gerekir ve istemci kendi yerel tarih / saat / saat dilimine dönüştürmek gerektiğini datetime.datetime(2016, 11, 5, 9, 43, 45, tzinfo=pytz.timezone('US/Pacific'))o beklediğiniz buysa ve görün
MRE

"Sunucu her zaman UTC'de tarih / saat iletmelidir ve istemci bunu kendi yerel tarih / saat / saat dilimine dönüştürmelidir" - hayır, bu evrensel olarak doğru değildir. Bazen müşterinin saat dilimini kullanmak uygun değildir ve verilerin bir parçası olarak uygun saat dilimi iletilmesi gerekir. Bir Londralı olarak, bir San Francisco satranç kulübünün toplantı saatlerini web sitelerinde görürsem, onları Londra zamanında değil, San Francisco zamanında görmeliyim.
Mark Amery

69

Python 3'te standart kütüphane UTC'yi saat dilimi olarak belirtmeyi çok daha kolaylaştırır:

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

Yalnızca standart kitaplığı kullanan ve hem Python 2 hem de Python 3'te çalışan bir çözüm istiyorsanız, jfs'nin cevabına bakın .

UTC'ye değil yerel saat dilimine ihtiyacınız varsa, Mihai Capotă'nın cevabına bakın.


19

Hem Python 2 hem de 3'te çalışan bir stdlib çözümü:

from datetime import datetime

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

nerede todayUTC gün (gece yarısı) başlangıcını temsil eden bir farkında datetime örneğidir ve utcbir tzinfo nesnesi (olan belgelere örnek ):

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

İlgili: Belirli bir UTC saati için gece yarısı (günün başlangıcı) almanın çeşitli yollarının performans karşılaştırması . Not: sabit olmayan bir UTC ofsetine sahip bir saat dilimi için gece yarısı almak daha karmaşıktır .


16

Geçerli saati temsil eden saat dilimi farkında datetime nesnesi oluşturmak için başka bir yöntem:

import datetime
import pytz

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

unutmayın pytz.utcve pytz.UTCher ikisi de tanımlanmıştır (ve aynıdır)
drevicko

2
Bu cevap kabul edilen yanıttan daha iyidir, çünkü daha evrenseldir: replace()zaman dilimi genellikle diğer birçok kullanımda hataya açıkken localize(), ing, naif zaman damgalarına zaman dilimi atamanın tercih edilen yoludur.
Antony Hatchkins

@AntonyHatchkins: .localize()yöntem belirsiz yerel zamanlar için başarısız oluyor (utc olmayan girdi). @ philfreo'nun kullandığı yanıt bu.now(pytz_timezone) gibi durumlarda çalışmaya devam eder.
jfs

Python belgelerinde belirtildiği gibi .now(pytz_timezone), tam olarak aynı şeyi yapar localize(utcnow)- önce UTC'de geçerli saati oluşturur, sonra ona bir saat dilimi atar: "<...> Bu durumda sonuç eşdeğerdir tz.fromutc(datetime.utcnow().replace(tzinfo=tz))". Her iki cevap da doğrudur ve her zaman çalışır.
Antony Hatchkins

1
Güvenli bir şekilde saat dilimi farkına varılabilen tek naif (utc olmayan) zaman şu anda : altta yatan sistemin UTC değerini bilmesi ve pytzOLSON db aracılığıyla onu dünyanın herhangi bir saat dilimine nasıl dönüştüreceğini bilmesi gerekiyor. Gün ışığından yararlanma vardiyaları sırasındaki belirsizlik nedeniyle başka naif (utc olmayan) zaman dilimini fark etmek zordur. Bu bir problem değildir .localize(bir is_dstdeğeri beslemek , herhangi bir tarih için çalışmasını sağlar). Bu gün ışığından yararlanma pratiğinin doğasında var olan bir problemdir.
Antony Hatchkins

16

Yalnızca standart kitaplığı kullanan bir astar, Python 3.3'ten başlayarak çalışır. ( Johnchen902 tarafından önerildiği gibi ) datetimekullanarak yerel bir saat dilimi farkında nesne alabilirsiniz :astimezone

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
Belgeler burada bulunan büyük bir yardımdır : docs.python.org/3.8/library/… . Bu inanılmaz derecede temel işlevsellik, dokümanlardaki belirsiz bir paragrafın derinliklerine gömülür, böylece bu yığın akışı cevabı, bu bilgi ile tüm internette etkili bir şekilde tek yerdir. Dokümantasyonda, Python 3.6'dan beri datetime.now()herhangi bir argüman olmadan çağrılabileceği ve doğru yerel sonucu döndürebileceği de görülebilir (naiflerin datetimeyerel saat diliminde olduğu varsayılır).
atimholt

8

Django kullanıyorsanız , tz farkında olmayan tarihleri ​​ayarlayabilirsiniz (yalnızca UTC ).

Comment settings.py aşağıdaki satırı:

USE_TZ = True

8
bu soruda django'nun nerede olduğunu gördün?
vonPetrushev

1
Bir tür ruh önceki yorum özürümü burada sildi, yine: utanç verici, yanlış cevap, çünkü soru Django'ya özgü değil. Yine de bazı kullanıcılara yardımcı olabileceği için bıraktım, ancak puan 0'a yaklaştığında silerim. Bu cevap uygun değilse, aşağıya çekinmeyin.
laffuste

6

pytz , Python 2.3 veya üstünü kullanarak doğru ve platformlar arası saat dilimi hesaplamalarına izin veren bir Python kütüphanesidir.

Stdlib ile bu mümkün değildir.

Benzer bir soruyu SO .


6

İşte stdlib ile oluşturmanın bir yolu:

import time
from datetime import datetime

FORMAT='%Y-%m-%dT%H:%M:%S%z'
date=datetime.strptime(time.strftime(FORMAT, time.localtime()),FORMAT)

tarih yerel tarih depolar ve UTC offset tanımlamak gerekirse bu çözümü kullanabilmeniz için, UTC saat dilimine değil tarih tarih üretilen hangi zaman dilimi . Bu örnekte ve yerel saat dilimimde:

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

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

Anahtar, %zoluşturulan zaman yapısının UTC ofsetini belirtmek için yönerge FORMAT temsiline eklemektir . Diğer gösterim formatlarına datetime modül belgelerinde bakılabilir

Eğer UTC saat dilimine tarihini gerekiyorsa, yerine geçebilir time.localtime () ile 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'

Düzenle

Bu sadece python3 üzerinde çalışır . Z yönergesi python 2 _strptime.py kodunda mevcut değildir


ValueError: 'z', '% Y-% m-% dT% H:% M:% S% z' biçiminde bozuk bir yönerge
jno

Python 2 kullanıyorsunuz, değil mi? Ne yazık ki, z yönergesi python 2'de mevcut değil gibi görünüyor. _Strptime.py kodu
jcazor

6

Dateutil'i , saat dilimi farkında olan Python datetime.datetime.now () bölümünde açıklandığı gibi kullanın :

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

1
Bunun yanlış sonuç verdiği bir durum için JF Sebastian'ın bu cevabına bakınız .
Antony Hatchkins

2
Diğer yazıdaki hatanın sadece belirli kullanım durumlarında alakalı olduğunu düşünüyorum. tzlocal()Fonksiyonu hala en basit çözümlerden biridir ve kesinlikle burada belirtilmelidir.
user8162

2

utcTarih çıkarmanın çalışabilmesi için saat diliminde saat dilimine duyarlı bir tarih elde etmek yeterlidir.

Ancak, geçerli saat diliminizde saat dilimine uygun bir tarih istiyorsanız tzlocal, gitmenin yolu şudur:

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

PS'nin dateutilbenzer bir işlevi vardır ( dateutil.tz.tzlocal). Ancak, adı paylaşmaya rağmen, JF Sebastian tarafından belirtildiği gibi tamamen farklı bir kod tabanına sahiptir .


python genellikle sunucuda kullanılır. Bir sunucudaki yerel saat dilimi genellikle anlamsızdır ve her zaman UTC olarak ayarlanmalıdır. Datetime tzinfo'nun bu şekilde ayarlanması bazı durumlarda başarısız olur. UTC'yi daha iyi kullanın, ardından yalnızca çıktıda istenen saat dilimine yerelleştirin. Örneğin, herhangi bir zaman çizelgesi hesaplaması gün ışığından tasarruf etmeyi dikkate almaz, bu nedenle bunlar UTC'de yapılmalı, ardından yerelleştirilmelidir.
MrE

@MrE Yanlış, offtopik, örnekler?
Antony Hatchkins

gün ışığından yararlanma durumunu gözlemleyen bir saat diliminde yerelleştirilmiş bir datetime nesnesi kullanmayı deneyin, gün ışığından yararlanma durumunu değiştirmek için birkaç gün ekleyin ve yerelleştirilmiş saat diliminde datetime nesneleri üzerinde çalışmanın başarısız olduğunu ve gün ışığından tasarruf etmediğini göreceksiniz. Bu nedenle, HER ZAMAN UTC saatinde herhangi bir tarih / saat işlemi yapmanız gerektiği yorumum.
MrE

nokta şu: bunu yapmayın, UTC'de işlemlerinizi yapın ve sonra çıktıdaki yerel saat alanına dönüştürmek için datetime.astimezone (saat dilimi) kullanın.
MrE

2

İşte okunabilir bir saat dilimi kullanan ve bugün () ile çalışan bir çözüm:

from pytz import timezone

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

Tüm zaman dilimlerini aşağıdaki gibi listeleyebilirsiniz:

import pytz

pytz.all_timezones
pytz.common_timezones # or

1

Özellikle UTC dışı zaman dilimleri için:

Kendi yöntemine sahip tek saat dilimi timezone.utc, ancak bir saat dilimini herhangi bir UTC ofsetiyle ve timedelta& timezonetuşlarını kullanarak ve kullanarak zorlayarak geçebilirsiniz .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'

timezone(timedelta(hours=n))Saat dilimi olarak kullanmak , burada gerçek gümüş mermidir ve başka birçok yararlı uygulamaya sahiptir.


0

Python'da geçerli saat ve tarihi alırsanız, tarih ve saati içe aktarırsanız, python'daki pytz paketi geçerli tarih ve saati aşağıdaki gibi alırsınız.

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

0

Başka bir alternatif aklıma daha iyi bir tek yer, kullandığı Pendulumyerine pytz. Aşağıdaki basit kodu göz önünde bulundurun:

>>> import pendulum

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

Sarkaç'ı kurmak ve belgelerine bakmak için buraya gidin . Tonlarca seçenek (basit ISO8601, RFC3339 ve diğerleri format desteği gibi), daha iyi performansa sahiptir ve daha basit kod verme eğilimindedir.


Bu oylama neden burada emin değilim, bu kod benim için 7/24 çalıştıran birden fazla programda çalışıyor :). başka bir fikre aldırmadım, ama lütfen kontrol etmeme izin vermek için neden sizin için çalışmadığını söyleyin. Şimdiden teşekkürler
ng10

0

Saat dilimine duyarlı bir tarih saati için saat dilimini aşağıda gösterildiği gibi kullanın. Varsayılan UTC'dir:

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

0

'Howchoo' dan Tyler, Datetime Objects hakkında daha iyi bir fikir edinmeme yardımcı olan gerçekten harika bir makale yaptı, aşağıdaki bağlantı

Tarihle Çalışma

aslında, her iki datetime nesnemin sonuna aşağıdakileri ekledim

.replace(tzinfo=pytz.utc)

Misal:

import pytz
import datetime from datetime

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

0

pnp_datetime komutunu deneyin , her zaman kullanılan ve döndürülen saat dilimi içindedir ve ofset-naif ve ofset-farkında herhangi bir soruna neden olmaz.

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

0

Python 3.6'dan beri, yerel saati (işletim sisteminizin ayarı) temsil eden bir saat dilimi farkında tarih-saat nesnesi elde etmek için sadece standart lib'e ihtiyacınız olduğu vurgulanmalıdır. Astimezone () kullanma

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

(@ johnchen902'nin yorumuna bakınız).

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.