-
-
Notifications
You must be signed in to change notification settings - Fork 485
Expand file tree
/
Copy pathparse_schedule.py
More file actions
96 lines (79 loc) · 2.74 KB
/
parse_schedule.py
File metadata and controls
96 lines (79 loc) · 2.74 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
#!/usr/bin/env python3
import os
import re
from datetime import datetime, timedelta, timezone
from zoneinfo import ZoneInfo
SCHEDULE_TIMEZONE = "America/Chicago"
MAX_SCHEDULE_DAYS = 30
def write_output(key: str, value: str) -> None:
path = os.environ.get("GITHUB_OUTPUT")
if not path:
return
with open(path, "a", encoding="utf-8") as handle:
handle.write(f"{key}={value}\n")
def fail(message: str) -> None:
write_output("valid", "false")
write_output("error", message)
raw = os.environ.get("RAW", "").strip()
if not raw:
fail("Missing schedule time.")
raise SystemExit(0)
normalized = re.sub(r"\s+", " ", raw).strip()
match = re.match(
r"^(?P<date>\d{4}-\d{2}-\d{2})\s+(?P<time>\d{1,2}(:\d{2})?\s*(am|pm)?)\s*(?P<tz>ct|cst|cdt)?$",
normalized,
re.IGNORECASE,
)
if not match:
fail(
"Invalid time format. Use `/schedule-merge YYYY-MM-DD HH:MM CT` or `/schedule-merge YYYY-MM-DD 9am CT`."
)
raise SystemExit(0)
date_part = match.group("date")
time_part = match.group("time").strip().lower()
hour = None
minute = 0
ampm_match = re.match(r"^(?P<h>\d{1,2})(:(?P<m>\d{2}))?(?P<ampm>am|pm)$", time_part.replace(" ", ""))
if ampm_match:
hour = int(ampm_match.group("h"))
minute = int(ampm_match.group("m") or 0)
ampm = ampm_match.group("ampm")
if hour < 1 or hour > 12 or minute > 59:
fail("Invalid time value.")
raise SystemExit(0)
if ampm == "pm" and hour != 12:
hour += 12
if ampm == "am" and hour == 12:
hour = 0
else:
hm_match = re.match(r"^(?P<h>\d{1,2})(:(?P<m>\d{2}))?$", time_part)
if not hm_match:
fail("Invalid time value.")
raise SystemExit(0)
hour = int(hm_match.group("h"))
minute = int(hm_match.group("m") or 0)
if hour > 23 or minute > 59:
fail("Invalid time value.")
raise SystemExit(0)
year, month, day = [int(part) for part in date_part.split("-")]
local_tz = ZoneInfo(SCHEDULE_TIMEZONE)
try:
local_dt = datetime(year, month, day, hour, minute, tzinfo=local_tz)
except ValueError:
fail("Invalid calendar date.")
raise SystemExit(0)
utc_dt = local_dt.astimezone(timezone.utc)
now_utc = datetime.now(timezone.utc)
if utc_dt <= now_utc:
fail("Scheduled time must be in the future.")
raise SystemExit(0)
max_future = now_utc + timedelta(days=MAX_SCHEDULE_DAYS)
if utc_dt > max_future:
fail(f"Scheduled time must be within {MAX_SCHEDULE_DAYS} days from now.")
raise SystemExit(0)
utc_iso = utc_dt.replace(microsecond=0).isoformat().replace("+00:00", "Z")
local_iso = local_dt.replace(microsecond=0).isoformat()
write_output("valid", "true")
write_output("utc", utc_iso)
write_output("local", local_iso)
write_output("display", local_dt.strftime("%Y-%m-%d %I:%M %p CT"))