Holidays |
| Description: | |
| This class knows about a several holidays and can return date value representing the day that a holiday falls on for a given year. It can also detemine if a given date is a holiday. 1/27/2000: Added several more holidays to this class. I should now have every major American holiday. 5/4/2001: Fixed the date calculation for Tax Day. 10/22/2004: Added several Jewish holidays. | |
| Code: | |
'--- clsHolidays
Option Explicit
' Hebrew_* Functions by Kees Couprie from
' http://www.geocities.com/couprie/calmath/index.html
'The following properties are exposed:
' Count() As Long
' HolidayName(holiday As holidays) As String
' HolidayInYear(holiday As holidays, InYear As Date) As Date
' IsHoliday(DateQuery As Date) As Boolean
' HolidayNameOfDate(DateQuery As Date) As String
Public Enum Holidays
NewYearsDay
MartinLutherKingDay
GroundhogDay
ValentinesDay
PresidentsDay
SaintPatricksDay
TaxDay
Easter
MothersDay
MemorialDay
FlagDay
FathersDay
IndependenceDay
LaborDay
ColumbusDay
Halloween
ElectionDay
VeteransDay
ThanksgivingDay
ChristmasEve
Christmas
NewYearsEve
RoshHashanah
YomKippur
Sukkot
SheminiAtzeret
SimchatTorah
Chanukkah
TuBShevat
Purim
PesachPassover
LagBOmer
Shavuot
TishaBAv
HolidayCount
End Enum
Private Const Hebrew_c_Gregorian = 1
Private Const Hebrew_c_Julian = 1
Public Property Get Count() As Long
Count = HolidayCount
End Property
Public Property Get HolidayName(holiday As Holidays) As String
Select Case holiday
Case NewYearsDay
HolidayName = "New Year's Day"
Case MartinLutherKingDay
HolidayName = "Martin Luther King Day"
Case PresidentsDay
HolidayName = "President's Day"
Case Easter
HolidayName = "Easter"
Case MemorialDay
HolidayName = "Memorial Day"
Case IndependenceDay
HolidayName = "Independence Day"
Case LaborDay
HolidayName = "Labor Day"
Case ColumbusDay
HolidayName = "Columbus Day"
Case VeteransDay
HolidayName = "Veterans' Day"
Case ThanksgivingDay
HolidayName = "Thanksgiving Day"
Case Christmas
HolidayName = "Christmas"
Case NewYearsEve
HolidayName = "New Years Eve"
Case Halloween
HolidayName = "Halloween"
Case GroundhogDay
HolidayName = "Groundhog Day"
Case ValentinesDay
HolidayName = "Valentine's Day"
Case SaintPatricksDay
HolidayName = "Saint Patrick's Day"
Case TaxDay
HolidayName = "Tax Day"
Case MothersDay
HolidayName = "Mother's Day"
Case FlagDay
HolidayName = "Flag Day"
Case FathersDay
HolidayName = "Father's Day"
Case ElectionDay
HolidayName = "Election Day"
Case ChristmasEve
HolidayName = "Christmas Eve"
Case RoshHashanah
HolidayName = "Rosh Hashanah"
Case YomKippur
HolidayName = "Yom Kippur"
Case Sukkot
HolidayName = "Sukkot"
Case SheminiAtzeret
HolidayName = "Shemini Atzeret"
Case SimchatTorah
HolidayName = "Simchat Torah"
Case Chanukkah
HolidayName = "Chanukkah"
Case TuBShevat
HolidayName = "Tu B'Shevat"
Case Purim
HolidayName = "Purim"
Case PesachPassover
HolidayName = "Pesach (Passover)"
Case LagBOmer
HolidayName = "Lag B'Omer"
Case Shavuot
HolidayName = "Shavu'ot"
Case TishaBAv
HolidayName = "Tisha B'Av"
Case Else
Debug.Assert False
HolidayName = "Error."
End Select
End Property
Public Property Get HolidayInYear(holiday As Holidays, _
InYear As Date) As Date
Select Case holiday
Case NewYearsDay
HolidayInYear = DateSerial(DatePart("yyyy", InYear), 1, 1)
Case MartinLutherKingDay
HolidayInYear = DayOfWeek(InYear, 1, 3, vbMonday)
Case PresidentsDay
HolidayInYear = DayOfWeek(InYear, 2, 3, vbMonday)
Case Easter
HolidayInYear = CalcEaster(InYear)
Case MemorialDay
HolidayInYear = LastDayInMonth(InYear, 5, vbMonday)
Case IndependenceDay
HolidayInYear = DateSerial(DatePart("yyyy", InYear), 7, 4)
Case LaborDay
HolidayInYear = DayOfWeek(InYear, 9, 1, vbMonday)
Case ColumbusDay
HolidayInYear = DayOfWeek(InYear, 10, 2, vbMonday)
Case VeteransDay
HolidayInYear = DateSerial(DatePart("yyyy", InYear), 11, 11)
Case ThanksgivingDay
HolidayInYear = DayOfWeek(InYear, 11, 4, vbThursday)
Case Christmas
HolidayInYear = DateSerial(DatePart("yyyy", InYear), 12, 25)
Case NewYearsEve
HolidayInYear = DateSerial(DatePart("yyyy", InYear), 12, 31)
Case Halloween
HolidayInYear = DateSerial(DatePart("yyyy", InYear), 10, 31)
Case GroundhogDay
HolidayInYear = DateSerial(DatePart("yyyy", InYear), 2, 2)
Case ValentinesDay
HolidayInYear = DateSerial(DatePart("yyyy", InYear), 2, 14)
Case SaintPatricksDay
HolidayInYear = DateSerial(DatePart("yyyy", InYear), 3, 17)
Case TaxDay
HolidayInYear = CalcTaxDay(InYear)
Case MothersDay
HolidayInYear = DayOfWeek(InYear, 5, 2, vbSunday)
Case FlagDay
HolidayInYear = DateSerial(DatePart("yyyy", InYear), 6, 14)
Case FathersDay
HolidayInYear = DayOfWeek(InYear, 6, 3, vbSunday)
Case ElectionDay
HolidayInYear = DayOfWeek(InYear, 11, 1, vbTuesday)
Case ChristmasEve
HolidayInYear = DateSerial(DatePart("yyyy", InYear), 12, 24)
Case RoshHashanah
HolidayInYear = Hebrew_InYear(InYear, 1, 1)
Case YomKippur
HolidayInYear = Hebrew_InYear(InYear, 1, 10)
Case Sukkot
HolidayInYear = Hebrew_InYear(InYear, 1, 15)
Case SheminiAtzeret
HolidayInYear = Hebrew_InYear(InYear, 1, 22)
Case SimchatTorah
HolidayInYear = Hebrew_InYear(InYear, 1, 23)
Case Chanukkah
HolidayInYear = Hebrew_InYear(InYear, 3, 25)
Case TuBShevat
HolidayInYear = Hebrew_InYear(InYear, 5, 15)
Case Purim
HolidayInYear = Hebrew_InYear(InYear, 6, 14)
Case PesachPassover
HolidayInYear = Hebrew_InYear(InYear, 7, 15)
Case LagBOmer
HolidayInYear = Hebrew_InYear(InYear, 8, 18)
Case Shavuot
HolidayInYear = Hebrew_InYear(InYear, 9, 6)
Case TishaBAv
HolidayInYear = Hebrew_InYear(InYear, 11, 9)
Case Else
Debug.Assert False
End Select
End Property
Private Function CalcTaxDay(InYear As Date) As Date
CalcTaxDay = DateSerial(DatePart("yyyy", InYear), 4, 15)
Do While DatePart("w", CalcTaxDay) = vbSunday Or _
DatePart("w", CalcTaxDay) = vbSaturday
CalcTaxDay = DateAdd("d", 1, CalcTaxDay)
Loop
End Function
Private Function LastDayInMonth(dateYear As Date, nMonth As Long, _
nDayOfWeek As VbDayOfWeek) As Date
Dim dateValid As Date
LastDayInMonth = DateSerial(DatePart("yyyy", dateYear), nMonth, 1)
Do Until DatePart("m", LastDayInMonth) <> nMonth
If DatePart("w", LastDayInMonth) = nDayOfWeek Then
dateValid = LastDayInMonth
End If
LastDayInMonth = DateAdd("d", 1, LastDayInMonth)
Loop
LastDayInMonth = dateValid
End Function
Private Function DayOfWeek(dateYear As Date, nMonth As Long, nWeeks As _
Long, nDayOfWeek As VbDayOfWeek) As Date
DayOfWeek = DateSerial(DatePart("yyyy", dateYear), nMonth, 1)
If DatePart("w", DayOfWeek) > nDayOfWeek Then
DayOfWeek = DateAdd("d", 7 - (DatePart("w", DayOfWeek) - _
nDayOfWeek), DayOfWeek)
ElseIf DatePart("w", DayOfWeek) < nDayOfWeek Then
DayOfWeek = DateAdd("d", (nDayOfWeek - DatePart("w", DayOfWeek)), _
DayOfWeek)
End If
DayOfWeek = DateAdd("d", ((nWeeks - 1) * 7), DayOfWeek)
End Function
Private Function CalcEaster(InYear As Date) As Date
Dim nGolden As Long
Dim nTillPaschal As Long
Dim nDayPaschal As Long
Dim nCentury As Long
Dim nEpact As Long
Dim nSundayPaschal As Long
Dim nMonth As Long
Dim nDay As Long
Dim nYear As Long
nYear = DatePart("yyyy", InYear)
nGolden = nYear Mod 19
nCentury = nYear \ 100
nEpact = (nCentury - nCentury \ 4 - (8 * nCentury + 13) \ 25 + 19 * _
nGolden + 15) Mod 30
nTillPaschal = nEpact - (nEpact \ 28) * (1 - (nEpact \ 28) * (29 \ _
(nEpact + 1)) * ((21 - nGolden) \ 11))
nDayPaschal = (nYear + nYear \ 4 + nTillPaschal + 2 - nCentury + _
nCentury \ 4) Mod 7
nSundayPaschal = nTillPaschal - nDayPaschal
nMonth = 3 + (nSundayPaschal + 40) \ 44
nDay = nSundayPaschal + 28 - 31 * (nMonth \ 4)
CalcEaster = DateSerial(nYear, nMonth, nDay)
End Function
Public Property Get IsHoliday(DateQuery As Date) As Boolean
Dim i As Long
IsHoliday = False
For i = 0 To HolidayCount - 1
If Int(DateQuery) = Int(HolidayInYear(i, DateQuery)) Then
IsHoliday = True
Exit For
End If
Next
End Property
Public Property Get HolidayNameOfDate(DateQuery As Date) As String
Dim i As Long
HolidayNameOfDate = "None"
For i = 0 To HolidayCount - 1
If Int(DateQuery) = Int(HolidayInYear(i, DateQuery)) Then
HolidayNameOfDate = HolidayName(i)
Exit For
End If
Next
End Property
Private Function Hebrew_JulianToDate(ByVal iJulian As Long) As Date
Hebrew_JulianToDate = CDate(iJulian - 2415019)
End Function
Private Sub Hebrew_JulianToHebrew(ByVal jdn As Long, _
ByRef iyear As Integer, _
ByRef iMonth As Integer, _
ByRef iDay As Integer, _
Optional ByVal monthcoding As Integer)
Dim InputJDN As Long
Dim tishri1 As Long
Dim LeftOverDays As Long
If jdn <= 347997 Then
iyear = 0
iMonth = 0
iDay = 0
Else
InputJDN = jdn - 347997
iyear = (InputJDN \ 365) + 1
tishri1 = Hebrew_ElapsedCalendarDays(iyear)
While (tishri1 > InputJDN)
iyear = iyear - 1
tishri1 = Hebrew_ElapsedCalendarDays(iyear)
Wend
iMonth = 1
LeftOverDays = InputJDN - tishri1
While (LeftOverDays >= Hebrew_LastDayOfMonth(iyear, iMonth))
LeftOverDays = LeftOverDays - _
Hebrew_LastDayOfMonth(iyear, iMonth)
iMonth = iMonth + 1
Wend
If Sgn(monthcoding) = -1 Then
If iMonth > 6 Then
If Hebrew_LeapYear(iyear) Then
iMonth = iMonth - 14
Else
iMonth = iMonth - 13
End If
End If
End If
iDay = LeftOverDays + 1
End If
End Sub
Private Sub Hebrew_GregorianToHebrew(ByRef iyear As Integer, _
ByRef iMonth As Integer, _
ByRef iDay As Integer)
Call Hebrew_JulianToHebrew(Hebrew_JulianToGregorian(iyear, _
iMonth, _
iDay), _
iyear, iMonth, iDay)
End Sub
Private Function Hebrew_MonthName(ByVal iyear, _
ByVal iMonth) As String
If ((iMonth > 6) And (Not (Hebrew_LeapYear(iyear)))) Then
iMonth = iMonth + 1
End If
Select Case iMonth
Case 1
Hebrew_MonthName = "Tishri" ' or "Tishrey"
Case 2
Hebrew_MonthName = "Heshvan"
Case 3
Hebrew_MonthName = "Kislev"
Case 4
Hebrew_MonthName = "Teveth" ' or "Tevet"
Case 5
Hebrew_MonthName = "Shevat"
Case 6
If (Not (Hebrew_LeapYear(iyear))) Then
Hebrew_MonthName = "Adar"
Else
Hebrew_MonthName = "Adar I"
End If
Case 7
Hebrew_MonthName = "Adar II"
Case 8
Hebrew_MonthName = "Nisan"
Case 9
Hebrew_MonthName = "Iyyar"
Case 10
Hebrew_MonthName = "Sivan"
Case 11
Hebrew_MonthName = "Tammuz"
Case 12
Hebrew_MonthName = "Av"
Case 13
Hebrew_MonthName = "Elul"
Case Else
Hebrew_MonthName = ""
End Select
End Function
Private Function Hebrew_HebrewToJulian(ByVal iyear, _
ByVal iMonth, ByVal iDay) As Long
Dim jdn As Long
Dim counter As Integer
If iMonth < 0 Then
If Hebrew_LeapYear(iyear) Then
iMonth = 14 + iMonth
Else
iMonth = 13 + iMonth
End If
End If
jdn = Hebrew_ElapsedCalendarDays(iyear)
For counter = 1 To (iMonth - 1) Step 1
jdn = jdn + Hebrew_LastDayOfMonth(iyear, counter)
Next counter
Hebrew_HebrewToJulian = jdn + (iDay - 1 + 347997)
End Function
Private Function Hebrew_DateInOrAfterCivilYear( _
ByVal civilYear As Integer, _
ByVal HebrewMonth As Integer, _
ByVal HebrewDay As Integer, _
Optional ByVal calendarType As Integer = _
Hebrew_c_Gregorian) As Long
Dim jdnJanuary1 As Long
Dim jdnHoliday As Long
Dim hebrewYear As Integer
Dim dummy1 As Integer
Dim dummy2 As Integer
jdnJanuary1 = Hebrew_CivilToJulian(civilYear, 1, 1, calendarType)
Hebrew_JulianToHebrew jdnJanuary1, hebrewYear, dummy1, dummy2
jdnHoliday = Hebrew_HebrewToJulian(hebrewYear, _
HebrewMonth, HebrewDay)
If jdnHoliday < jdnJanuary1 Then
' Oops! Wrong civil Year. Use next hebrewYear's in stead.
jdnHoliday = Hebrew_HebrewToJulian(hebrewYear + 1, _
HebrewMonth, HebrewDay)
End If
Hebrew_DateInOrAfterCivilYear = jdnHoliday
End Function
Private Function Hebrew_DateInOrBeforeCivilYear( _
ByVal civilYear As Integer, _
ByVal HebrewMonth As Integer, _
ByVal HebrewDay As Integer, _
Optional ByVal calendarType As Integer = _
Hebrew_c_Gregorian) As Long
Dim jdnDecember31 As Long
Dim jdnHoliday As Long
Dim hebrewYear As Integer
Dim dummy1 As Integer
Dim dummy2 As Integer
jdnDecember31 = Hebrew_CivilToJulian(civilYear, _
12, 31, calendarType)
Call Hebrew_JulianToHebrew(jdnDecember31, hebrewYear, _
dummy1, dummy2)
jdnHoliday = Hebrew_HebrewToJulian(hebrewYear, _
HebrewMonth, HebrewDay)
If jdnHoliday > jdnDecember31 Then
' Oops! Wrong civil Year. Use last hebrewYear's in stead.
jdnHoliday = Hebrew_HebrewToJulian(hebrewYear - 1, _
HebrewMonth, HebrewDay)
End If
Hebrew_DateInOrBeforeCivilYear = jdnHoliday
End Function
Private Function Hebrew_ElapsedCalendarDays(ByVal iyear) As Long
Dim MonthsElapsed As Long
Dim PartsElapsed As Long
Dim HoursElapsed As Long
Dim ConjunctionDay As Long
Dim ConjunctionParts As Long
Dim AlternativeDay As Long
MonthsElapsed = (235 * (((iyear - 1) \ 19))) + _
(12 * ((iyear - 1) Mod 19)) + _
(7 * ((iyear - 1) Mod 19) + 1) \ 19
PartsElapsed = 204 + 793 * (MonthsElapsed Mod 1080)
HoursElapsed = 5 + 12 * MonthsElapsed + _
793 * ((MonthsElapsed \ 1080)) + _
PartsElapsed \ 1080
ConjunctionDay = 1 + 29 * MonthsElapsed + HoursElapsed \ 24
ConjunctionParts = (1080 * (HoursElapsed Mod 24)) + _
PartsElapsed Mod 1080
If ((ConjunctionParts >= 19440) Or _
(((ConjunctionDay Mod 7) = 2) And _
(ConjunctionParts >= 9924) And _
(Not (Hebrew_LeapYear(iyear)))) Or _
(((ConjunctionDay Mod 7) = 1) And _
(ConjunctionParts >= 16789) And _
(Hebrew_LeapYear(iyear - 1)))) _
Then
AlternativeDay = ConjunctionDay + 1
Else
AlternativeDay = ConjunctionDay
End If
If (((AlternativeDay Mod 7) = 0) Or _
((AlternativeDay Mod 7) = 3) Or _
((AlternativeDay Mod 7) = 5)) _
Then
AlternativeDay = AlternativeDay + 1
End If
Hebrew_ElapsedCalendarDays = AlternativeDay
End Function
Private Function Hebrew_LastDayOfMonth(ByVal iyear, _
ByVal iMonth) As Integer
If ((iMonth > 6) And (Not (Hebrew_LeapYear(iyear)))) Then
iMonth = iMonth + 1
End If
Select Case iMonth
Case 2
If Hebrew_LongHeshvan(iyear) Then
Hebrew_LastDayOfMonth = 30
Else
Hebrew_LastDayOfMonth = 29
End If
Case 3
If Hebrew_ShortKislev(iyear) Then
Hebrew_LastDayOfMonth = 29
Else
Hebrew_LastDayOfMonth = 30
End If
Case 6
If Hebrew_LeapYear(iyear) Then
Hebrew_LastDayOfMonth = 30
Else
Hebrew_LastDayOfMonth = 29
End If
Case 4, 7, 9, 11, 13
Hebrew_LastDayOfMonth = 29
Case Else
Hebrew_LastDayOfMonth = 30
End Select
End Function
Private Function Hebrew_LeapYear(ByVal iyear) As Boolean
If ((((7 * iyear) + 1) Mod 19) < 7) Then
Hebrew_LeapYear = True
Else
Hebrew_LeapYear = False
End If
End Function
Private Function Hebrew_JulianToGregorian(ByVal iyear As Integer, _
ByVal iMonth As Integer, _
ByVal iDay As Integer) As Long
Dim lYear As Long
Dim lMonth As Long
Dim lDay As Long
lYear = CLng(iyear)
lMonth = CLng(iMonth)
lDay = CLng(iDay)
Hebrew_JulianToGregorian = 367 * lYear - _
((7 * (lYear + 5001 + ((lMonth - 9) \ 7))) \ 4) _
+ ((275 * lMonth) \ 9) + lDay + 1729777
End Function
Private Function Hebrew_CivilToJulian(ByVal iyear As Integer, _
ByVal iMonth As Integer, _
ByVal iDay As Integer, _
Optional ByVal calendarType As Integer _
= Hebrew_c_Gregorian) As Long
Dim lYear As Long
Dim lMonth As Long
Dim lDay As Long
If calendarType = Hebrew_c_Gregorian And ((iyear > 1582) Or _
((iyear = 1582) And (iMonth > 10)) Or _
((iyear = 1582) And (iMonth = 10) And (iDay > 14))) _
Then
lYear = CLng(iyear)
lMonth = CLng(iMonth)
lDay = CLng(iDay)
Hebrew_CivilToJulian = ((1461 * (lYear + 4800 + _
((lMonth - 14) \ 12))) \ 4) _
+ ((367 * (lMonth - 2 - 12 * _
(((lMonth - 14) \ 12)))) \ 12) _
- ((3 * (((lYear + 4900 + _
((lMonth - 14) \ 12)) \ 100))) \ 4) _
+ lDay - 32075
Else
Hebrew_CivilToJulian = Hebrew_JulianToGregorian(iyear, _
iMonth, iDay)
End If
End Function
Private Function Hebrew_LongHeshvan(ByVal iyear) As Boolean
Hebrew_LongHeshvan = ((Hebrew_DaysInYear(iyear) Mod 10) = 5)
End Function
Private Function Hebrew_ShortKislev(ByVal iyear) As Boolean
Hebrew_ShortKislev = ((Hebrew_DaysInYear(iyear) Mod 10) = 3)
End Function
Private Function Hebrew_DaysInYear(ByVal iyear) As Boolean
Hebrew_DaysInYear = Hebrew_HebrewToJulian(iyear + 1, 1, 1) - _
Hebrew_HebrewToJulian(iyear, 1, 1)
End Function
Private Function Hebrew_InYear(InYear As Date, ByVal nMonth As Integer, nDay As Integer)
Dim nMonthOrig As Integer
Dim nDayOrig As Integer
Dim nYearOrig As Integer
Dim nYear As Integer
nMonthOrig = nMonth
nDayOrig = nDay
nYear = DatePart("yyyy", InYear)
nYearOrig = nYear
Hebrew_InYear = Hebrew_JulianToDate(Hebrew_DateInOrBeforeCivilYear( _
nYear, nMonth, nDay))
nYear = DatePart("yyyy", Hebrew_InYear)
nMonth = DatePart("m", Hebrew_InYear)
nDay = DatePart("d", Hebrew_InYear)
Hebrew_GregorianToHebrew nYear, nMonth, nDay
If Hebrew_LeapYear(nYear) And nMonthOrig >= 7 Then
Hebrew_InYear = Hebrew_JulianToDate(Hebrew_DateInOrBeforeCivilYear( _
nYearOrig, nMonthOrig + 1, nDayOrig))
End If
End Function
'--- End of clsHolidays
| |
| Sample Usage: | |
Dim objHolidays As clsHolidays
Dim i As Long
Set objHolidays = New clsHolidays
For i = 0 To objHolidays.Count - 1
Debug.Print objHolidays.HolidayName(i) & " occurs on " & _
Format(objHolidays.HolidayInYear(i, Now), "mmmm dd, yyyy")
Next
If objHolidays.IsHoliday(Now) Then
Debug.Print "Today is " & objHolidays.HolidayNameOfDate(Now)
Else
Debug.Print "Today is not a holiday"
End If
| |