Python ve JavaScript arasındaki JSON tarih aralığı


393

JSON kullanarak Python serileştirilmiş biçimde datetime.datetime nesnesini göndermek ve JSON kullanarak JavaScript serisini kaldırmak istiyorum . Bunu yapmanın en iyi yolu nedir?


Bir kütüphane kullanmayı mı tercih edersiniz yoksa bunu kendiniz mi kodlamak istiyorsunuz?
guettli

Yanıtlar:


370

Bunu yapmak için json.dumps dosyasına 'default' parametresini ekleyebilirsiniz:

date_handler = lambda obj: (
    obj.isoformat()
    if isinstance(obj, (datetime.datetime, datetime.date))
    else None
)
json.dumps(datetime.datetime.now(), default=date_handler)
'"2010-04-20T20:08:21.634121"'

Hangisi ISO 8601 biçimi.

Daha kapsamlı bir varsayılan işleyici işlevi:

def handler(obj):
    if hasattr(obj, 'isoformat'):
        return obj.isoformat()
    elif isinstance(obj, ...):
        return ...
    else:
        raise TypeError, 'Object of type %s with value of %s is not JSON serializable' % (type(obj), repr(obj))

Güncelleme: Türün yanı sıra değer çıktısı eklendi.
Güncelleme: Ayrıca tarih işlemek


11
Sorun, listede / dict içinde başka nesneler varsa, bu kod onları Yok dönüştürecek olmasıdır.
Tomasz Wysocki

5
json.dumps bunların nasıl dönüştürüleceğini bilemez, ancak istisna baskı altındadır. Ne yazık ki bir satır lambda düzeltme eksiklikleri var. Bilinmeyenler üzerinde bir istisna olmasını tercih ederseniz (ki bu iyi bir fikirdir) yukarıda eklediğim işlevi kullanın.
JT.

9
tam çıktı biçiminde de saat dilimi olmalıdır ... ve isoformat () bu işlevselliği sağlamaz ... bu yüzden geri dönmeden önce dizeye bu bilgileri eklediğinizden emin olmalısınız
Nick Franceschina

3
Bu en iyi yol. Bu neden cevap olarak seçilmedi?
Brendon Crawford

16
Lambda, datetime olmayan türlerde temel uygulamayı çağıracak şekilde uyarlanabilir, bu nedenle gerekirse TypeError yükseltilebilir:dthandler = lambda obj: obj.isoformat() if isinstance(obj, datetime) else json.JSONEncoder().default(obj)
Pascal Bourque

81

Diller arası projeler için, RfC 3339 tarihleri içeren dizelerin en iyi yol olduğunu öğrendim . RfC 3339 tarihi şöyle görünür:

  1985-04-12T23:20:50.52Z

Sanırım formatın çoğu belli. Sadece alışılmadık bir şey sonunda "Z" olabilir. GMT / UTC anlamına gelir. Ayrıca CEST (yaz aylarında Almanya) için +02: 00 gibi bir saat dilimi uzaklığı ekleyebilirsiniz. Kişisel olarak görüntülenene kadar her şeyi UTC'de tutmayı tercih ederim.

Görüntüleme, karşılaştırma ve depolama için tüm dillerde dize biçiminde bırakabilirsiniz. Hesaplamalar için tarihe ihtiyacınız varsa, çoğu dilde bir yerel tarih nesnesine geri dönüştürmek kolaydır.

JSON'u şöyle oluşturun:

  json.dump(datetime.now().strftime('%Y-%m-%dT%H:%M:%SZ'))

Ne yazık ki, Javascript'in Tarih yapıcısı RfC 3339 dizelerini kabul etmiyor, ancak internette birçok ayrıştırıcı var.

huTools.hujson , zaman dilimleri doğru işlenirken tarih / datetime nesneleri de dahil olmak üzere Python kodunda karşılaşabileceğiniz en yaygın kodlama sorunlarını ele almaya çalışır.


17
Bu tarih biçimlendirme mekanizması, hem datetime: datetime.isoformat () tarafından hem de varsayılan olarak nesneleri dize olarak simplejsonatacak olan yerel olarak desteklenir . Manuel saldırıya gerek yok . datetimeisoformatstrftime
jrk

9
@jrk - datetimeNesnelerden isoformatdizgeye otomatik dönüşüm almıyorum . Benim için, simplejson.dumps(datetime.now())verimTypeError: datetime.datetime(...) is not JSON serializable
kostmo

6
json.dumps(datetime.datetime.now().isoformat())sihrin gerçekleştiği yerdir.
jathanizm

2
Simplejson'un güzelliği, karmaşık bir veri yapım varsa, onu ayrıştırıp JSON'a dönüştürmesidir. Her datetime nesnesi için json.dumps (datetime.datetime.now (). İsoformat ()) yapmak zorunda kalırsam, bunu kaybederim. Bunu düzeltmenin bir yolu var mı?
andrewrk

1
superjoe30: bunun nasıl yapılacağı hakkında stackoverflow.com/questions/455580/… adresine bakın
maksimum

67

Ben hallettim.

Diyelim ki bir Python datetime nesnesi var diyelim d DateTime.Now ile oluşturulan, (). Değeri:

datetime.datetime(2011, 5, 25, 13, 34, 5, 787000)

JSON'a ISO 8601 datetime dizesi olarak serileştirebilirsiniz:

import json    
json.dumps(d.isoformat())

Örnek datetime nesnesi şu şekilde serileştirilir:

'"2011-05-25T13:34:05.787000"'

Bu değer, bir kez Javascript katmanında alındığında, bir Date nesnesi oluşturabilir:

var d = new Date("2011-05-25T13:34:05.787000");

Javascript 1.8.5 itibariyle, Date nesnelerinin bir toJSON yöntemi vardır ve bu da standart biçimde bir dize döndürür. Yukarıdaki Javascript nesnesini tekrar JSON'a serileştirmek için komut şu şekilde olur:

d.toJSON()

Hangi size verecek:

'2011-05-25T20:34:05.787Z'

Python'da bir kez alınan bu dize, bir datetime nesnesine geri serileştirilebilir:

datetime.strptime('2011-05-25T20:34:05.787Z', '%Y-%m-%dT%H:%M:%S.%fZ')

Bu, başlangıç ​​tarihiyle aynı olan ve bu nedenle doğru olan aşağıdaki datetime nesnesiyle sonuçlanır:

datetime.datetime(2011, 5, 25, 20, 34, 5, 787000)

50

Kullanarak json, kendi özel serileştiricilerinizi sağlamak için JSONEncoder öğesini alt sınıflandırabilir ve varsayılan () yöntemini geçersiz kılabilirsiniz:

import json
import datetime

class DateTimeJSONEncoder(json.JSONEncoder):
    def default(self, obj):
        if isinstance(obj, datetime.datetime):
            return obj.isoformat()
        else:
            return super(DateTimeJSONEncoder, self).default(obj)

Sonra şöyle diyebilirsiniz:

>>> DateTimeJSONEncoder().encode([datetime.datetime.now()])
'["2010-06-15T14:42:28"]'

7
Küçük geliştirme - kullanım obj.isoformat(). dumps()Diğer yararlı argümanları da içeren daha yaygın çağrıyı da kullanabilirsiniz (like indent): simplejson.dumps (myobj, cls = JSONEncoder, ...)
rcoup

3
Çünkü bu DateTimeJSONEncoder'ın üst yöntemini değil JSONEncoder'ın üst yöntemini çağırır. IE, iki seviye yukarı çıkacaksın.
Brian Arsuaga

30

Standart kütüphane jsonmodülünü kullanarak datetime.datetime ve datetime.date nesnelerini özyinelemeli olarak kodlamak ve çözmek için oldukça eksiksiz bir çözüm . %fDatetime.datetime.strptime () biçim dizesindeki biçim kodu yalnızca o zamandan beri desteklendiğinden, bu Python> = 2.6 gerektirir . Python 2.5 desteği için,%f dönüştürmeye çalışmadan önce ISO tarih dizesinden mikrosaniye ve soyun, ancak elbette mikrosaniye hassasiyetini kaybedersiniz. Bir saat dilimi adı veya UTC ofseti içerebilen diğer kaynaklardan alınan ISO tarih dizeleriyle birlikte çalışabilirlik için, dönüşümden önce tarih dizesinin bazı bölümlerini çıkarmanız gerekebilir. ISO tarih dizeleri (ve diğer birçok tarih biçimi) için tam bir ayrıştırıcı için üçüncü taraf dateutil modülüne bakın.

Kod çözme yalnızca ISO tarih dizeleri bir JavaScript değişmez nesne gösterimindeki veya bir nesne içindeki iç içe yapılardaki değerler olduğunda çalışır. Üst düzey bir dizinin öğeleri olan ISO tarih dizeleri değil deşifre edilmesi.

Yani bu işe yarıyor:

date = datetime.datetime.now()
>>> json = dumps(dict(foo='bar', innerdict=dict(date=date)))
>>> json
'{"innerdict": {"date": "2010-07-15T13:16:38.365579"}, "foo": "bar"}'
>>> loads(json)
{u'innerdict': {u'date': datetime.datetime(2010, 7, 15, 13, 16, 38, 365579)},
u'foo': u'bar'}

Ve bu da:

>>> json = dumps(['foo', 'bar', dict(date=date)])
>>> json
'["foo", "bar", {"date": "2010-07-15T13:16:38.365579"}]'
>>> loads(json)
[u'foo', u'bar', {u'date': datetime.datetime(2010, 7, 15, 13, 16, 38, 365579)}]

Ancak bu beklendiği gibi çalışmaz:

>>> json = dumps(['foo', 'bar', date])
>>> json
'["foo", "bar", "2010-07-15T13:16:38.365579"]'
>>> loads(json)
[u'foo', u'bar', u'2010-07-15T13:16:38.365579']

İşte kod:

__all__ = ['dumps', 'loads']

import datetime

try:
    import json
except ImportError:
    import simplejson as json

class JSONDateTimeEncoder(json.JSONEncoder):
    def default(self, obj):
        if isinstance(obj, (datetime.date, datetime.datetime)):
            return obj.isoformat()
        else:
            return json.JSONEncoder.default(self, obj)

def datetime_decoder(d):
    if isinstance(d, list):
        pairs = enumerate(d)
    elif isinstance(d, dict):
        pairs = d.items()
    result = []
    for k,v in pairs:
        if isinstance(v, basestring):
            try:
                # The %f format code is only supported in Python >= 2.6.
                # For Python <= 2.5 strip off microseconds
                # v = datetime.datetime.strptime(v.rsplit('.', 1)[0],
                #     '%Y-%m-%dT%H:%M:%S')
                v = datetime.datetime.strptime(v, '%Y-%m-%dT%H:%M:%S.%f')
            except ValueError:
                try:
                    v = datetime.datetime.strptime(v, '%Y-%m-%d').date()
                except ValueError:
                    pass
        elif isinstance(v, (dict, list)):
            v = datetime_decoder(v)
        result.append((k, v))
    if isinstance(d, list):
        return [x[1] for x in result]
    elif isinstance(d, dict):
        return dict(result)

def dumps(obj):
    return json.dumps(obj, cls=JSONDateTimeEncoder)

def loads(obj):
    return json.loads(obj, object_hook=datetime_decoder)

if __name__ == '__main__':
    mytimestamp = datetime.datetime.utcnow()
    mydate = datetime.date.today()
    data = dict(
        foo = 42,
        bar = [mytimestamp, mydate],
        date = mydate,
        timestamp = mytimestamp,
        struct = dict(
            date2 = mydate,
            timestamp2 = mytimestamp
        )
    )

    print repr(data)
    jsonstring = dumps(data)
    print jsonstring
    print repr(loads(jsonstring))

Gibi tarih yazdırmak Eğer datetime.datetime.utcnow().isoformat()[:-3]+"Z"tam olarak JSON.stringify () JavaScript ürettiğinin gibi olacak
w00t

24

Yalnızca Javascript'in JSON'u tüketeceğinden eminseniz, Datedoğrudan Javascript nesnelerini iletmeyi tercih ederim .

ctime()Üzerinde yöntem datetimenesneleri JavaScript tarihi nesne anlayabileceği bir dize döndürür.

import datetime
date = datetime.datetime.today()
json = '{"mydate":new Date("%s")}' % date.ctime()

Javascript bunu bir nesne değişmezi olarak mutlu bir şekilde kullanacak ve Date nesnenizi tam olarak yerleşik hale getireceksiniz.


12
Teknik olarak geçerli JSON değil, geçerli bir JavaScript nesnesi değişmezidir. (Prensip uğruna, Content-Type'ı application / json yerine text / javascript olarak ayarlayabilirdim.) Tüketici her zaman ve sonsuza dek sadece bir JavaScript uygulaması olacaksa , evet, bu oldukça zariftir. Kullanırdım.
sistem DURAKLAT

13
.ctime()zaman bilgi geçmek için ÇOK kötü bir yoldur .isoformat(), çok daha iyidir. Ne .ctime()varmış gibi, saat dilimi ve yaz saatini atmak. Bu işlev öldürülmeli.
Evgeny

Yıllar sonra: lütfen bunu asla düşünmeyin. Bu sadece json senin gerçekten değil gerektiğini Javascript değerlendirirseniz çalışır ...
domenukk

11

Oyunun sonlarında ... :)

Çok basit bir çözüm, json modülü varsayılanını düzeltmektir. Örneğin:

import json
import datetime

json.JSONEncoder.default = lambda self,obj: (obj.isoformat() if isinstance(obj, datetime.datetime) else None)

Artık json.dumps () yöntemini her zaman datetime özelliğini destekliyormuş gibi kullanabilirsiniz ...

json.dumps({'created':datetime.datetime.now()})

Bu, json modülünün bu uzantısının her zaman devreye girmesini ve sizin veya başkalarının json serileştirmesini kullanma şeklini (mevcut kodda ya da değil) değiştirmek istemiyorsanız mantıklıdır.

Bazılarının kütüphaneleri bu şekilde yamalamayı kötü uygulama olarak değerlendirebileceğini unutmayın. Başvurunuzu birden fazla şekilde genişletmek istemeniz durumunda özel dikkat gösterilmelidir - böyle bir durumdur, çözümü ramen veya JT ile kullanmanızı ve her durumda uygun json uzantısını seçmenizi öneririm.


6
Bu sessizce serileştirilemeyen nesneleri yiyor ve onları dönüştürüyor None. Bunun yerine bir istisna atmak isteyebilirsiniz.
Blender

6

Zaman damgası hariç, topluluk wiki yanıtına eklenecek çok fazla şey yok !

Javascript aşağıdaki biçimi kullanır:

new Date().toJSON() // "2016-01-08T19:00:00.123Z"

Python tarafı ( json.dumpsişleyici için diğer cevaplara bakın):

>>> from datetime import datetime
>>> d = datetime.strptime('2016-01-08T19:00:00.123Z', '%Y-%m-%dT%H:%M:%S.%fZ')
>>> d
datetime.datetime(2016, 1, 8, 19, 0, 0, 123000)
>>> d.isoformat() + 'Z'
'2016-01-08T19:00:00.123000Z'

Bu Z'yi dışarıda bırakırsanız, açısal gibi ön uç çerçeveleri tarihi tarayıcı-yerel saat diliminde görüntüleyemez:

> $filter('date')('2016-01-08T19:00:00.123000Z', 'yyyy-MM-dd HH:mm:ss')
"2016-01-08 20:00:00"
> $filter('date')('2016-01-08T19:00:00.123000', 'yyyy-MM-dd HH:mm:ss')
"2016-01-08 19:00:00"

4

Python tarafında:

import time, json
from datetime import datetime as dt
your_date = dt.now()
data = json.dumps(time.mktime(your_date.timetuple())*1000)
return data # data send to javascript

Javascript tarafında:

var your_date = new Date(data)

veriler python'dan kaynaklanıyorsa



0

Görünüşe göre "doğru" JSON (iyi JavaScript) tarih biçimi 2012-04-23T18: 25: 43.511Z - UTC ve "Z". Bu JavaScript olmadan, dizeden bir Date () nesnesi oluştururken web tarayıcısının yerel saat dilimini kullanır.

"Saf" bir süre için (Python'un saat dilimi olmadan bir saati çağırdığı ve bunun yerel olduğu varsayıldığı için) aşağıdakiler yerel saat dilimini zorlayarak UTC'ye dönüştürülebilir:

def default(obj):
    if hasattr(obj, "json") and callable(getattr(obj, "json")):
        return obj.json()
    if hasattr(obj, "isoformat") and callable(getattr(obj, "isoformat")):
        # date/time objects
        if not obj.utcoffset():
            # add local timezone to "naive" local time
            # /programming/2720319/python-figure-out-local-timezone
            tzinfo = datetime.now(timezone.utc).astimezone().tzinfo
            obj = obj.replace(tzinfo=tzinfo)
        # convert to UTC
        obj = obj.astimezone(timezone.utc)
        # strip the UTC offset
        obj = obj.replace(tzinfo=None)
        return obj.isoformat() + "Z"
    elif hasattr(obj, "__str__") and callable(getattr(obj, "__str__")):
        return str(obj)
    else:
        print("obj:", obj)
        raise TypeError(obj)

def dump(j, io):
    json.dump(j, io, indent=2, default=default)

bu neden bu kadar zor.


0

Python'dan JavaScript'e tarih dönüşümü için, tarih nesnesinin belirli ISO biçiminde, yani ISO biçiminde veya UNIX numarası olması gerekir. ISO biçiminde bazı bilgiler yoksa, önce Date.parse ile Unix numarasına dönüştürebilirsiniz. Ayrıca Date.parse, React ile de çalışır, ancak yeni Date bir istisnayı tetikleyebilir.

Milisaniye olmadan bir DateTime nesneniz varsa, aşağıdakilerin dikkate alınması gerekir. :

  var unixDate = Date.parse('2016-01-08T19:00:00') 
  var desiredDate = new Date(unixDate).toLocaleDateString();

Örnek tarih, bir API çağrısından sonra sonuç.data nesnesinde eşit bir değişken olabilir.

Tarihi istenen biçimde görüntüleme seçenekleri (örneğin, uzun hafta içi günleri görüntülemek için) MDN belgesine bakın .

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.