from .dates_add_days import epoch_day_from_iso def _is_digit(ch: str) -> bool: # A character range, not str.isdigit(), which accepts other scripts' digits. return "0" <= ch <= "9" def iso_to_unix(timestamp: str) -> int: """An RFC 3339 date-time to Unix seconds, parsed by position. datetime.fromisoformat accepts more shapes than this (and fewer before 3.11), so the rules are written out to match the other languages. """ if not isinstance(timestamp, str): raise ValueError('"%s" is not an RFC 3339 timestamp such as 2026-09-26T12:00:00Z' % (timestamp,)) t = timestamp def bad() -> ValueError: return ValueError('"%s" is not an RFC 3339 timestamp such as 2026-09-26T12:00:00Z' % (t,)) if len(t) < 20: raise bad() for i in (0, 1, 2, 3, 5, 6, 8, 9, 11, 12, 14, 15, 17, 18): if not _is_digit(t[i]): raise bad() if t[4] != "-" or t[7] != "-" or t[10] not in ("T", "t") or t[13] != ":" or t[16] != ":": raise bad() i = 19 if t[i] == ".": i += 1 start = i while i < len(t) and _is_digit(t[i]): i += 1 if i == start: raise bad() if i < len(t) and t[i] in ("Z", "z") and i + 1 == len(t): offset = 0 elif ( i < len(t) and t[i] in ("+", "-") and i + 6 == len(t) and _is_digit(t[i + 1]) and _is_digit(t[i + 2]) and t[i + 3] == ":" and _is_digit(t[i + 4]) and _is_digit(t[i + 5]) ): oh = int(t[i + 1 : i + 3]) om = int(t[i + 4 : i + 6]) if oh > 23 or om > 59: raise bad() offset = (1 if t[i] == "+" else -1) * (oh * 3600 + om * 60) else: raise bad() h, m, s = int(t[11:13]), int(t[14:16]), int(t[17:19]) if h > 23 or m > 59 or s > 59: raise bad() return epoch_day_from_iso(t[0:10]) * 86400 + h * 3600 + m * 60 + s - offset