Maintenance Window
When working with RDS maintenance operations it may be useful to see if a given date and time are within an instance's configured maintenance window. AWS use a fairly esoteric format to express these windows.
from datetime import datetime, timedelta
import sys
from typing import Dict, Tuple
import unittest
WEEKDAY_NUMBERS: Dict[str, int] = {
"mon": 0,
"tue": 1,
"wed": 2,
"thu": 3,
"fri": 4,
"sat": 5,
"sun": 6,
}
class TestCase(unittest.TestCase):
def test_parse_aws_datetime_range(self):
start, end = parse_aws_datetime_range("sat:04:02-sat:04:32")
assert start == ("sat", (4, 2))
assert end == ("sat", (4, 32))
def test_nearest_start_end_datetimes(self):
assert nearest_start_end_datetimes(
datetime(2021, 12, 25, 6, 0, 0), ("sat", (0, 0)), ("sun", (0, 0))
) == (
datetime(2021, 12, 25, 0, 0, 0),
datetime(2021, 12, 26, 0, 0, 0),
)
def test_nearest_start_end_datetimes_same_day(self):
assert nearest_start_end_datetimes(
datetime(2021, 11, 23, 9, 0, 0), ("sat", (4, 2)), ("sat", (4, 32))
) == (
datetime(2021, 11, 20, 4, 2, 0),
datetime(2021, 11, 20, 4, 32, 0),
)
def test_nearest_start_end_datetimes_across_weeks(self):
assert nearest_start_end_datetimes(
datetime(2021, 11, 23, 9, 0, 0), ("sun", (0, 0)), ("mon", (1, 0))
) == (
datetime(2021, 11, 21, 0, 0, 0),
datetime(2021, 11, 22, 1, 0, 0),
)
def parse_aws_datetime_range(range: str) -> Tuple[Tuple[str, str]]:
start, end = range.split("-", 1)
start_day, start_hour, start_minute = start.split(":")
end_day, end_hour, end_minute = end.split(":")
return (
(start_day, (int(start_hour), int(start_minute))),
(end_day, (int(end_hour), int(end_minute))),
)
def nearest_start_end_datetimes(
time: datetime, start: Tuple[str, Tuple[int, int]], end: Tuple[str, Tuple[int, int]]
) -> Tuple[datetime, datetime]:
start_day, start_time = start
end_day, end_time = end
date_weekday = time.weekday()
start_day_offset = date_weekday - WEEKDAY_NUMBERS[start_day]
if start_day_offset < 0:
start_day_offset += 7
start_datetime = time + timedelta(days=-start_day_offset)
start_datetime = start_datetime.replace(
hour=start_time[0], minute=start_time[1], second=0, microsecond=0
)
end_datetime = start_datetime
while end_datetime.weekday() != WEEKDAY_NUMBERS[end_day]:
end_datetime = start_datetime + timedelta(days=1)
end_datetime = end_datetime.replace(
hour=end_time[0], minute=end_time[1], second=0, microsecond=0
)
return start_datetime, end_datetime
if __name__ == "__main__":
maintenance_window = sys.argv[1]
now = datetime.now()
start, end = parse_aws_datetime_range(maintenance_window)
start_datetime, end_datetime = nearest_start_end_datetimes(now, start, end)
if start_datetime <= now <= end_datetime:
print(f"{now.isoformat()} is between {start_datetime.isoformat()} and {end_datetime.isoformat()}")
sys.exit(0)
else:
print(f"{now.isoformat()} is not between {start_datetime.isoformat()} and {end_datetime.isoformat()}")
sys.exit(1)