1import datetime
2import decimal
3import json
4import uuid
5from typing import Any
6
7from plain.utils.duration import duration_iso_string
8from plain.utils.functional import Promise
9from plain.utils.timezone import is_aware
10
11
12class PlainJSONEncoder(json.JSONEncoder):
13 """
14 JSONEncoder subclass that knows how to encode date/time, decimal types, and
15 UUIDs.
16 """
17
18 def default(self, o: Any) -> Any:
19 # See "Date Time String Format" in the ECMA-262 specification.
20 if isinstance(o, datetime.datetime):
21 r = o.isoformat()
22 if o.microsecond:
23 r = r[:23] + r[26:]
24 if r.endswith("+00:00"):
25 r = r.removesuffix("+00:00") + "Z"
26 return r
27 elif isinstance(o, datetime.date):
28 return o.isoformat()
29 elif isinstance(o, datetime.time):
30 if is_aware(o):
31 raise ValueError("JSON can't represent timezone-aware times.")
32 r = o.isoformat()
33 if o.microsecond:
34 r = r[:12]
35 return r
36 elif isinstance(o, datetime.timedelta):
37 return duration_iso_string(o)
38 elif isinstance(o, decimal.Decimal | uuid.UUID | Promise):
39 return str(o)
40 else:
41 return super().default(o)