app/services/idv/aamva_state_maintenance_window.rb
# frozen_string_literal: true
module Idv
class AamvaStateMaintenanceWindow
# _All_ AAMVA maintenance windows are expressed in 'ET' (LG-14028)
TZ = 'America/New_York'
MAINTENANCE_WINDOWS = {
'CA' => [
# Daily, 4:00 - 5:30 am. ET.
{ cron: '0 4 * * *', duration_minutes: 90 },
# Monday, 1:00 - 1:45 am. ET
{ cron: '0 1 * * Mon', duration_minutes: 45 },
# Monday, 1:00 - 4:30 am. ET on 1st and 3rd Monday of month.
{ cron: '0 1 * * Mon#1', duration_minutes: 3.5 * 60 },
{ cron: '0 1 * * Mon#3', duration_minutes: 3.5 * 60 },
],
'CT' => [
# Daily, 4:00 am. to 6:30 am. ET.
{ cron: '0 4 * * *', duration_minutes: 90 },
# Sunday 6:00 am. to 9:30 am. ET
{ cron: '0 6 * * Mon', duration_minutes: 3.5 * 60 },
],
'DC' => [
# Daily, Midnight to 6 am. ET.
{ cron: '0 0 * * *', duration_minutes: 6 * 60 },
],
'DE' => [
# Daily, Midnight to 5 am. ET.
{ cron: '0 0 * * *', duration_minutes: 5 * 60 },
],
'FL' => [
# Sunday 7:00 am. to 12:00 pm. ET
{ cron: '0 7 * * Sun', duration_minutes: 5 * 60 },
],
'IA' => [
# "Daily system resets, normally at 4:45 am. to 5:15 am ET."
{ cron: '45 4 * * *', duration_minutes: 30 },
],
'IN' => [
# Sunday morning maintenance from 6 am. to 10 am. ET.
{ cron: '0 6 * * Sun', duration_minutes: 4 * 60 },
],
'IL' => [
{ cron: '30 2 * * *', duration_minutes: 2.5 * 60 }, # Daily, 2:30 am. to 5 am. ET.
],
'KY' => [
# Daily maintenance from 2:50 am. to 6:40 am. ET
{ cron: '50 2 * * *', duration_minutes: 230 },
],
'MA' => [
# Daily maintenance from 6 am. to 6:15 am. ET.
{ cron: '0 6 * * *', duration_minutes: 15 },
# Wednesday 7 am. to 7:30 am. ET.
{ cron: '0 7 * * Wed', duration_minutes: 30 },
# Saturday 10:00 pm. to Sunday 10:00 am
{ cron: '0 22 * * Sat', duration_minutes: 12 * 60 },
# First Friday of each month: 12 to 6 am. ET.
{ cron: '0 0 * * Fri#1', duration_minutes: 6 * 60 },
],
'MD' => [
# Daily maintenance from 3 am. to 3:15 am. ET.
{ cron: '0 3 * * *', duration_minutes: 15 },
# Sunday maintenance may occur from 6 am. to 10 am. ET.
{ cron: '0 6 * * Sun', duration_minutes: 4 * 60 },
],
'MI' => [
# Daily maintenance from 9 pm. to 9:15 pm. ET.
{ cron: '0 21 * * *', duration_minutes: 15 },
],
'MO' => [
# Daily maintenance from 2 am. to 4:30 am. ...
{ cron: '0 2 * * *', duration_minutes: 2.5 * 60 },
# ... from 6:30 am to 6:45 am ...
{ cron: '30 6 * * *', duration_minutes: 15 },
# ... and 8:30 am. to 8:35 am ET.
{ cron: '30 8 * * *', duration_minutes: 5 },
# Sundays from 9 am. to 10:30 am. ET...
{ cron: '0 9 * * Sun', duration_minutes: 90 },
# ...and 5 am to 5:45 am ET on 2nd Sunday of month.
{ cron: '0 5 * * Sun#2', duration_minutes: 45 },
],
'NC' => [
# Daily, Midnight to 7:00 am. ET.
{ cron: '0 0 * * *', duration_minutes: 7 * 60 },
# Sundays from 5am. till Noon
{ cron: '0 5 * * Sun', duration_minutes: 7 * 60 },
],
# NM: "Sunday mornings." (not modeling; too vague)
'NY' => [
# Sunday maintenance 8 pm. to 9 pm. ET.
{ cron: '0 20 * * Sun', duration_minutes: 60 },
],
'PA' => [
# Sunday maintenance may occur, often between 5:30 am. & 7:00 am. ET
{ cron: '30 5 * * Sun', duration_minutes: 90 },
],
'SC' => [
# Sunday maintenance from 7:00 pm. to 10:00 pm. ET.
{ cron: '0 19 * * Sun', duration_minutes: 3 * 60 },
],
'TX' => [
# Downtime on weekends between 9 pm ET to 7 am ET.
{ cron: '0 21 * * Sat,Sun', duration_minutes: 10 * 60 },
],
'VA' => [
# Sunday morning maintenance 3:00 am. to 5 am. ET.
{ cron: '0 3 * * Sun', duration_minutes: 120 },
# Daily maintenance from 5 am. to 5:30 am.
{ cron: '0 5 * * *', duration_minutes: 30 },
# "Might not respond for short spells, daily between 7 pm and 8:30 pm." (not modeling this)
],
'VT' => [
# Daily maintenance from midnight to 5 am. ET.
{ cron: '0 0 * * *', duration_minutes: 5 * 60 },
],
'WA' => [
# Maintenance from Saturday 9:45 pm. to Sunday 8:15 am. ET.
{ cron: '45 21 * * Sat', duration_minutes: 10.5 * 60 },
],
'WI' => [
# Downtime on Tuesday – Saturday typically between 3 – 4 am ET.
{ cron: '0 3 * * Tue-Sat', duration_minutes: 60 },
# Downtime on Sunday from 6 – 10 am. ET.
{ cron: '0 6 * * Sun', duration_minutes: 4 * 60 },
],
'WV' => [
# Occasional Sunday maintenance from 6:00 am. to noon ET.
{ cron: '0 6 * * Sun', duration_minutes: 6 * 60 },
],
'WY' => [
# Daily, 2 am. to 5 am. ET.
{ cron: '0 2 * * *', duration_minutes: 3 * 60 },
],
}.freeze
PARSED_MAINTENANCE_WINDOWS = MAINTENANCE_WINDOWS.transform_values do |windows|
Time.use_zone(TZ) do
windows.map do |window|
cron = Fugit.parse_cron(window[:cron])
{ cron: cron, duration_minutes: window[:duration_minutes] }
end
end
end.freeze
class << self
def in_maintenance_window?(state)
Time.use_zone(TZ) do
windows_for_state(state).any? { |window| window.cover?(Time.zone.now) }
end
end
def windows_for_state(state)
Time.use_zone(TZ) do
PARSED_MAINTENANCE_WINDOWS.fetch(state, []).map do |window|
previous = window[:cron].previous_time.to_t
(previous..(previous + window[:duration_minutes].minutes))
end
end
end
end
end
end