bson.json_util datetime кодирует и декодирует лучшие практики

Я пытаюсь кодировать и декодировать объект datetime python, используя утилиты pymongo bson. Какая здесь лучшая практика?

>>> from bson import json_util
>>> import datetime
>>> utcnow = datetime.datetime.utcnow()
>>> x = json_util.dumps({'now': utcnow})
>>> json_util.loads(x)['now'] == utcnow
False

>>> json_util.loads(x)['now'] - utcnow
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: can't subtract offset-naive and offset-aware datetimes

>>> json_util.loads(x)['now'].replace(tzinfo=None) - utcnow
datetime.timedelta(-1, 86399, 999088)

>>> datetime.datetime.utcfromtimestamp(1424297808578 / 1000) == json_util.loads(x)['now'].replace(tzinfo=None)
True

^ Это действительно лучший способ? Или написать свой собственный кодировщик/декодировщик и использовать библиотеку json?


person estobbart    schedule 18.02.2015    source источник


Ответы (1)


Кажется, bson округляется до миллисекунд:

>>> from datetime import datetime
>>> import bson # $ pip install bson
>>> d = datetime.utcnow(); d, abs(d - bson.loads(bson.dumps({'utcnow': d}))['utcnow'].replace(tzinfo=None))

(datetime.datetime(2015, 2, 18, 23, 54, 47, 733092), datetime.timedelta(0, 0, 92))

Это задокументированное поведение:

UTC datetime — int64 — это миллисекунды UTC с эпохи Unix.

Если вам нужны микросекунды: вместо этого вы можете хранить целое число микросекунд с эпохи Unix:

from datetime import datetime

td = utc_dt - datetime(1970, 1, 1) 
micros = td.microseconds + (td.seconds + td.days * 24 * 3600) * 10**6

Чтобы преобразовать микросекунды с эпохи Unix обратно в наивный объект даты и времени, который представляет время UTC:

from datetime import datetime, timedelta

utc_dt = datetime(1970, 1, 1) + timedelta(microseconds=micros)

int64 ("\x12") более чем достаточно для представления времени Unix с микросекундным разрешением (это в любом случае превышает диапазон datetime).

Примечание. Временная метка POSIX «забывает» високосные секунды, например:

import time

tt = time.strptime("2015-07-01 01:59:60", "%Y-%m-%d %H:%M:%S")
ts_leap = time.mktime(tt)
tt = time.strptime("2015-07-01 02:00:00", "%Y-%m-%d %H:%M:%S")
ts_after = time.mktime(tt)
assert ts_leap == ts_after # assuming "right" timezone is not used

Если вы заботитесь о микросекундах; вы должны выяснить, что ваша система делает в високосные секунды.

Время (аппаратные часы, программные таймеры) на обычном компьютер не очень точен, поэтому разрешения в миллисекундах должно быть достаточно во многих случаях, например, если вы используете ntp для синхронизации времени между компьютерами, тогда NTP v3 имеет точность 1-2 мс в локальной сети и 10 мс в глобальной сети.

Хотя иногда вы хотите сохранить цифры во вводе, даже если они неточны.

person jfs    schedule 19.02.2015
comment
Мне ничего не нужно, я хотел бы иметь возможность кодировать; декодировать; сравнить == Верно; - person estobbart; 19.02.2015
comment
@estobbart: вы можете сравнить с миллисекундным разрешением abs(a-b) <= timedelta(microseconds=500) или сохранить микросекунды, как показано в ответе. Это тебе решать. Оба допустимы в разных приложениях. - person jfs; 19.02.2015
comment
Я не возражаю против отрицательного голоса, но был бы признателен за объяснение - person jfs; 20.05.2015