Exercise 1 solution from datetime import datetime, date, timezone from zoneinfo import ZoneInfo import json def encode(o): if isinstance(o, datetime): if o.tzinfo is None: raise ValueError("naive datetime: attach a zone first") return o.astimezone(timezone.utc).isoformat(timespec="seconds").replace("+00:00", "Z") if isinstance(o, date): return o.isoformat() raise TypeError(f"cannot encode {type(o).__name__}") def dumps(obj): return json.dumps(obj, default=encode, sort_keys=True) def loads(text, instants=(), days=()): data = json.loads(text) for key in instants: data[key] = datetime.fromisoformat(data[key]) for key in days: data[key] = date.fromisoformat(data[key]) return data order = { "id": 42, "placed": datetime(2026, 11, 2, 9, 30, tzinfo=ZoneInfo("America/New_York")), "ships_on": date(2026, 11, 5), } text = dumps(order) print(text) back = loads(text, instants=("placed",), days=("ships_on",)) print(back["placed"], back["ships_on"]) print(back["placed"] == order["placed"]) print(back["placed"].utcoffset(), order["placed"].utcoffset()) try: dumps({"placed": datetime(2026, 11, 2, 9, 30)}) except ValueError as e: print("ValueError:", e) try: dumps({"x": {1, 2}}) except TypeError as e: print("TypeError:", e) Output (run, and exactly as shown): {"id": 42, "placed": "2026-11-02T14:30:00Z", "ships_on": "2026-11-05"} 2026-11-02 14:30:00+00:00 2026-11-05 True 0:00:00 -1 day, 19:00:00 ValueError: naive datetime: attach a zone first TypeError: cannot encode set Explanation: 09:30 in New York on 2 November 2026 is UTC-5 (daylight saving ended on 1 November), so the instant is 14:30 UTC. The round-tripped value equals the original, because aware datetimes compare by instant, but it no longer carries the New York offset: it comes back as UTC (the last line shows an offset of 0 against -5 hours, which Python prints as -1 day, 19:00:00). If the original local time must be shown, store the zone name separately and convert for display. The encoder tests datetime before date because datetime is a subclass of date, and it rejects naive values at the boundary rather than guessing a zone.