a6b3f039d8
Pages: /, /simple, /decimal, /seth, /calendar, /astro, /convert, /timegov Features: Seth Calendar (10×36 + holidays), decimal time, moon phases, astronomy (sun/moon), bidirectional time converter, Seth date display, leap day split cell in calendar grid.
108 lines
3.6 KiB
Python
Executable File
108 lines
3.6 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
import json
|
|
import time
|
|
from datetime import date, datetime, timezone
|
|
from http.server import ThreadingHTTPServer, SimpleHTTPRequestHandler
|
|
from pathlib import Path
|
|
from urllib.parse import urlparse
|
|
|
|
ROOT = Path(__file__).resolve().parent
|
|
CLOCK_CGI_PATH = "/zzz__2fbc6c3300df7e4483acd44c5044098a9fcc61d6.cgi"
|
|
|
|
|
|
def us_dst_dates_for_year(year: int) -> tuple[int, int]:
|
|
march1 = date(year, 3, 1)
|
|
first_sunday_march = 1 + ((6 - march1.weekday()) % 7)
|
|
second_sunday_march = first_sunday_march + 7
|
|
|
|
nov1 = date(year, 11, 1)
|
|
first_sunday_november = 1 + ((6 - nov1.weekday()) % 7)
|
|
|
|
return second_sunday_march, first_sunday_november
|
|
|
|
|
|
class Handler(SimpleHTTPRequestHandler):
|
|
def __init__(self, *args, **kwargs):
|
|
super().__init__(*args, directory=str(ROOT), **kwargs)
|
|
|
|
def do_GET(self):
|
|
parsed = urlparse(self.path)
|
|
|
|
if parsed.path == "/":
|
|
self.send_response(302)
|
|
self.send_header("Location", "/timegov/")
|
|
self.end_headers()
|
|
return
|
|
|
|
if parsed.path in {"/simple", "/simple/"}:
|
|
self.path = "/index.html"
|
|
return super().do_GET()
|
|
|
|
if parsed.path in {"/decimal", "/decimal/"}:
|
|
self.path = "/decimal.html"
|
|
return super().do_GET()
|
|
|
|
if parsed.path in {"/seth", "/seth/"}:
|
|
self.path = "/seth.html"
|
|
return super().do_GET()
|
|
|
|
if parsed.path in {"/calendar", "/calendar/"}:
|
|
self.path = "/calendar.html"
|
|
return super().do_GET()
|
|
|
|
if parsed.path in {"/astro", "/astro/"}:
|
|
self.path = "/astro.html"
|
|
return super().do_GET()
|
|
|
|
if parsed.path in {"/convert", "/convert/"}:
|
|
self.path = "/convert.html"
|
|
return super().do_GET()
|
|
|
|
if parsed.path == "/api/time":
|
|
self.send_response(200)
|
|
self.send_header("Content-Type", "application/json")
|
|
self.send_header("Cache-Control", "no-store")
|
|
self.end_headers()
|
|
payload = {
|
|
"epoch_ms": time.time_ns() // 1_000_000,
|
|
"iso_utc": datetime.now(timezone.utc).isoformat(),
|
|
}
|
|
self.wfile.write(json.dumps(payload).encode("utf-8"))
|
|
return
|
|
|
|
if parsed.path == "/api/timegov/auxdata.xml":
|
|
now = datetime.now(timezone.utc)
|
|
dst_start_day, dst_end_day = us_dst_dates_for_year(now.year)
|
|
xml = (
|
|
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
|
|
"<timeData>"
|
|
f"<currYear>{now.year}</currYear>"
|
|
f"<DstDates>3 {dst_start_day} 11 {dst_end_day}</DstDates>"
|
|
"<LeapDate>2099 12 31</LeapDate>"
|
|
"</timeData>"
|
|
)
|
|
self.send_response(200)
|
|
self.send_header("Content-Type", "application/xml")
|
|
self.send_header("Cache-Control", "no-store")
|
|
self.end_headers()
|
|
self.wfile.write(xml.encode("utf-8"))
|
|
return
|
|
|
|
if parsed.path == CLOCK_CGI_PATH:
|
|
t2 = time.time_ns() // 1_000
|
|
t3 = time.time_ns() // 1_000
|
|
xml = f"<timestamp time2=\"{t2}\" time3=\"{t3}\"/>"
|
|
self.send_response(200)
|
|
self.send_header("Content-Type", "application/xml")
|
|
self.send_header("Cache-Control", "no-store")
|
|
self.end_headers()
|
|
self.wfile.write(xml.encode("utf-8"))
|
|
return
|
|
|
|
return super().do_GET()
|
|
|
|
|
|
if __name__ == "__main__":
|
|
server = ThreadingHTTPServer(("0.0.0.0", 8092), Handler)
|
|
server.serve_forever()
|