# -*- coding: utf-8 -*- # Copyright 2025 Mike Fährmann # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License version 2 as # published by the Free Software Foundation. """Date/Time utilities""" import sys import time from datetime import datetime, date, timedelta, timezone # noqa F401 EPOCH = datetime(1970, 1, 1) SECOND = timedelta(0, 1) def normalize(dt): # if (o := dt.utcoffset()) is not None: # return dt.replace(tzinfo=None, microsecond=0) - o if dt.tzinfo is not None: return dt.astimezone(timezone.utc).replace(tzinfo=None, microsecond=0) if dt.microsecond: return dt.replace(microsecond=0) return dt def convert(value): """Convert 'value' to a naive UTC datetime object""" if not value: return EPOCH if isinstance(value, datetime): return value if isinstance(value, str): try: if value[-1] == "Z": # compat for Python < 3.11 value = value[:-1] return normalize(datetime.fromisoformat(value)) except Exception: pass return parse_ts(value) def parse(dt_string, format): """Parse 'dt_string' according to 'format'""" try: return normalize(datetime.strptime(dt_string, format)) except Exception: return EPOCH if sys.hexversion < 0x30c0000: # Python <= 3.11 def parse_iso(dt_string): """Parse 'dt_string' as ISO 8601 value""" try: if dt_string[-1] == "Z": # compat for Python < 3.11 dt_string = dt_string[:-1] return normalize(datetime.fromisoformat(dt_string)) except Exception: return EPOCH def parse_compat(dt_string, format): """Parse 'dt_string' as ISO 8601 value using 'format'""" return parse(dt_string, format) from_ts = datetime.utcfromtimestamp now = datetime.utcnow else: # Python >= 3.12 def parse_iso(dt_string): """Parse 'dt_string' as ISO 8601 value""" try: return normalize(datetime.fromisoformat(dt_string)) except Exception: return EPOCH def parse_compat(dt_string, format): """Parse 'dt_string' as ISO 8601 value""" return parse_iso(dt_string) def from_ts(ts=None): """Convert Unix timestamp to naive UTC datetime""" Y, m, d, H, M, S, _, _, _ = time.gmtime(ts) return datetime(Y, m, d, H, M, S) now = from_ts def parse_ts(ts, default=EPOCH): """Create a datetime object from a Unix timestamp""" try: return from_ts(int(ts)) except Exception: return default def to_ts(dt): """Convert naive UTC datetime to Unix timestamp""" return (dt - EPOCH) / SECOND def to_ts_string(dt): """Convert naive UTC datetime to Unix timestamp string""" try: return str((dt - EPOCH) // SECOND) except Exception: return ""