# coding=utf-8 import re import shlex import subprocess from datetime import datetime _TZ_REGEX = r'''^([A-z0-9]+\/)?([A-z0-9]+)([+-]\d+)?$''' class ErrorInvalidTimezone(Exception): pass class ErrorInvalidCmdTemplate(Exception): pass class ErrorTimeConfig(Exception): pass def get_system_timezone(cmd: str = "cat /etc/timezone") -> str: """ Rufe die Systemzeitzone mit dem entsprechenden Befehl ab. :return: Systemzeitzone im Unix-Format (z.B. Europe/Berlin) :raise ErrorTimeConfig: wenn der Befehl einen Fehler zurückgibt :raise ErrorInvalidTimezone: wenn die ermittelte Zeitzone ein ungültiges Format hat """ tz = _run_cmd(cmd).strip() if not re.match(_TZ_REGEX, tz): raise ErrorInvalidTimezone(f'Got timezone {tz} with invalid format') return tz def set_system_datetime(date_time: datetime, cmd_tmpl: str = "date -s '%Y-%m-%d %H:%M:%S'"): """ Ändere die Systemzeit mit dem entsprechenden Befehl. Die Anwendung muss hierfür als Root laufen. :param cmd_tmpl: Befehlsvorlage (verwende Python-strftime()-Platzhalter für Datum und Uhrzeit) :param date_time: Einzustellendes Datum und Uhrzeit :raise ErrorTimeConfig: wenn der Konfigurationsbefehl einen Fehler zurückgibt """ cmd_str = date_time.strftime(cmd_tmpl) _run_cmd(cmd_str) def set_system_timezone(tz: str, cmd_tmpl: str = "timedatectl set-timezone '{TZ}'"): """ Ändere die Systemzeitzone mit dem entsprechenden Befehl. Die Anwendung muss hierfür als Root laufen. :param tz: Zeitzone im Unix-Format (z.B. Europe/Berlin) :param cmd_tmpl: Befehlsvorlage (verwende ``{TZ}`` als Platzhalter für die Zeitzone). :raise ErrorInvalidCmdTemplate: wenn die Befehlsvorlage keinen Platzhalter enthält :raise ErrorInvalidTimezone: wenn die eingegebene Zeitzone ein ungültiges Format hat :raise ErrorTimeConfig: wenn der Konfigurationsbefehl einen Fehler zurückgibt """ if '{TZ}' not in cmd_tmpl: raise ErrorInvalidCmdTemplate( 'Command template %s is missing {TZ} placeholder') if not re.match(_TZ_REGEX, tz): raise ErrorInvalidTimezone(f'Timezone {tz} has invalid format') cmd_str = cmd_tmpl.replace('{TZ}', tz) _run_cmd(cmd_str) def _run_cmd(cmd_str: str) -> str: cmd_parts = shlex.split(cmd_str) try: completed = subprocess.run(cmd_parts, check=True, universal_newlines=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) return completed.stdout except subprocess.CalledProcessError as e: raise ErrorTimeConfig( f'Error configuring system time. Ran cmd {e.cmd}. \ Output "{e.stdout}{e.stderr}". Exit code {e.returncode}.') from e