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,38 @@ 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.
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.
92101 - freezegun's `FakeDatetime` type.
93102
94103 https://github.com/ijl/orjson#default
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
118+ # Freezegun support. Needed?
119+ # if hasattr(obj, "isoformat"):
120+ # return obj.isoformat() # noqa: ERA001
100121 raise TypeError
101122
102123
@@ -108,8 +129,12 @@ def json_dumps(obj: t.Any) -> bytes:
108129 """
109130 return orjson .dumps (
110131 obj ,
111- default = cratedb_json_encoder ,
112- option = (orjson .OPT_NON_STR_KEYS | orjson .OPT_SERIALIZE_NUMPY ),
132+ default = json_encoder ,
133+ option = (
134+ orjson .OPT_PASSTHROUGH_DATETIME
135+ | orjson .OPT_NON_STR_KEYS
136+ | orjson .OPT_SERIALIZE_NUMPY
137+ ),
113138 )
114139
115140
0 commit comments