Ofset-naif ve ofset-farkında tarihler çıkarılamıyor


305

timestamptzPostgreSQL'de saat diliminde bir alanım var . Tablodan veri çektiğimde, şu anda zamanını çıkarmak istiyorum, böylece yaşını alabilirim.

Yaşadığım sorun her ikisi de datetime.datetime.now()ve datetime.datetime.utcnow()bana bu hatayı alıyorum sonuçlanan zaman dilimi habersiz zaman damgaları döndürmek gibi görünüyor:

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

Bundan kaçınmanın bir yolu var mı (tercihen üçüncü taraf bir modül kullanılmadan).

DÜZENLEME: Öneriler için teşekkürler, ancak saat dilimini ayarlamaya çalışmak bana hata veriyor gibi görünüyor .. bu yüzden ben sadece PG'de saat dilimi farkında olmayan zaman damgalarını kullanacağım ve her zaman kullanarak ekleyin:

NOW() AT TIME ZONE 'UTC'

Bu şekilde tüm zaman damgalarım varsayılan olarak UTC'dir (bunu yapmak daha can sıkıcı olsa da).

Yanıtlar:


316

saat dilimi farkındalığını kaldırmaya çalıştınız mı?

dan http://pytz.sourceforge.net/

naive = dt.replace(tzinfo=None)

saat dilimi dönüşümü de eklemek zorunda kalabilir.

edit: Lütfen bu cevabın yaşını unutmayın. Bir Python 3 cevabı aşağıdadır.


32
Bunu yapmanın tek yolu bu gibi görünüyor. Python'un zaman dilimleri için böylesine berbat bir desteğe sahip olması, zaman damgalarıyla düzgün bir şekilde çalışmak için bir üçüncü taraf modülüne ihtiyaç duyduğu için oldukça topal görünüyor ..
Ian

33
(Sadece kayıt için) Aslında saat dilimi hakkında bilgi eklemek daha iyi bir fikir olabilir: stackoverflow.com/a/4530166/548696
Tadeck 21:12

7
saf datetime nesneleri doğal olarak belirsizdir ve bu nedenle bunlardan kaçınılmalıdır. Bunun yerine tzinfo eklemek kolaydır
jfs

1
@Kylotan: UTC bu bağlamda bir saat dilimidir (tzinfo sınıfı tarafından temsil edildiği gibi). datetime.timezone.utcVeya 'ya bakın pytz.utc. Örneğin, 1970-01-01 00:00:00belirsizdir ve siz belirsizliği giderecek bir zaman dilimini eklemek zorunda: 1970-01-01 00:00:00 UTC. Görüyorsunuz, yeni bilgiler eklemelisiniz ; zaman damgası kendi başına belirsizdir.
jfs

1
@JFSebastian: Sorun, utcnowsaf bir nesne veya zaman dilimi olmadan bir zaman damgası döndürmemesi gerektiğidir. Dokümanlardan "Zaman içinde yoruma açık olmayan belirli bir anı temsil etmek için bilinçli bir nesne kullanılır". UTC'deki herhangi bir zaman, tanım gereği bu kriteri karşılar.
Kylotan

214

Doğru çözüm, saat dilimi bilgisini eklemektir , örneğin, Python 3'te geçerli saati bilinçli bir tarih / saat nesnesi olarak almak için:

from datetime import datetime, timezone

now = datetime.now(timezone.utc)

Eski Python sürümlerinde, utctzinfo nesnesini kendiniz tanımlayabilirsiniz (datetime belgelerinden örnek):

from datetime import tzinfo, timedelta, datetime

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

sonra:

now = datetime.now(utc)

10
Kabul edilen cevap IMHO'yu savunurken tz'yi kaldırmaktan daha iyidir.
Shautieh


61

Bazı insanlar Django özellikle bu tür bir veritabanı etkileşimi soyut bir arayüz olarak kullandığını biliyorum. Django bunun için kullanılabilecek yardımcı programlar sağlar:

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

Sadece bu tür bir arayüz kullanıyor olsanız bile, temel bir Django ayarları altyapısı kurmanız gerekir (ayarlarda, USE_TZ=Truebilinçli bir tarih almak için dahil etmeniz gerekir ).

Tek başına, bu muhtemelen Django'yu bir arayüz olarak kullanmaya motive edecek kadar yakın değildir, ancak başka birçok avantaj vardır. Öte yandan, Django uygulamanızı (yaptığım gibi) yönettiğiniz için burada tökezlediyseniz, belki de bu yardımcı olur ...


1
USE_TZ=Trueburada bilinçli bir tarih almak için ihtiyacınız var.
jfs

2
Evet - JFSebastian'ın açıkladığı gibi settings.py ayarlarınızı yapmanız gerektiğini belirtmeyi unuttum (sanırım bu bir 'ayarla ve unut' örneğiydi).
adaçayı

Ve bu ayrıca + timedelta(hours=5, minutes=30)IST
ABcDexter

25

Bu çok basit ve anlaşılır bir çözüm
İki satır kod

# First we obtain de timezone info o some datatime variable    

tz_info = your_timezone_aware_variable.tzinfo

# Now we can subtract two variables using the same time zone info
# For instance
# Lets obtain the Now() datetime but for the tz_info we got before

diff = datetime.datetime.now(tz_info)-your_timezone_aware_variable

Sonuç: Tarih- saat değişkenlerinizi aynı zaman bilgisiyle yönetmelisiniz


Yanlış? Yazdığım kod test edildi ve bir django projesinde kullanıyorum. Çok açık ve basit
ePi272314

"yanlış", cevabınızdaki son cümleyi ifade eder: "... eklemeli ... UTC değil" - UTC saat dilimi burada çalışır ve bu nedenle ifade yanlıştır.
jfs

: berrak Kastettiğim olmak diff = datetime.now(timezone.utc) - your_timezone_aware_variable(ve eserleri (a - b)neden açıklama yukarıdaki formül (a - b)bile çalışabilir a.tzinfodeğildir b.tzinfo).
jfs

6

Psycopg2 modülünün kendi saat dilimi tanımları vardır, bu yüzden utcnow etrafında kendi paketleyicimi yazdım:

def pg_utcnow():
    import psycopg2
    return datetime.utcnow().replace(
        tzinfo=psycopg2.tz.FixedOffsetTimezone(offset=0, name=None))

ve sadece pg_utcnowPostgreSQL ile karşılaştırmak için şimdiki zamana ihtiyacınız olduğunda kullanıntimestamptz


Örneğin sıfır utc ofseti döndüren herhangi bir tzinfo nesnesi bunu yapar .
jfs

6

Aynı problemle de karşılaştım. Sonra bir çok araştırmadan sonra bir çözüm buldum.

Sorun, datetime nesnesini model veya formdan aldığımızda ofset farkındadır ve sistem tarafından zamanı alırsak saftır .

Ben ne yaptım şimdiki zaman timezone.now () kullanarak var ve django.utils ithalat zaman dilimi tarafından tarafından zaman dilimi almak ve proje ayarları dosyasında USE_TZ = True koymak .


2

Son derece basit bir çözüm buldum:

import datetime

def calcEpochSec(dt):
    epochZero = datetime.datetime(1970,1,1,tzinfo = dt.tzinfo)
    return (dt - epochZero).total_seconds()

Hem saat dilimine duyarlı hem de saat dilimi naif datetime değerleriyle çalışır. Ayrıca, ek kitaplık veya veritabanı geçici çözümlerine gerek yoktur.


1

Ben buldum timezone.make_aware(datetime.datetime.now())django yararlı (Ben 1.9.1). Ne yazık ki, bir datetimenesneyi ofset farkında hale getiremezsiniz , o timetz()zaman. Buna datetimedayanarak bir tane yapmalı ve karşılaştırmalar yapmalısınız.


1

PostgreSQL'in yaş hesaplamasını yapamamanızın acil bir nedeni var mı? Gibi bir şey

select *, age(timeStampField) as timeStampAge from myTable

2
Evet var .. ama çoğunlukla soruyordum çünkü postgre'deki tüm hesaplamaları yapmak istemiyorum.
Ian

0

Bunun eski olduğunu biliyorum, ama sadece birinin yararlı bulması durumunda çözümümü ekleyeceğimi düşündüm.

Yerel saf tarih saatini bir zaman sunucusundan bilinçli bir tarih saatiyle karşılaştırmak istedim. Temelde farkında datetime nesnesini kullanarak yeni bir naif datetime nesnesi oluşturdum. Biraz hack ve çok hoş görünmüyor ama işi bitiriyor.

import ntplib
import datetime
from datetime import timezone

def utc_to_local(utc_dt):
    return utc_dt.replace(tzinfo=timezone.utc).astimezone(tz=None)    

try:
    ntpt = ntplib.NTPClient()
    response = ntpt.request('pool.ntp.org')
    date = utc_to_local(datetime.datetime.utcfromtimestamp(response.tx_time))
    sysdate = datetime.datetime.now()

... işte geçiştirmek ...

    temp_date = datetime.datetime(int(str(date)[:4]),int(str(date)[5:7]),int(str(date)[8:10]),int(str(date)[11:13]),int(str(date)[14:16]),int(str(date)[17:19]))
    dt_delta = temp_date-sysdate
except Exception:
    print('Something went wrong :-(')

Bilginize, utc_to_local()cevabım gelen bir yerel saati döndürür farkında datetime nesnesi (O Python'un 3.3+ kodudur)
jfs

Kodunuzun ne yapmaya çalıştığı açık değildir. İle değiştirebilirsiniz delta = response.tx_time - time.time().
jfs
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.