Exercise 2 solution import struct def pack_date(year, month, day): if not (1980 <= year <= 2107 and 1 <= month <= 12 and 1 <= day <= 31): raise ValueError("date out of range") return ((year - 1980) << 9) | (month << 5) | day def pack_time(hour, minute, second): if not (0 <= hour <= 23 and 0 <= minute <= 59 and 0 <= second <= 59): raise ValueError("time out of range") return (hour << 11) | (minute << 5) | (second // 2) def unpack_date(value): return (value >> 9) + 1980, (value >> 5) & 0x0F, value & 0x1F def unpack_time(value): return value >> 11, (value >> 5) & 0x3F, (value & 0x1F) * 2 d = pack_date(2026, 11, 2) t = pack_time(14, 30, 45) print(hex(d), hex(t)) print(unpack_date(d), unpack_time(t)) print(struct.pack("", e) print(all(unpack_date(pack_date(y, m, dd)) == (y, m, dd) for y in (1980, 2026, 2107) for m in range(1, 13) for dd in (1, 15, 31))) # 0x5d62 0x73d6 # (2026, 11, 2) (14, 30, 44) # d6 73 62 5d # (14, 30, 44) (14, 30, 44) # year -> date out of range # month -> date out of range # second -> time out of range # True (Run as written; the output matched the comments.) Explanation: Packing shifts each field to its place and ORs them together; unpacking shifts each field back down and masks it to its width. The time stores seconds divided by 2 in five bits, so 45 and 44 both come back as 44: the odd second is lost on the way in, and nothing can restore it. The two 16-bit values are written little-endian, so 0x73d6 appears in the file as d6 73. The day check of 31 is only a range check; it does not know which months are shorter.