J'ai adapté l'excellente liste de formules de vacances de https://www.extendoffice.com/documents/excel/2602-excel-if-date-is-public-holiday.html
Article de vacances
Cellule
Formules
Jour de semaine le plus proche
Année en cours
C2
\=YEAR(NOW())
Jour de l'An
C3
\=DATE(C2,1,1)
\=C3+CHOOSE(WEEKDAY(C3),1,0,0,0,0,0,-1)
Journée Martin Luther King Jr.
C4
\=DATE(C2,1,1)+14+CHOOSE(WEEKDAY(DATE(C2,1,1)),1,0,6,5,4,3,2)
Journée des présidents
C5
\=DATE(C2,2,1)+14+CHOOSE(WEEKDAY(DATE(C2,2,1)),1,0,6,5,4,3,2)
Vendredi saint
C6
\=C7-2
Dimanche de Pâques
C7
\=DATE(C2,3,1)+((MOD(INT(C2 / 100) - INT(INT(C2 / 100) / 4) - INT((8 * INT(C2 / 100) + 13) / 25) + 19 * MOD(C2, 19) + 15, 30) - INT(MOD(INT(C2 / 100) - INT(INT(C2 / 100) / 4) - INT((8 * INT(C2 / 100) + 13) / 25) + 19 * MOD(C2, 19) + 15, 30) / 28) * (1 - INT(MOD(INT(C2 / 100) - INT(INT(C2 / 100) / 4) - INT((8 * INT(C2 / 100) + 13) / 25) + 19 * MOD(C2, 19) + 15, 30) / 28) * INT(29 / (MOD(INT(C2 / 100) - INT(INT(C2 / 100) / 4) - INT((8 * INT(C2 / 100) + 13) / 25) + 19 * MOD(C2, 19) + 15, 30) + 1))) * INT((21 - MOD(C2, 19))) / 11))) - (MOD(C2 + INT(C2 / 4) + (MOD(INT(C2 / 100) - INT(INT(C2 / 100) / 4) - INT((8 * INT(C2 / 100) + 13) / 25) + 19 * MOD(C2, 19) + 15, 30) - INT(MOD(INT(C2 / 100) - INT(INT(C2 / 100) / 4) - INT((8 * INT(C2 / 100) + 13) / 25) + 19 * MOD(C2, 19) + 15, 30) / 28) * (1 - INT(MOD(INT(C2 / 100) - INT(INT(C2 / 100) / 4) - INT((8 * INT(C2 / 100) + 13) / 25) + 19 * MOD(C2, 19) + 15, 30) / 28) * INT(29 / (MOD(INT(C2 / 100) - INT(INT(C2 / 100) / 4) - INT((8 * INT(C2 / 100) + 13) / 25) + 19 * MOD(C2, 19) + 15, 30) + 1))) * INT((21 - MOD(C2, 19))) / 11))) + 2 - INT(C2 / 100) + INT(INT(C2 / 100) / 4), 7)) + 27)
Le jour du souvenir
C8
\=DATE(C2,6,1)-WEEKDAY(DATE(C2,6,6))
Le quinzième anniversaire
C9
\=DATE(C2,6,19)
\=C9+CHOOSE(WEEKDAY(C9),1,0,0,0,0,0,-1)
Jour de l'indépendance
C10
\=DATE(C2,7,4)
\=C10+CHOOSE(WEEKDAY(C10),1,0,0,0,0,0,-1)
Fête du travail
C11
\=DATE(C2,9,1)+CHOOSE(WEEKDAY(DATE(C2,9,1)),1,0,6,5,4,3,2)
Jour de Colomb
C12
\=DATE(C2,10,1)+7+CHOOSE(WEEKDAY(DATE(C2,10,1)),1,0,6,5,4,3,2)
Journée des vétérans
C13
\=DATE(C2,11,11)
\=C13+CHOOSE(WEEKDAY(C13),1,0,0,0,0,0,-1)
Thanksgiving
C14
\=DATE(C2,11,1)+21+CHOOSE(WEEKDAY(DATE(C2,11,1)),4,3,2,1,0,6,5)
Le jour de Noël
C15
\=DATE(C2,12,25)
\=C15+CHOOSE(WEEKDAY(C15),1,0,0,0,0,0,-1)
Une fois que vous avez collé le tableau ci-dessus dans Excel, vous pouvez remplacer les adresses des cellules de la colonne B par la formule ="C"&ROW()
En C# ou VB, vous pouvez remplacer le DATE(...)+ initial par DATE(...).AddDays(...) et implémenter les fonctions Excel comme suit :
// C# Version
DateTime DATE(int year, int month, int day) { return new DateTime(year, month, day); }
int WEEKDAY(DateTime date) { return 1 + (int)date.DayOfWeek; }
int CHOOSE(params int[] list) { return list[0] < list.Length ? list[list[0]] : 0; }
int MOD(int value, int modulus) { return value % modulus; }
int INT(float value) { return (int)value; }
public int EasterOffset(int year)
{
var offset = ((MOD(INT(year / 100) - INT(INT(year / 100) / 4) - INT((8 * INT(year / 100) + 13) / 25)
+ 19 * MOD(year, 19) + 15, 30) - INT(MOD(INT(year / 100) - INT(INT(year / 100) / 4) - INT((8 * INT(year / 100) + 13) / 25)
+ 19 * MOD(year, 19) + 15, 30) / 28) * (1 - INT(MOD(INT(year / 100) - INT(INT(year / 100) / 4) - INT((8 * INT(year / 100) + 13) / 25)
+ 19 * MOD(year, 19) + 15, 30) / 28) * INT(29 / (MOD(INT(year / 100) - INT(INT(year / 100) / 4) - INT((8 * INT(year / 100) + 13) / 25)
+ 19 * MOD(year, 19) + 15, 30) + 1)) * INT((21 - MOD(year, 19)) / 11))) - (MOD(year + INT(year / 4) +
(MOD(INT(year / 100) - INT(INT(year / 100) / 4) - INT((8 * INT(year / 100) + 13) / 25)
+ 19 * MOD(year, 19) + 15, 30) - INT(MOD(INT(year / 100) - INT(INT(year / 100) / 4) - INT((8 * INT(year / 100) + 13) / 25)
+ 19 * MOD(year, 19) + 15, 30) / 28) * (1 - INT(MOD(INT(year / 100) - INT(INT(year / 100) / 4) - INT((8 * INT(year / 100) + 13) / 25)
+ 19 * MOD(year, 19) + 15, 30) / 28) * INT(29 / (MOD(INT(year / 100) - INT(INT(year / 100) / 4) - INT((8 * INT(year / 100) + 13) / 25)
+ 19 * MOD(year, 19) + 15, 30) + 1)) * INT((21 - MOD(year, 19)) / 11))) + 2 - INT(year / 100) + INT(INT(year / 100) / 4), 7)) + 27);
return offset;
}
public Dictionary<string, DateTime> CalculateHolidays(int year = 0)
{
var calc = new Dictionary<string, DateTime>();
year = year > 0 ? year : DateTime.Today.Year;
calc.Add("New Years Day", DATE(year, 1, 1));
calc.Add("Martin Luther King Jr. Day", DATE(year, 1, 1).AddDays(14 + CHOOSE(WEEKDAY(DATE(year, 1, 1)), 1, 0, 6, 5, 4, 3, 2)));
calc.Add("President Day", DATE(year, 2, 1).AddDays(14 + CHOOSE(WEEKDAY(DATE(year, 2, 1)), 1, 0, 6, 5, 4, 3, 2)));
calc.Add("Good Friday", DATE(year, 3, 1).AddDays(EasterOffset(year) - 2));
calc.Add("Easter Sunday", DATE(year, 3, 1).AddDays(EasterOffset(year)));
calc.Add("Memorial Day", DATE(year, 6, 1).AddDays(-WEEKDAY(DATE(year, 6, 6))));
calc.Add("Juneteenth", DATE(year, 6, 19));
calc.Add("Independence Day", DATE(year, 1, 1));
calc.Add("Labor Day", DATE(year, 7, 4));
calc.Add("Columbus Day", DATE(year, 10, 1).AddDays(7 + CHOOSE(WEEKDAY(DATE(year, 10, 1)), 1, 0, 6, 5, 4, 3, 2)));
calc.Add("Veterans Day", DATE(year, 11, 11));
calc.Add("Thanksgiving Day", DATE(year, 11, 1).AddDays(21 + CHOOSE(WEEKDAY(DATE(year, 11, 1)), 4, 3, 2, 1, 0, 6, 5)));
calc.Add("Thanksgiving Friday", DATE(year, 11, 1).AddDays(22 + CHOOSE(WEEKDAY(DATE(year, 11, 1)), 4, 3, 2, 1, 0, 6, 5)));
calc.Add("Christmas Eve", DATE(year, 12, 24));
calc.Add("Christmas Day", DATE(year, 12, 25));
calc.Add("New Years Eve", DATE(year, 12, 31));
return calc;
}
public DateTime NearestWeekday(DateTime date)
{
return date.AddDays(date.DayOfWeek == 0 ? 1 : (date.DayOfWeek == DayOfWeek.Saturday ? -1 : 0));
}
'' VB Version
Public Function vbDATE(year As Integer, month As Integer, day As Integer) As DateTime
vbDATE = (New Date(year, month, day))
End Function
Public Function WEEKDAY(forDate As DateTime) As Integer
WEEKDAY = Int(forDate.DayOfWeek) + 1
End Function
Public Function CHOOSE(list As Integer()) As Integer
CHOOSE = IIf(list(0) < list.Length, list(list(0)), 0)
End Function
Public Function vbMOD(value As Double, modulus As Integer) As Integer
vbMOD = value Mod modulus
End Function
Public Function vbINT(value As Double) As Integer
vbINT = Int(value)
End Function
Public Function EasterOffset(year As Integer) As Integer
EasterOffset = ((vbMOD(vbINT(year / 100) - vbINT(vbINT(year / 100) / 4) - vbINT((8 * vbINT(year / 100) + 13) / 25) + 19 * vbMOD(year, 19) + 15, 30) - vbINT(vbMOD(vbINT(year / 100) - vbINT(vbINT(year / 100) / 4) - vbINT((8 * vbINT(year / 100) + 13) / 25) + 19 * vbMOD(year, 19) + 15, 30) / 28) * (1 - vbINT(vbMOD(vbINT(year / 100) - vbINT(vbINT(year / 100) / 4) - vbINT((8 * vbINT(year / 100) + 13) / 25) + 19 * vbMOD(year, 19) + 15, 30) / 28) * vbINT(29 / (vbMOD(vbINT(year / 100) - vbINT(vbINT(year / 100) / 4) - vbINT((8 * vbINT(year / 100) + 13) / 25) + 19 * vbMOD(year, 19) + 15, 30) + 1)) * vbINT((21 - vbMOD(year, 19)) / 11))) - (vbMOD(year + vbINT(year / 4) + (vbMOD(vbINT(year / 100) - vbINT(vbINT(year / 100) / 4) - vbINT((8 * vbINT(year / 100) + 13) / 25) + 19 * vbMOD(year, 19) + 15, 30) - vbINT(vbMOD(vbINT(year / 100) - vbINT(vbINT(year / 100) / 4) - vbINT((8 * vbINT(year / 100) + 13) / 25) + 19 * vbMOD(year, 19) + 15, 30) / 28) * (1 - vbINT(vbMOD(vbINT(year / 100) - vbINT(vbINT(year / 100) / 4) - vbINT((8 * vbINT(year / 100) + 13) / 25) + 19 * vbMOD(year, 19) + 15, 30) / 28) * vbINT(29 / (vbMOD(vbINT(year / 100) - vbINT(vbINT(year / 100) / 4) - vbINT((8 * vbINT(year / 100) + 13) / 25) + 19 * vbMOD(year, 19) + 15, 30) + 1)) * vbINT((21 - vbMOD(year, 19)) / 11))) + 2 - vbINT(year / 100) + vbINT(vbINT(year / 100) / 4), 7)) + 27)
End Function
Public Function CalcualteHolidays(year As Integer) As Collection
Dim holidays As New Collection
holidays.Add("New Years Day", vbDATE(year, 1, 1))
holidays.Add("Martin Luther King Jr. Day", vbDATE(year, 1, 1).AddDays(14 + CHOOSE({WEEKDAY(vbDATE(year, 1, 1)), 1, 0, 6, 5, 4, 3, 2})))
holidays.Add("Presidents Day", vbDATE(year, 2, 1).AddDays(14 + CHOOSE({WEEKDAY(vbDATE(year, 2, 1)), 1, 0, 6, 5, 4, 3, 2})))
holidays.Add("Good Friday", vbDATE(year, 3, 1).AddDays(EasterOffset(year) - 2))
holidays.Add("Easter Sunday", vbDATE(year, 3, 1).AddDays(EasterOffset(year)))
holidays.Add("Memorial Day", vbDATE(year, 6, 1).AddDays(-WEEKDAY(vbDATE(year, 6, 6))))
holidays.Add("Juneteenth", vbDATE(year, 6, 19))
holidays.Add("Independence Day", vbDATE(year, 7, 4))
holidays.Add("Labor Day", vbDATE(year, 9, 1).AddDays(CHOOSE({WEEKDAY(vbDATE(year, 9, 1)), 1, 0, 6, 5, 4, 3, 2})))
holidays.Add("Columbus Day", vbDATE(year, 10, 1).AddDays(7 + CHOOSE({WEEKDAY(vbDATE(year, 10, 1)), 1, 0, 6, 5, 4, 3, 2})))
holidays.Add("Veterans Day", vbDATE(year, 11, 11))
holidays.Add("Thanksgiving", vbDATE(year, 11, 1).AddDays(21 + CHOOSE({WEEKDAY(vbDATE(year, 11, 1)), 4, 3, 2, 1, 0, 6, 5})))
holidays.Add("Christmas Day", vbDATE(year, 12, 25))
holidays.Add("New Years Eve", vbDATE(year, 12, 31))
CalcualteHolidays = holidays
End Function
Public Function NearestWeekDay(holiday As DateTime) As DateTime
NearestWeekDay = holiday.AddDays(IIf(holiday.DayOfWeek = DayOfWeek.Saturday, -1, IIf(holiday.DayOfWeek = DayOfWeek.Sunday, 1, 0)))
End Function
Dans la logique ci-dessus, j'ai fait de l'année en cours C2 un argument d'une fonction qui renvoie une liste d'objets Vacances. Voici la version JavaScript :
<!DOCTYPE html><html><body>
<h2>JavaScript Holidays</h2>
<input id="Year" value="2022" onkeyup="GetHolidays()" />
<p id="holidays"></p>
<script>
function DATE(y, m, d) { return new Date(y, m - 1, d, 0, 0, 0, 0); }
function WEEKDAY(date) { return 1 + date.getDay(); }
function CHOOSE(list) { return list[0]<list.length ? list[list[0]] : 0; }
function MOD(value, modulus) { return value % modulus; }
function INT(value) { return ~~value; }
function AddDays(date, days) { return new Date(date.getFullYear(), date.getMonth(), date.getDate() + days); }
class Holiday {constructor(name, date) { this.Name = name;
this.Day = ' SunMonTueWedThuFriSat'.substr(WEEKDAY(date)*3,3);
this.Date = date;
this.NearestWeekday = AddDays(date, CHOOSE([WEEKDAY(date),1,0,0,0,0,0,-1]));}}
function GetHolidays(year) {
var y = year ? year : document.getElementById("Year").value;
var holidays = [];
holidays.push(new Holiday("New Years Day", DATE(y,1,1)));
holidays.push(new Holiday("Martin Luther King Jr. Day",
AddDays(DATE(y,1,1), 14 + CHOOSE([WEEKDAY(DATE(y,1,1)),1,0,6,5,4,3,2]))));
holidays.push(new Holiday("Presidents Day",
AddDays(DATE(y,2,1), 14 + CHOOSE([WEEKDAY(DATE(y,2,1)),1,0,6,5,4,3,2]))));
var easterOffset =((MOD(INT(y/100)-INT(INT(y/100)/4)-INT((8*INT(y/100)+13)/25)+
19*MOD(y,19)+15,30)-INT(MOD(INT(y/100)-INT(INT(y/100)/4)-INT((8*INT(y/100)+13)/25)
+19*MOD(y,19)+15,30)/28)*(1-INT(MOD(INT(y/100)-INT(INT(y/100)/4)-INT((8*INT(y/100)+13)/25)
+19*MOD(y,19)+15,30)/28)*INT(29/(MOD(INT(y/100)-INT(INT(y/100)/4)-INT((8*INT(y/100)+13)/25)
+19*MOD(y,19)+15,30)+1))*INT((21-MOD(y,19))/11)))-(MOD(y+INT(y/4)+
(MOD(INT(y/100)-INT(INT(y/100)/4)-INT((8*INT(y/100)+13)/25)
+19*MOD(y,19)+15,30)-INT(MOD(INT(y/100)-INT(INT(y/100)/4)-INT((8*INT(y/100)+13)/25)
+19*MOD(y,19)+15,30)/28)*(1-INT(MOD(INT(y/100)-INT(INT(y/100)/4)-INT((8*INT(y/100)+13)/25)
+19*MOD(y,19)+15,30)/28)*INT(29/(MOD(INT(y/100)-INT(INT(y/100)/4)-INT((8*INT(y/100)+13)/25)
+19*MOD(y,19)+15,30)+1))*INT((21-MOD(y,19))/11)))+2-INT(y/100)+INT(INT(y/100)/4),7))+27);
holidays.push(new Holiday("Good Friday", AddDays(DATE(y,3,1), easterOffset - 2)));
holidays.push(new Holiday("Easter Sunday", AddDays(DATE(y,3,1), easterOffset)));
holidays.push(new Holiday("Memorial Day", AddDays(DATE(y,6,1), -WEEKDAY(DATE(y,6,6)))));
holidays.push(new Holiday("Juneteenth", DATE(y,6,19)));
holidays.push(new Holiday("Independence Day", DATE(y,7,4)));
holidays.push(new Holiday("Labor Day",
AddDays(DATE(y,9,1), CHOOSE([WEEKDAY(DATE(y,9,1)),1,0,6,5,4,3,2]))));
holidays.push(new Holiday("Columbus Day",
AddDays(DATE(y,10,1), 7+CHOOSE([WEEKDAY(DATE(y,10,1)),1,0,6,5,4,3,2]))));
holidays.push(new Holiday("Veterans Day", DATE(y,11,11)));
holidays.push(new Holiday("Thanksgiving Day",
AddDays(DATE(y,11,1), 21+CHOOSE([WEEKDAY(DATE(y,11,1)),4,3,2,1,0,6,5]))));
holidays.push(new Holiday("Christmas", DATE(y,12,25)));
holidays.push(new Holiday("New Years Eve", DATE(y,12,31)));
var html = (JSON.stringify(holidays)).split('},').join('}<br/>\r\n')
.split('T06:00:00.000Z').join('').split('T05:00:00.000Z').join('')
document.getElementById("holidays").innerHTML = html;
}
GetHolidays(2022);
</script>
</body></html>
Enfin, pour les projets de base de données, vous pouvez définir une plage d'années et renvoyer les vacances pour chaque année par une série de jointures en SQL :
WITH Years as (SELECT yr = year(getdate()) - 21 + delta
FROM (SELECT RANK() OVER(ORDER BY T2.x2) AS delta
FROM ((SELECT CAST(REPLACE(SPACE(200), ' ', '<r/>') AS xml).query('.')) AS T1(x1)
CROSS APPLY T1.x1.nodes('r') AS T2(x2))) y),
Easter as (SELECT yr, offset = (((((CONVERT(int, yr / 100)) - CONVERT(int, (CONVERT(int, yr / 100)) / 4) - CONVERT(int, (8 * (CONVERT(int, yr / 100)) + 13) / 25) + (19 * (yr % 19)) + 15) % 30)
- CONVERT(int, (((CONVERT(int, yr / 100)) - CONVERT(int, (CONVERT(int, yr / 100)) / 4) - CONVERT(int, (8 * (CONVERT(int, yr / 100)) + 13) / 25) + (19 * (yr % 19)) + 15) % 30) / 28)
* (1 - CONVERT(int, (((CONVERT(int, yr / 100)) - CONVERT(int, (CONVERT(int, yr / 100)) / 4) - CONVERT(int, (8 * (CONVERT(int, yr / 100)) + 13) / 25) + (19 * (yr % 19)) + 15) % 30) / 28)
* CONVERT(int, 29 / ((((CONVERT(int, yr / 100)) - CONVERT(int, (CONVERT(int, yr / 100)) / 4) - CONVERT(int, (8 * (CONVERT(int, yr / 100)) + 13) / 25) + (19 * (yr % 19)) + 15) % 30) + 1)))
* CONVERT(int, (29 - (yr % 19)) / 11)) - ((yr + CONVERT(int, yr / 4)
+ ((((CONVERT(int, yr / 100)) - CONVERT(int, (CONVERT(int, yr / 100)) / 4) - CONVERT(int, (8 * (CONVERT(int, yr / 100)) + 13) / 25) + (19 * (yr % 19)) + 15) % 30)
- CONVERT(int, (((CONVERT(int, yr / 100)) - CONVERT(int, (CONVERT(int, yr / 100)) / 4) - CONVERT(int, (8 * (CONVERT(int, yr / 100)) + 13) / 25) + (19 * (yr % 19)) + 15) % 30) / 28)
* (1 - CONVERT(int, (((CONVERT(int, yr / 100)) - CONVERT(int, (CONVERT(int, yr / 100)) / 4) - CONVERT(int, (8 * (CONVERT(int, yr / 100)) + 13) / 25) + (19 * (yr % 19)) + 15) % 30) / 28)
* CONVERT(int, 29 / ((((CONVERT(int, yr / 100)) - CONVERT(int, (CONVERT(int, yr / 100)) / 4) - CONVERT(int, (8 * (CONVERT(int, yr / 100)) + 13) / 25) + (19 * (yr % 19)) + 15) % 30) + 1)))
* CONVERT(int, (29 - (yr % 19)) / 11)) + 2 - (CONVERT(int, yr / 100)) + CONVERT(int, (CONVERT(int, yr / 100)) / 4)) % 7) + 27) FROM Years),
Dates as (SELECT HolidayName = 'New Years Day', HolidayDate = DATEFROMPARTS(yr,1,1) FROM Years
UNION
SELECT HolidayName = 'Martin Luther King Jr. Day',
HolidayDate = DATEADD(d, 14 + CHOOSE(datepart(dw, DATEFROMPARTS(yr,1,1)),1,0,6,5,4,3,2), DATEFROMPARTS(yr,1,1))
FROM Years WHERE yr >= 1983
UNION
SELECT HolidayName = 'Presidents Day',
HolidayDate = DATEADD(d, 14 + CHOOSE(datepart(dw, DATEFROMPARTS(yr,2,1)),1,0,6,5,4,3,2), DATEFROMPARTS(yr,2,1))
FROM Years
UNION
SELECT HolidayName = 'Good Friday', HolidayDate = DATEADD(d, offset - 2, DATEFROMPARTS(yr,3,1)) FROM Easter
UNION
SELECT HolidayName = 'Easter', HolidayDate = DATEADD(d, offset, DATEFROMPARTS(yr,3,1)) FROM Easter
UNION
SELECT HolidayName = 'Memorial Day',
HolidayDate = CASE WHEN yr >= 1971 THEN DATEADD(d, -datepart(dw, DATEFROMPARTS(yr,6,6)), DATEFROMPARTS(yr,6,1))
ELSE DATEFROMPARTS(yr,5,30) END
FROM Years
UNION
SELECT HolidayName = 'Juneteenth', HolidayDate = DATEFROMPARTS(yr,6,19) FROM Years WHERE yr >= 2021
UNION
SELECT HolidayName = 'Independence Day', HolidayDate = DATEFROMPARTS(yr,7,4) FROM Years
UNION
SELECT HolidayName = 'Labor Day',
HolidayDate = DATEADD(d, CHOOSE(datepart(dw, DATEFROMPARTS(yr,9,1)),1,0,6,5,4,3,2), DATEFROMPARTS(yr,9,1))
FROM Years WHERE yr >= 1894
UNION
SELECT HolidayName = 'Columbus Day',
HolidayDate = DATEADD(d, 7 + CHOOSE(datepart(dw, DATEFROMPARTS(yr,10,1)),1,0,6,5,4,3,2), DATEFROMPARTS(yr,10,1))
FROM Years
UNION
SELECT HolidayName = 'Veterans Day', HolidayDate = DATEFROMPARTS(yr,11,11) FROM Years WHERE yr >= 1919
UNION
SELECT HolidayName = 'Thanksgiving Day',
HolidayDate = DATEADD(d, 21 + CHOOSE(datepart(dw, DATEFROMPARTS(yr,11,1)),4,3,2,1,0,6,5), DATEFROMPARTS(yr,11,1))
FROM Years
UNION
SELECT HolidayName = 'Thanksgiving Friday',
HolidayDate = DATEADD(d, 22 + CHOOSE(datepart(dw, DATEFROMPARTS(yr,11,1)),4,3,2,1,0,6,5), DATEFROMPARTS(yr,11,1))
FROM Years
UNION
SELECT HolidayName = 'Christmas Eve', HolidayDate = DATEFROMPARTS(yr,12,24) FROM Years
UNION
SELECT HolidayName = 'Christmas Day', HolidayDate = DATEFROMPARTS(yr,12,25) FROM Years
UNION
SELECT HolidayName = 'New Years Eve', HolidayDate = DATEFROMPARTS(yr,12,31) FROM Years)
SELECT [Sort] = RANK() OVER(ORDER BY HolidayDate),
HolidayName, [Day] = SUBSTRING(' SunMonTueWedThuFriSat', datepart(dw, HolidayDate)*3, 3),
HolidayDate, NearestWeekday = DATEADD(d, CHOOSE(datepart(dw, HolidayDate),1,0,0,0,0,0,-1), HolidayDate)
FROM Dates