Saya ingin mengirim objek datetime.datetime dalam bentuk serial dari Python menggunakan JSON dan de-serialize dalam JavaScript menggunakan JSON. Apa cara terbaik untuk melakukan ini?
Saya ingin mengirim objek datetime.datetime dalam bentuk serial dari Python menggunakan JSON dan de-serialize dalam JavaScript menggunakan JSON. Apa cara terbaik untuk melakukan ini?
Jawaban:
Anda dapat menambahkan parameter 'default' ke json.dumps untuk menangani ini:
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"'
Yaitu ISO 8601 format .
Fungsi pengendali default yang lebih komprehensif:
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))
Pembaruan: Menambahkan output jenis dan juga nilai.
Pembaruan: Juga menangani tanggal
dthandler = lambda obj: obj.isoformat() if isinstance(obj, datetime) else json.JSONEncoder().default(obj)
Untuk proyek lintas bahasa, saya menemukan bahwa string yang berisi tanggal RfC 3339 adalah cara terbaik untuk pergi. Tanggal RfC 3339 terlihat seperti ini:
1985-04-12T23:20:50.52Z
Saya pikir sebagian besar formatnya jelas. Satu-satunya hal yang agak tidak biasa adalah "Z" pada akhirnya. Itu singkatan dari GMT / UTC. Anda juga bisa menambahkan offset zona waktu seperti +02: 00 untuk CEST (Jerman di musim panas). Saya pribadi lebih suka menyimpan semuanya dalam UTC sampai ditampilkan.
Untuk tampilan, perbandingan, dan penyimpanan Anda dapat membiarkannya dalam format string di semua bahasa. Jika Anda memerlukan tanggal perhitungan mudah untuk mengubahnya kembali ke objek tanggal asli dalam sebagian besar bahasa.
Jadi hasilkan JSON seperti ini:
json.dump(datetime.now().strftime('%Y-%m-%dT%H:%M:%SZ'))
Sayangnya, konstruktor Date Javascript tidak menerima string RfC 3339 tetapi ada banyak parser yang tersedia di Internet.
huTools.hujson mencoba menangani masalah penyandian paling umum yang mungkin Anda temui dalam kode Python termasuk objek tanggal / datetime sambil menangani zona waktu dengan benar.
datetime
: datetime.isoformat () dan oleh simplejson
, yang akan membuang datetime
objek sebagai isoformat
string secara default. Tidak perlu strftime
melakukan peretasan manual .
datetime
objek ke isoformat
string. Bagi saya, simplejson.dumps(datetime.now())
hasilTypeError: datetime.datetime(...) is not JSON serializable
json.dumps(datetime.datetime.now().isoformat())
Di sinilah keajaiban terjadi.
Saya sudah mengerjakannya.
Katakanlah Anda memiliki objek datetime Python, d , dibuat dengan datetime.now (). Nilainya adalah:
datetime.datetime(2011, 5, 25, 13, 34, 5, 787000)
Anda bisa membuat cerita bersambung menjadi JSON sebagai string datetime ISO 8601:
import json
json.dumps(d.isoformat())
Objek datetime contoh akan diserialisasi sebagai:
'"2011-05-25T13:34:05.787000"'
Nilai ini, setelah diterima di lapisan Javascript, dapat membuat objek Tanggal:
var d = new Date("2011-05-25T13:34:05.787000");
Pada Javascript 1.8.5, Objek tanggal memiliki metode toJSON, yang mengembalikan string dalam format standar. Untuk membuat serial objek Javascript di atas kembali ke JSON, maka perintahnya adalah:
d.toJSON()
Yang akan memberi Anda:
'2011-05-25T20:34:05.787Z'
String ini, setelah diterima dengan Python, dapat dideserialisasi kembali ke objek datetime:
datetime.strptime('2011-05-25T20:34:05.787Z', '%Y-%m-%dT%H:%M:%S.%fZ')
Ini menghasilkan objek datetime berikut, yang sama dengan yang Anda mulai dan karenanya benar:
datetime.datetime(2011, 5, 25, 20, 34, 5, 787000)
Dengan menggunakan json
, Anda dapat mensubklasifikasikan JSONEncoder dan mengganti metode default () untuk memberikan serializers khusus:
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)
Kemudian, Anda dapat menyebutnya seperti ini:
>>> DateTimeJSONEncoder().encode([datetime.datetime.now()])
'["2010-06-15T14:42:28"]'
obj.isoformat()
. Anda juga dapat menggunakan dumps()
panggilan yang lebih umum , yang menggunakan argumen berguna lainnya (seperti indent
): simplejson.dumps (myobj, cls = JSONEncoder, ...)
Berikut adalah solusi yang cukup lengkap untuk pengkodean dan penguraian ulang objek datetime.datetime dan datetime.date secara rekursif menggunakan json
modul library standar . Ini perlu Python> = 2.6 karena %f
kode format dalam string format datetime.datetime.strptime () hanya didukung sejak saat itu. Untuk dukungan Python 2.5, jatuhkan %f
dan lepaskan mikrodetik dari string tanggal ISO sebelum mencoba mengonversinya, tetapi Anda akan kehilangan presisi mikrodetik, tentu saja. Untuk interoperabilitas dengan string tanggal ISO dari sumber lain, yang mungkin menyertakan nama zona waktu atau offset UTC, Anda juga mungkin perlu menghapus beberapa bagian string tanggal sebelum konversi. Untuk parser lengkap untuk string tanggal ISO (dan banyak format tanggal lainnya) lihat modul dateutil pihak ketiga .
Decoding hanya berfungsi ketika string tanggal ISO adalah nilai dalam notasi objek literal JavaScript atau dalam struktur bersarang di dalam objek. String tanggal ISO, yang merupakan item dari array level atas tidak akan akan diterjemahkan.
Yaitu ini berfungsi:
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'}
Dan ini juga:
>>> 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)}]
Tapi ini tidak berfungsi seperti yang diharapkan:
>>> 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']
Berikut kodenya:
__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))
datetime.datetime.utcnow().isoformat()[:-3]+"Z"
itu akan persis seperti apa yang dihasilkan JSON.stringify () dalam javascript
Jika Anda yakin hanya Javascript yang akan menggunakan JSON, saya lebih suka meneruskan Javascript Date
objek secara langsung.
The ctime()
metode pada datetime
objek akan mengembalikan string bahwa Javascript Tanggal objek bisa mengerti.
import datetime
date = datetime.datetime.today()
json = '{"mydate":new Date("%s")}' % date.ctime()
Javascript akan dengan senang hati menggunakannya sebagai objek literal, dan Anda memiliki objek Date Anda.
.ctime()
adalah cara yang SANGAT buruk untuk menyampaikan informasi waktu, .isoformat()
jauh lebih baik. Apa yang .ctime()
dilakukan adalah membuang zona waktu dan penghematan siang hari seolah-olah tidak ada. Fungsi itu harus dibunuh.
Terlambat dalam permainan ... :)
Solusi yang sangat sederhana adalah dengan menambal standar modul json. Sebagai contoh:
import json
import datetime
json.JSONEncoder.default = lambda self,obj: (obj.isoformat() if isinstance(obj, datetime.datetime) else None)
Sekarang, Anda dapat menggunakan json.dumps () seolah-olah itu selalu mendukung datetime ...
json.dumps({'created':datetime.datetime.now()})
Ini masuk akal jika Anda memerlukan ekstensi ini ke modul json untuk selalu masuk dan ingin tidak mengubah cara Anda atau orang lain menggunakan serialisasi json (baik dalam kode yang ada atau tidak).
Perhatikan bahwa beberapa orang mungkin menganggap menambal perpustakaan dengan cara itu sebagai praktik yang buruk. Perhatian khusus perlu diambil jika Anda mungkin ingin memperpanjang aplikasi Anda dengan lebih dari satu cara - adalah kasus seperti itu, saya sarankan untuk menggunakan solusi oleh ramen atau JT dan pilih ekstensi json yang tepat dalam setiap kasus.
None
. Anda mungkin ingin melempar pengecualian.
Tidak banyak yang ditambahkan ke jawaban wiki komunitas, kecuali stempel waktu !
Javascript menggunakan format berikut:
new Date().toJSON() // "2016-01-08T19:00:00.123Z"
Sisi python (untuk json.dumps
pawang, lihat jawaban lain):
>>> 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'
Jika Anda membiarkan Z keluar, kerangka kerja frontend seperti angular tidak dapat menampilkan tanggal di zona waktu browser-lokal:
> $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"
Saran saya adalah menggunakan perpustakaan. Ada beberapa yang tersedia di pypi.org.
Saya menggunakan ini, ini berfungsi baik: https://pypi.python.org/pypi/asjson
Rupanya format tanggal JSON "JavaScript" yang benar adalah 2012-04-23T18: 25: 43.511Z - UTC dan "Z". Tanpa JavaScript ini akan menggunakan zona waktu lokal browser web saat membuat objek Date () dari string.
Untuk waktu "naif" (apa yang Python sebut waktu tanpa zona waktu dan ini dianggap lokal) di bawah ini akan memaksa zona waktu lokal sehingga kemudian dapat dikonversi dengan benar ke UTC:
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)
mengapa ini sangat sulit.
Untuk konversi tanggal Python ke JavaScript, objek tanggal harus dalam format ISO tertentu, yaitu format ISO atau nomor UNIX. Jika format ISO tidak memiliki beberapa info, maka Anda dapat mengonversi ke nomor Unix dengan Date.parse terlebih dahulu. Selain itu, Date.parse bekerja dengan Bereaksi juga sementara Tanggal baru dapat memicu pengecualian.
Jika Anda memiliki objek DateTime tanpa milidetik, hal-hal berikut perlu dipertimbangkan. :
var unixDate = Date.parse('2016-01-08T19:00:00')
var desiredDate = new Date(unixDate).toLocaleDateString();
Tanggal contoh bisa juga variabel di objek result.data setelah panggilan API.
Untuk opsi untuk menampilkan tanggal dalam format yang diinginkan (mis. Untuk menampilkan hari kerja yang panjang) periksa MDN doc .