Skip to content

Commit 7bee0a2

Browse files
committed
Initial Version of usp_is_date_in_cron_period
1 parent 2e2bd86 commit 7bee0a2

File tree

1 file changed

+289
-0
lines changed

1 file changed

+289
-0
lines changed
Lines changed: 289 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,289 @@
1+
CREATE OR ALTER PROCEDURE tSQLcron.usp_is_date_in_cron_period
2+
@cron_expression NVARCHAR(100)
3+
/*{second} {minute} {hour} {day} {month} {day-of-week}*/
4+
,@validate_date DATETIME = NULL
5+
,@out_is_cron_true BIT OUTPUT
6+
7+
AS
8+
/*******************************************************************************
9+
---
10+
Name: "[tSQLcron].[usp_is_date_in_cron_period]"
11+
12+
Description: |
13+
This stored procedure can be used to evaluate if a given date is true for
14+
a given cron_expression. It will return true (1) if it is.
15+
16+
WARNING: This is a best effort implementation and has NO validation of
17+
the inputs, not all cron expressions are supported so please
18+
test your expressions.
19+
20+
Parameters: |
21+
- @cron_expression : a valid cron expression (see more in 'Cron Expression')
22+
- @validate_date : optional parameter of the date to validate against the cron expression
23+
defaults to current UTC datetime
24+
- out_is_cron_true : OUTPUT variable that returns true or false
25+
26+
Format : {second} {minute} {hour} {day} {month} {day-of-week*}
27+
Value-Options : * * * * * *
28+
: {0-59} {0-59} {0-23} {1-31} {1-12} {1-7}
29+
: 1,2... 1,2... 1,2... 1,2... 1,2... 1,2...
30+
31+
See more information below
32+
33+
CRON element allowed values:
34+
- second : "{*|0|0,1,2..59|1-59|(1-59|0,1..)/1-59}"
35+
- minute : "{*|0|0,1,2..59|1-59|(1-59|0,1..)/1-59}"
36+
- hour : "{*|0|0,1,2..23|1-23|(1-23|0,1..)/1-23}"
37+
- day : "{*|0|1,2,3..31|1-31|(1-31|1,2..)/1-31}"
38+
- month : "{*|0|1,2,3..12|1-12|(1-12|1,2..)/1-12}"
39+
- day-of-week : "{*|0|1,2,3..7|1-7|1-7/1-7}"
40+
- note*: |
41+
The value of `{day-of-week}` depends on settings of SQL Server.
42+
43+
You can identify what `{day-of-week}` monday is using the below snippet
44+
`SELECT DATEPART(WEEKDAY,'2020-04-13') AS Monday_The_13th_Of_April_2020`
45+
46+
Usually : 1:Sunday,2:Monday,3:Tuesday,4:Wednesday,5:Thursday,6:Friday,7:Saturday
47+
48+
Value-Options : |
49+
Any Or Ignore
50+
`*` means that the section is ignored / is any
51+
52+
Exactly this (Second/Minute/...)
53+
`0` means that the section is run exactly when the (Second/Minute/...) is 0. i.e. When the (Second/Minute/...) is exactly 0
54+
55+
Exactly one of these comma separated (Second/Minute/...)
56+
`1,4,6` means that the section is run exactly when the (Second/Minute/...) is 0. i.e. When the (Second/Minute/...) is exactly 1 or 4 or 6
57+
58+
Is Between this range (Second/Minute/...)
59+
`0-5` means that the section is run within this interval, between 0 and 5
60+
61+
Is Between Ranges and is divisible by (n) (Second/Minute/...)
62+
`0-30/5` means that the section is run within this interval, between 0 and 30 AND is exactly divisible by 5 i.e every 5th (Second/Minute/...) between 0-30
63+
`* /5` means every 5th (Second/Minute/...) [Note Remove the space between * and /]
64+
`0/5` means every 5th (Second/Minute/...) [Same as above]
65+
66+
Sample CRON expressions: |
67+
- always/any-time - N'* * * * * *'
68+
- every 15th minute - N'* 0/15 * * * *' (remove space between * and / since it is close SQL Comment)
69+
- everyday at a 13:10 - N'* 10 13 * * *'
70+
- everyday at the 10th minute between 13H-23H range - N'* 10 13-23 * * *'
71+
72+
- on the 27th of June at anytime - N'* * * 27 6 *'
73+
- on the 27th of June at 13:10 - N'* 10 13 27 6 *'
74+
- on the 27th of June at 13:10:30 - N'30 10 13 27 6 *'
75+
76+
- every monday at 13:10 - N'* 10 13 * * 2'
77+
- every monday,wed,thu at 13:10 - N'* 10 13 * * 2,4,5'
78+
- weekdays (mon-fri) at 13:10 - N'* 10 13 * * 2-6'
79+
- weekdays alternate (mon-fri) at 13:10 - N'* 10 13 * * 2-6/2'
80+
- midnight on weekends - N'* 10 13 * * 2-6/2'
81+
82+
Example: |
83+
DECLARE @out_is_cron_true BIT ;
84+
EXEC tSQLcron.sp_is_date_in_cron_period
85+
@cron_expression = N'* 0/15 * * * *' -- nvarchar(100)
86+
, @validate_date = '2020-01-01 13:15:00' -- datetime
87+
, @out_is_cron_true = @out_is_cron_true OUTPUT -- bit
88+
89+
IF (@out_is_cron_true = 1 )
90+
BEGIN
91+
-- DO SOMETHING
92+
END
93+
94+
...
95+
********************************************************************************/
96+
BEGIN
97+
SET NOCOUNT ON;
98+
99+
DROP TABLE IF EXISTS
100+
#hours
101+
, #minute_second ;
102+
103+
DECLARE
104+
@is_cron_true BIT = 0
105+
, @is_success BIT = 0 ;
106+
107+
DECLARE
108+
@Seconds NVARCHAR(100)
109+
, @Minutes NVARCHAR(100)
110+
, @Hours NVARCHAR(100)
111+
, @DayOfMonth NVARCHAR(100)
112+
, @Month NVARCHAR(100)
113+
, @DayOfWeek NVARCHAR(100) ;
114+
115+
SET @validate_date = ISNULL(@validate_date,GETUTCDATE());
116+
117+
DECLARE
118+
@now_second INT = DATEPART( SECOND, @validate_date)
119+
, @now_minute INT = DATEPART( MINUTE, @validate_date)
120+
, @now_hour INT = DATEPART( HOUR, @validate_date)
121+
, @now_day INT = DATEPART( DAY, @validate_date)
122+
, @now_month INT = DATEPART( MONTH, @validate_date)
123+
, @now_day_of_week INT = DATEPART( WEEKDAY, @validate_date)
124+
, @now_year INT = DATEPART( YEAR, @validate_date) ;
125+
126+
127+
128+
129+
EXEC tSQLcron.usp_write_verbose '::START::' , 'tSQLcron.sp_is_date_in_cron_period';
130+
EXEC tSQLcron.usp_write_verbose '@cron_expression' , @cron_expression;
131+
EXEC tSQLcron.usp_write_verbose '@validate_date' , @validate_date;
132+
133+
134+
BEGIN TRY
135+
SELECT
136+
@Seconds = cron_seconds
137+
, @Minutes = cron_minutes
138+
, @Hours = cron_hours
139+
, @DayOfMonth = cron_day_of_month
140+
, @Month = cron_month
141+
, @DayOfWeek = cron_day_of_week
142+
FROM tSQLcron.fn_parse_cron_expression(@cron_expression);
143+
144+
EXEC tSQLcron.usp_write_verbose ' INFO' , 'fn_parse_cron_expression';
145+
EXEC tSQLcron.usp_write_verbose ' @Seconds' , @Seconds;
146+
EXEC tSQLcron.usp_write_verbose ' @Minutes' , @Minutes;
147+
EXEC tSQLcron.usp_write_verbose ' @Hours' , @Hours;
148+
EXEC tSQLcron.usp_write_verbose ' @DayOfMonth', @DayOfMonth;
149+
EXEC tSQLcron.usp_write_verbose ' @Month' , @Month;
150+
EXEC tSQLcron.usp_write_verbose ' @DayOfWeek' , @DayOfWeek;
151+
152+
EXEC tSQLcron.usp_write_verbose ' INFO' , 'NOW(@validate_date)';
153+
EXEC tSQLcron.usp_write_verbose ' @now_second' , @now_second;
154+
EXEC tSQLcron.usp_write_verbose ' @now_minute' , @now_minute;
155+
EXEC tSQLcron.usp_write_verbose ' @now_hour' , @now_hour;
156+
EXEC tSQLcron.usp_write_verbose ' @now_day' , @now_day;
157+
EXEC tSQLcron.usp_write_verbose ' @now_month' , @now_month;
158+
EXEC tSQLcron.usp_write_verbose ' @now_day_of_week' , @now_day_of_week;
159+
EXEC tSQLcron.usp_write_verbose ' @now_year' , @now_year;
160+
161+
162+
163+
164+
EXEC tSQLcron.usp_write_verbose ' INFO' , 'Check RETURN True if expressions is any time';
165+
IF (
166+
1 = 1
167+
AND @Seconds IN ( N'*', N'0/1', N'*/1' )
168+
AND @Minutes IN ( N'*', N'0/1', N'*/1' )
169+
AND @Hours IN ( N'*', N'0/1', N'*/1' )
170+
AND @DayOfMonth IN ( N'*', N'0/1', N'*/1' )
171+
AND @Month IN ( N'*', N'0/1', N'*/1' )
172+
AND @DayOfWeek IN ( N'*', N'0/1', N'*/1' )
173+
)
174+
BEGIN
175+
EXEC tSQLcron.usp_write_verbose ' INFO' , N'Returning, any time expressions passed';
176+
SELECT @is_cron_true = 1 , @is_success = 1 , @out_is_cron_true = 1;
177+
RETURN @is_success;
178+
END ;
179+
180+
EXEC tSQLcron.usp_write_verbose ' INFO' , 'CREATE #Hours and #minute_second TABLE';
181+
SELECT CAST(value AS INT) AS hours INTO #hours FROM
182+
STRING_SPLIT(N'0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23', ',') ;
183+
184+
SELECT
185+
CAST(value AS INT) AS minutes
186+
, CAST(value AS INT) AS seconds
187+
INTO #minute_second
188+
FROM
189+
STRING_SPLIT(N'0,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', ',') ;
190+
191+
EXEC tSQLcron.usp_write_verbose ' INFO' , 'PROCESSING cron expression';
192+
193+
WITH dates
194+
AS (SELECT DISTINCT
195+
dd.date_key
196+
, dd.calendar_year
197+
, dd.calendar_month
198+
, dd.calendar_day
199+
, dd.day_of_week
200+
FROM
201+
tSQLcron.dim_date dd
202+
CROSS APPLY tSQLcron.fn_parse_cron_expression_element( @DayOfMonth ) cDm
203+
CROSS APPLY tSQLcron.fn_parse_cron_expression_element( @DayOfWeek ) cDw
204+
CROSS APPLY tSQLcron.fn_parse_cron_expression_element( @Month ) cM
205+
WHERE
206+
1 = 1
207+
-- Look at Todays Date
208+
AND date_key = CAST(@validate_date AS DATE)
209+
210+
--
211+
-- DayOfMonth
212+
AND (cDm.is_any = 1
213+
OR dd.calendar_day BETWEEN cDm.start_period AND cDm.end_period
214+
OR dd.calendar_day = cDm.this_period
215+
)
216+
AND dd.calendar_day % ISNULL( cDm.interval_period, 1 ) = 0
217+
--
218+
--
219+
-- DayOfweek
220+
AND (cDw.is_any = 1
221+
OR dd.day_of_week BETWEEN cDw.start_period AND cDw.end_period
222+
OR dd.day_of_week = cDw.this_period
223+
)
224+
AND dd.day_of_week % ISNULL( cDw.interval_period, 1 ) = 0
225+
226+
-- Month
227+
AND (cM.is_any = 1
228+
OR dd.calendar_month BETWEEN cM.start_period AND cM.end_period
229+
OR dd.calendar_month = cM.this_period
230+
)
231+
AND dd.calendar_month % ISNULL( cM.interval_period, 1 ) = 0)
232+
, Times
233+
AS (SELECT
234+
H.hours AS time_hour
235+
, M.minutes AS time_minute
236+
, S.seconds AS time_second
237+
, TIMEFROMPARTS( H.hours, M.minutes, S.seconds, 0, 0 ) AS valid_times
238+
FROM
239+
#hours H
240+
CROSS APPLY #minute_second M
241+
CROSS APPLY #minute_second S
242+
CROSS APPLY tSQLcron.fn_parse_cron_expression_element( @Hours ) cH
243+
CROSS APPLY tSQLcron.fn_parse_cron_expression_element( @Minutes ) cM
244+
CROSS APPLY tSQLcron.fn_parse_cron_expression_element( @Seconds ) cS
245+
WHERE
246+
1 = 1
247+
-- Hours
248+
AND (cH.is_any = 1 OR H.hours BETWEEN cH.start_period AND cH.end_period OR H.hours = cH.this_period)
249+
AND H.hours % ISNULL( cH.interval_period, 1 ) = 0
250+
251+
-- Minutes
252+
AND (cM.is_any = 1 OR M.minutes BETWEEN cM.start_period AND cM.end_period OR M.minutes = cM.this_period)
253+
AND M.minutes % ISNULL( cM.interval_period, 1 ) = 0
254+
255+
-- Seconds
256+
AND (cS.is_any = 1 OR S.seconds BETWEEN cS.start_period AND cS.end_period OR S.seconds = cS.this_period)
257+
AND S.seconds % ISNULL( cS.interval_period, 1 ) = 0)
258+
, time_object
259+
AS (SELECT
260+
DATETIMEFROMPARTS(
261+
d.calendar_year, d.calendar_month, d.calendar_day, t.time_hour, t.time_minute, t.time_second, 0
262+
) AS valid_time
263+
FROM
264+
dates d
265+
CROSS APPLY Times t
266+
WHERE
267+
1 = 1
268+
AND (@Seconds IN ( N'*', N'0/1', N'*/1' ) OR t.time_second = @now_second)
269+
AND (@Minutes IN ( N'*', N'0/1', N'*/1' ) OR t.time_minute = @now_minute)
270+
AND (@Hours IN ( N'*', N'0/1', N'*/1' ) OR t.time_hour = @now_hour)
271+
AND (@DayOfMonth IN ( N'*', N'0/1', N'*/1' ) OR d.calendar_day = @now_day)
272+
AND (@Month IN ( N'*', N'0/1', N'*/1' ) OR d.calendar_month = @now_month)
273+
AND (@DayOfWeek IN ( N'*', N'0/1', N'*/1' ) OR d.day_of_week = @now_day_of_week)
274+
AND (@now_year = d.calendar_year))
275+
SELECT TOP (1) @is_cron_true = 1 FROM time_object
276+
WHERE time_object.valid_time >= @validate_date
277+
OPTION(RECOMPILE) ;
278+
279+
SELECT @is_success = 1 , @out_is_cron_true = @is_cron_true;
280+
END TRY
281+
BEGIN CATCH
282+
SET @is_success = 0;
283+
284+
THROW;
285+
286+
END CATCH;
287+
EXEC tSQLcron.usp_write_verbose '::END::' , 'tSQLcron.sp_is_date_in_cron_period';
288+
RETURN @is_success
289+
END;

0 commit comments

Comments
 (0)