I have taken the answers given here and come up with the following database structure:
Column Data Type
id int
task_name nvarchar(50)
frequency_type nvarchar(10)
runat_day int
runat_month int
runat_year int
The data looks like this:
id task_name frequency_type runat_day runat_month runat_year
1 Do this Once once 16 2 2018
2 Do this Monthly monthly 31 0 0
3 Do this Yearly yearly 28 2 0
4 Do this Daily daily 0 0 0
5 Do this Weekly weekly 6 0 0
The Query to pull the data back out looks like this:
DECLARE @chosen_date datetime = '2018-02-28'
DECLARE @lastDayOfMonth int = datePart(d,dateadd(s,-1,dateadd(mm, datediff(m,0,getdate())+1,0)))
select task_name
from scheduled_tasks
where (frequency_type = 'once' and runat_day = DATEPART(DAY, @chosen_date) and runat_month = DATEPART(MONTH, @chosen_date) and runat_year = DATEPART(YEAR, @chosen_date))
or frequency_type = 'daily'
or (frequency_type = 'weekly' and runat_day = DATEPART(WEEKDAY,@chosen_date))
or (frequency_type = 'monthly' and runat_day = DATEPART(DAY, @chosen_date))
or (frequency_type = 'monthly' and @lastDayOfMonth = DATEPART(DAY, @chosen_date) and runat_day >= @lastDayOfMonth)
or (frequency_type = 'yearly' and runat_day = DATEPART(DAY, @chosen_date) and runat_month = DATEPART(MONTH, @chosen_date))
So far, all of my use cases have come up correctly, even end of month dates which fall on a day that doesn't exist in the given month is handled correctly (e.g. 31st of every month will still be triggered on the 28th of Feb)
The fields that aren't required for a given frequency_type simply have zero or null in them, and are ignored by the query.
It does not take in to consideration a yearly event that occurs on the 29th of Feb in a leap year, nor does it consider time of day in any way.