I’ve created an SQL user defined function to determine if a given date is a Japanese holiday. It now consists of a monstrosity of functions that call the same inline view for the *n*th time and it does not look very wise. While I know the best real-life solution is to build a calendar table, I wouldn’t dare to scrap all the code lines I have written. I look forward to refactoring ideas. Thanks.
Function isholiday(d) just bundles three functions; each defining three types of Japanese holidays.
DELIMITER //
CREATE FUNCTION isholiday(d date) RETURNS int
BEGIN
DECLARE t int;
CASE WHEN isregularholiday(d) = 1
THEN SET t = 1;
WHEN iscarryoverholiday(d) = 1
THEN SET t = 1;
WHEN isdentholiday(d) = 1
THEN SET t = 1;
ELSE SET t = 0; END CASE;
RETURN t;
END
//
isregularholiday(d) looks like this:
DELIMITER //
CREATE FUNCTION isregularholiday(d date) RETURNS int
BEGIN
DECLARE s int;
DECLARE t int;
SELECT count(*) INTO s
FROM (SELECT holiday_desc
, CASE WHEN SUBSTR(holiday_date, 3, 1) = '-'
THEN CAST(CONCAT_WS('-', YEAR(d), holiday_date) AS DATE)
WHEN holiday_date = 'ATH21'
THEN ATHLETIC_DATE(d)
/*There are six mobile holidays but I shall spare you the other five. */
ELSE NULL END cnvt
FROM holidays
WHERE YEAR(d) BETWEEN valid_from AND valid_to) **base**
WHERE cnvt = d;
CASE s WHEN 1 THEN SET t = 1;
ELSE SET t = 0;
END CASE;
RETURN t;
END
//
Function argument d (or its month-date value) is not directly matched with holiday_date column (cf. cnvt = d) because the exact dates of some holidays are functionally dependent on the year, hence the CASE expression that branches into several functions. The function will be referring to the inline view base all the time.
Now, besides the basic holidays there are two types of holidays and they are fickle. I tentatively call them carryover holidays and dent holidays. A carryover holiday is defined like this:
If a holiday H falls on a Sunday, the first non-holiday weekday is the carryover holiday of H. Since there are a few holidays clamped together, the first non-holiday weekday for H is not necessarily the next day or Monday.
A dent holiday is a weekday between two holidays. The carryover takes precedence over the dent holiday.
Because of mobile holidays both types of holidays are mobile and fickle and have to be calculated for the year. iscarryoverholiday(d) queries base and count the number of rows for a range between d and the Sunday just before d. If the count equals the date difference between d and the last Sunday, d is a carryover holiday.
isdentholiday(d) calls isregularholiday() for d, d-1 and d+1 (expected values for each argument being 0, 1, 1). It also calls iscarryoverholiday() for d (to get 0), so it reads base four times. Ah….
I won’t bother you with any more details. I am just wondering if I can do something smart and define a closest thing to a dynamic view on MySQL.
Thanks YOU for reading this far!
Just in case, holiday as used here excludes Sundays.
I wrote a script called Holiday List. Basically, it is a table UDF that is passed a year as a parameter and it returns a “holiday table” which I can then join against to find holidays. This could give you a start point and some thoughts on how to deal with holidays, and you could add your rules for Japanese holidays to the script…
http://www.sqlservercentral.com/scripts/Date+Manipulation/74302/
If you want the script and can’t get it at the above link, let me know and I’ll e-mail it to you… The script is written for Microsoft SQL, it might take a bit of code changes to work under mySQL, but there is nothing too fancy in the script