#!/usr/bin/lua -- Programm: ntpstat.lua -- Version: 2.0 -- Created on 25/Jun/2026 by jokel local mode = arg[1] -- Hilfe local function show_help() print([[ ntpstat (Lua-Version 2.0) Aufruf: ntpstat -> Vollständige Ausgabe ntpstat last -> Zeitpunkt der letzten Synchronisation ntpstat next -> Zeitpunkt der nächsten Synchronisation ntpstat status -> synchronised / unsynchronised ntpstat tz -> Sommerzeit/Winterzeit anzeigen ntpstat --help -> Diese Hilfe ]]) end if mode == "--help" then show_help() os.exit(0) end -- Shell-Befehl local function cmd(s) local f = io.popen(s) local out = f:read("*a") f:close() return out end local tracking = cmd("chronyc tracking") local sources = cmd("chronyc sources") -- Leap Status local leap = tracking:match("Leap status%s*:%s*(%S+)") or "Unknown" -- Zeitzonen-Offset (lokal - UTC) local function get_tz_offset() local now_local = os.time() local now_utc = os.time(os.date("!*t")) return now_local - now_utc end local tz_offset = get_tz_offset() -- Sommerzeit-Erkennung local function is_summer_time() return os.date("*t").isdst == true end -- Ref time (UTC) local ref_line = tracking:match("Ref time %(UTC%)%s*:%s*(.-)\n") local last_sync_epoch = nil local last_sync_local = "n/a" local age = 0 if ref_line then -- Thu Jun 25 07:13:06 2026 local weekday, month_str, day, hh, mm, ss, year = ref_line:match("(%a+)%s+(%a+)%s+(%d+)%s+(%d+):(%d+):(%d+)%s+(%d+)") if month_str then local months = { Jan=1, Feb=2, Mar=3, Apr=4, May=5, Jun=6, Jul=7, Aug=8, Sep=9, Oct=10, Nov=11, Dec=12 } local month = months[month_str] -- UTC → lokale Zeit korrigieren local t = { year = year, month = month, day = day, hour = hh, min = mm, sec = ss, isdst = false } -- os.time(t) interpretiert t als lokale Zeit → Offset ADDIEREN local local_epoch = os.time(t) last_sync_epoch = local_epoch + tz_offset local now_epoch = os.time() age = now_epoch - last_sync_epoch last_sync_local = os.date("%Y-%m-%d %H:%M:%S", last_sync_epoch) end end -- aktive Quelle finden local active = sources:match("%s*%^%*%s*.-\n") if not active then print("ERROR: keine aktive Quelle gefunden") print(sources) os.exit(1) end -- "^*" entfernen active = active:gsub("%s*%^%*%s*", "") -- Hostname entfernen active = active:gsub("^[^%s]+%s+", "") -- Felder extrahieren local fields = {} for w in active:gmatch("%S+") do table.insert(fields, w) end local poll = tonumber(fields[2]) or 10 local lastrx = tonumber(fields[4]) or 0 local interval = 2 ^ poll local remaining = interval - lastrx if remaining < 0 then remaining = 0 end local now_epoch = os.time() local next_sync_epoch = now_epoch + remaining local next_sync_human = os.date("%Y-%m-%d %H:%M:%S", next_sync_epoch) -- Offset extrahieren local offset = tracking:match("System time%s*:%s*(.-)\n") or "n/a" -- Offset in Sekunden extrahieren local offset_value = offset:match("([%d%.%-]+)%s*seconds") offset_value = tonumber(offset_value) or 0 -- in Millisekunden umrechnen local offset_ms = offset_value * 1000 -- Richtung bestimmen local offset_dir = offset:match("slow") and "langsamer" or "schneller" -- STATUS if mode == "status" then if leap == "Normal" then print("synchronised") else print("unsynchronised") end os.exit(0) end -- TZ if mode == "tz" then if is_summer_time() then print("Sommerzeit aktiv (CEST)") else print("Winterzeit aktiv (CET)") end os.exit(0) end local now = "%.3f ms %s als NTP-Zeit." -- LAST if mode == "last" then print(last_sync_local .. string.format(" Offset " .. now, offset_ms, offset_dir)) os.exit(0) end -- NEXT if mode == "next" then print(next_sync_human) os.exit(0) end -- VOLLAUSGABE if leap == "Normal" then print("synchronised") else print("unsynchronised") end print("Last sync : " .. last_sync_local) print("Age : " .. age .. "s") print("Poll : " .. poll) print("Interval : " .. interval .. "s") print("LastRx : " .. lastrx .. "s") print("Next sync : " .. next_sync_human) print("Remaining : " .. remaining .. "s") print(string.format("Offset s : %.9f Sekunden.", offset_value, offset_dir)) print(string.format("Offset ms : " .. now, offset_ms, offset_dir)) local tz_status = is_summer_time() and "Sommerzeit (CEST)" or "Winterzeit (CET)" print("TZ-Status : " .. tz_status)