2020# software solely pursuant to the terms of the relevant commercial agreement.
2121
2222
23+ import calendar
24+ import datetime as dt
2325import heapq
2426import io
2527import logging
@@ -84,19 +86,35 @@ def super_len(o):
8486 return None
8587
8688
87- def cratedb_json_encoder (obj : t .Any ) -> str :
89+ epoch_aware = dt .datetime (1970 , 1 , 1 , tzinfo = dt .timezone .utc )
90+ epoch_naive = dt .datetime (1970 , 1 , 1 )
91+
92+
93+ def json_encoder (obj : t .Any ) -> t .Union [int , str ]:
8894 """
8995 Encoder function for orjson, with additional type support.
9096
91- - Python's `Decimal` type.
92- - freezegun's `FakeDatetime` type.
97+ - Python's `Decimal` type will be serialized to `str`.
98+ - Python's `dt.datetime` and `dt.date` types will be
99+ serialized to `int` after converting to milliseconds
100+ since epoch.
93101
94102 https://github.com/ijl/orjson#default
103+ https://cratedb.com/docs/crate/reference/en/latest/general/ddl/data-types.html#type-timestamp
95104 """
96105 if isinstance (obj , Decimal ):
97106 return str (obj )
98- elif hasattr (obj , "isoformat" ):
99- return obj .isoformat ()
107+ if isinstance (obj , dt .datetime ):
108+ if obj .tzinfo is not None :
109+ delta = obj - epoch_aware
110+ else :
111+ delta = obj - epoch_naive
112+ return int (
113+ delta .microseconds / 1000.0
114+ + (delta .seconds + delta .days * 24 * 3600 ) * 1000.0
115+ )
116+ if isinstance (obj , dt .date ):
117+ return calendar .timegm (obj .timetuple ()) * 1000
100118 raise TypeError
101119
102120
@@ -108,8 +126,12 @@ def json_dumps(obj: t.Any) -> bytes:
108126 """
109127 return orjson .dumps (
110128 obj ,
111- default = cratedb_json_encoder ,
112- option = (orjson .OPT_NON_STR_KEYS | orjson .OPT_SERIALIZE_NUMPY ),
129+ default = json_encoder ,
130+ option = (
131+ orjson .OPT_PASSTHROUGH_DATETIME
132+ | orjson .OPT_NON_STR_KEYS
133+ | orjson .OPT_SERIALIZE_NUMPY
134+ ),
113135 )
114136
115137
0 commit comments