Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations Westi on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

classes to generate apptments and avoid holidays

Status
Not open for further replies.

rewdee

Programmer
Aug 17, 2001
295
US
Thanks to those who helped me with the Easter dates. I thought I would show my appreciation by sharing the code. If you make any enhancements or comments please post. If anyone is interested just Email me your address and I will send you the project on how I used the classes.

Here's the code for creating statutory holidays class:
Code:
''''''''''''''''''''''''''''''''''''
Statutory Holiday Class

Created by William D. Nielsen

[URL unfurl="true"]www.2Rs.ca[/URL]
Date:  September 11,2002

Description:  This class basic function is to calculate and manage statutory holidays

Client: YMCA of Woodbuffalo

''''''''''''''''''''''''''''''''''''''''''

Option Compare Database

'error handling constants
Private Const cERR_ADD_DATE As Long = 3001
Private Const cERR_ADD_MSG  As String = "Error in attempting to add the date: "

'private fields
Private mdteStart        As Date
Private mdteEnd          As Date
Private mcolStatHolidays As Collection

'Initialize the class and set variables to default values
Private Sub Class_Initialize()
    Set mcolStatHolidays = New Collection
    mdteStart = Date
    mdteEnd = DateSerial(Year(dteStart), 12, 31) 'default to last day of the current year
End Sub

'Free class and instance of statutory holidays
Private Sub Class_Terminate()
    Set mcolStartHolidays = Nothing
End Sub

Property Let StartDate(ByVal dte As Date)
    mdteStart = dte
    'force recalculation
    getStatHolidays
End Property

Property Get StartDate() As Date
    StartDate = mdteStart
End Property
Property Let EndDate(ByVal dte As Date)
    mdteEnd = dte
    'force recalculation
    getStatHolidays
End Property

Property Get EndDate() As Date
    EndDate = mdteEnd
End Property
Public Function StatHolidays() As Collection
    Set StatHolidays = mcolStatHolidays
End Function

Public Sub AddStatHoliday(dte As Date)
'simple adding to the holiday collection
    On Error GoTo errHandler
    mcolStatHolidays.Add dte, CStr(dte)
    Exit Sub
errHandler:
    Select Case Err.number
        Case 457: 'just ignore if it is a duplicate entry
            Exit Sub
        Case Else
            Err.Raise cERR_ADD_DATE, Err.Source, 
cERR_ADD_MSG & CStr(dte)
    End Select
End Sub

Public Sub RemoveStatHoliday(dte As Date)
    On Error Resume Next
    mcolStatHolidays.Remove dte
End Sub

Public Sub AddVariableStatHoliday(iMonth As Integer, iWeek 
As Integer, iDay As Integer)
'''''''''''''''''''''''''''''''''''''''
'IN:                                                        
      '
'   iMonth : represents the Month e.g. 1-January,2-
February, etc. '
'   iWeek  : represents what week of the month e.g. 3-third 
week  '
'   iDay   : represents the weekday of the 1-Sunday,2-
Monday, etc '
'An Example would be the Canadian Holdiay Victoria day 
which is   '
'the third Monday of each May and hence the call to this 
procedure'
'would 
be:                                                        '
'          AddVariableStatHoliday 
5,3,1                           '
''''''''''''''''''''''''''''''''''''''''''''
********************************************
'*PROGRAMMER'S NOTE:  There is no error checking to ensure 
that  *
'*                    that what is passed to this procedure 
is   *
'*                    invalid.  Disaster may come upon 
the       *
'*                    programmer who uses this without 
either    *
'*                    writing a more stringent data 
checking     *
'*                    here or on the front end as 
dateserial     *
'*                    will always return a valid 
date.           *
'* ******************************************
'some holidays like labour day, Victoria Day, etc. do not 
have a fixed date and hence this function to
'calculate and add to collection of statutory holidays
    Dim iWkday   As Integer
    Dim iStatDay As Integer
    Dim iCount   As Integer
    Dim dteStat  As Date
    
    On Error GoTo errHandler
    
    'get the first day of the particular month
    iWkday = Weekday(DateSerial(Year(mdteStart), iMonth, 1))
    'find what day it is
    'check if it is the correct date
    If iWkday = iDay Then
        iStatDay = iWkday + ((iWeek - 1) * 7)
     Else
        'loop through the days of the week until reached 
the parameter day
        iCount = 1
        Do
            iCount = iCount + 1
        Loop Until iDay = Weekday(DateSerial(Year
(mdteStart), iMonth, iCount))
        
        iStatDay = iCount + ((iWeek - 1) * 7)
    End If
    
    'add the holiday to the collection
    dteStat = DateSerial(Year(mdteStart), iMonth, iStatDay)
    'check if we need to see if next year
    mcolStatHolidays.Add dteStat, CStr(dteStat)
    If Year(mdteEnd) > Year(mdteStart) Then
        mcolStatHolidays.Add DateSerial(Year(mdteEnd), 
iMonth, iStatDay), CStr(DateSerial(Year(mdteEnd), iMonth, 
iStatDay))
    End If
    Exit Sub
errHandler:
    Select Case Err.number
        Case 457: 'just ignore if it is a duplicate entry
            Exit Sub
        Case Else
            Err.Raise cERR_ADD_DATE, Err.Source, cERR_ADD_MSG & CStr(dteStat)
    End Select
    
End Sub

Private Sub getStatHolidays()
**********************************************************
'*PROGRAMMER'S NOTE:  It would be more efficient to check 
whether a date is already present and  *
'*                    add/delete dates depending on my 
mdteStart and mdeteEnd date ranges but    *
'*                    but for simplicity sake I just delete 
everything and start over.           *
********************************************************
    Dim iCount    As Integer
    Dim dteArr(8) As Date
    Dim dteEaster As Date
    Dim dteGoodFr As Date
    
    'clear the previous holidays in my collection
    For iCount = 1 To mcolStatHolidays.Count
        mcolStatHolidays.Remove 1
    Next
    
    'add common date type holidays
    dteArr(0) = DateSerial(Year(mdteStart), 1, 1)      'New 
Year's day
    dteArr(1) = DateSerial(Year(mdteStart), 7, 
1)      'Canada Day
    dteArr(2) = DateSerial(Year(mdteStart), 12, 
25)    'Christmas
    dteArr(3) = DateSerial(Year(mdteStart), 12, 
26)    'Boxing Day
    
    'now add dates to see if we need to add dates for the 
following year
    'this will occur when the dteStart is in one year and 
dteFinish is
    'in the following year e.g. December 1, 2002 and 
Febuary 14, 2003
**********************************************************
    '*PROGRAMMER'S NOTE:  For my purposes the mdteStart and 
mdteEnd time frame will not be greater   *
    '*                    than one year.  For time frames 
greater than one year a dynamic array will *
    '*                    have to be 
used.                                                           *
***********************************************************
    dteArr(4) = DateSerial(Year(mdteStart) + 1, 1, 
1)      'New Year's day
    dteArr(5) = DateSerial(Year(mdteStart) + 1, 7, 
1)      'Canada Day
    dteArr(6) = DateSerial(Year(mdteStart) + 1, 12, 
25)    'Christmas
    dteArr(7) = DateSerial(Year(mdteStart) + 1, 12, 
26)    'Boxing Day
    
    'now iterate through the holidays seeing if the dates 
are within the specified
    For iCount = 0 To UBound(dteArr)
        If dteArr(i) >= mdteStart And dteArr(i) <= mdteEnd Then mcolStatHolidays.Add dteArr(i), dteArr(i)
    Next iCount
    
    'now check to see if the Easter holidays need to be 
added
    dteEaster = EasterDate(Year(mdteStart))
    dteGoodFr = DateAdd(&quot;d&quot;, -2, dteEaster)
    If dteEaster >= mdteStart And dteEaster <= mdteEnd Then 
mcolStatHolidays.Add dteEaster, CStr(dteEaster)
    If dteGoodFr >= mdteStart And dteGoodFr <= mdteEnd Then 
mcolStatHolidays.Add dteGoodFr, CStr(dteGoodFr)
    
    'if the EndDate goes to the next year--check to add 
those Easter Dates
    If Year(dteEnd) > Year(dteStart) Then
        dteEaster = EasterDate(Year(mdteEnd))
        dteGoodFr = DateAdd(&quot;d&quot;, -2, dteEaster)
        If dteEaster >= mdteStart And dteEaster <= mdteEnd 
Then mcolStatHolidays.Add dteEaster, CStr(dteEaster)
        If dteGoodFr >= mdteStart And dteGoodFr <= mdteEnd 
Then mcolStatHolidays.Add dteGoodFr, CStr(dteGoodFr)
    End If
End Sub

Public Function EasterDate(YearToCalculate As Integer) As 
Date
'taken from 
[URL unfurl="true"]http://www.blibbleblobble.co.uk/Downloads/CodeLibrary/Functi[/URL]
ons/EasterDate.htm
    Dim CurrentCentury As Integer
    Dim YearMod19 As Integer
    Dim Temp As Variant
    Dim tA As Integer
    Dim tB As Integer
    Dim tC As Integer
    Dim Temp2 As Variant
    Dim tD As Integer
    Dim tE As Integer
    Dim EasterDayOfMonth As Integer
             
    'Store the number of the current century
    CurrentCentury = Int(YearToCalculate / 100)
     
    'Determine the approximate place in the 19-year solunar 
cycle
    YearMod19 = Int(Remainder(YearToCalculate, 19))
     
    'calculate PFM date
    Temp = Int((CurrentCentury - 15) / 2) + 202 - (11 * 
YearMod19)
         
    'Insert leap days to fiddle the solunar cycle to make 
it 19 years
    If (CurrentCentury > 26) Then
        Temp = -1
    End If
     
    Select Case CurrentCentury
        Case 21, 24, 25:
            Temp = -1
        Case Else:
    End Select
     
     
    Temp = Remainder(Temp, 30)
         
    tA = Temp + 21
     
    If Temp = 29 Then
        tA = tA - 1
    End If
     
    If (Temp = 28) And (YearMod19 > 10) Then
        tA = tA - 1
    End If
     
    'find the next Sunday
    tB = Remainder((tA - 19), 7)
     
    'fix the leap years in 1 of every 4 ceuturies
    tC = Remainder((40 - CurrentCentury), 4)
    If tC = 3 Then
        tC = tC + 1
    End If
     
    If tC > 1 Then
        tC = tC + 1
    End If
                     
    Temp = Remainder(YearToCalculate, 100)
     
    Temp2 = CStr(Temp / 4)
     
    If Mid(Temp2, 1, 1) = &quot;.&quot; Then
        Temp2 = &quot;0&quot; & Temp2
    End If
     
    Temp2 = Int(Temp2)
     
    'Find the next sunday
    tD = Remainder((Temp + Temp2), 7)
     
    tE = Remainder((20 - tB - tC - tD), 7) + 1
     
    'Calculate the number of days since the beginning of 
march
    EasterDayOfMonth = tA + tE
     
    'Convert to a VB date format
    EasterDate = CDate(&quot;1 march &quot; & CStr(YearToCalculate)) 
+ EasterDayOfMonth - 1

End Function

Private Function Remainder(VarNumerator As Variant, 
varDivisor As Variant) As Integer
    Remainder = VarNumerator Mod varDivisor
End Function

The code to the recurring dates class:
Code:
'''''''''''''''''''''''''''''''''''''''''''''''
clsRecurrDates Class
Created by William D. Neilsen
[URL unfurl="true"]www.2rs.ca[/URL]

Date:  September 11, 2002
Description:  This class will initiate an object that will have the properties and methods to create a list of recurring dates and holiday conflicts between dates

Client : YMCA of WoodBuffalo

''''''''''''''''''''''''''''''''''''''''''''''''''''''''''

Option Compare Database

Private mobjHolidays As clsStatHolidays     'object of 
clsStatHolidays to watch for conflicting days
Private mdteStart    As Date                'start point to 
check for appointments
Private mdteEnd      As Date                'end point to 
check for appointements
Private mbInclHol    As Boolean             'whether to 
make an appointment on a holiday
Private mintNumOfRec As Integer             'Number of 
appointments that could be made
Private mcolApptDays As Collection          'Days that 
appointments are on eg. every Tuesday and Wednesday
Private mcolRecDates As Collection          'collection of 
appointments that could be made
Private mcolHolConf  As Collection          'collection of 
appointments that could not be made due to holiday conflicts
Private bDoneFlag    As Boolean             'variable keeps 
track whether need to recalculate again

'Initialize the class and set variables to default values
Private Sub Class_Initialize()
On Local Error GoTo Class_Initialize_Err
        Dim Msg As String
        mdteStart = Date
        mdteEnd = DateSerial(Year(Date), 12, 31)
        mbInclHol = False
        mintNumOfRec = 0
        Set mcolApptDays = New Collection
        Set mcolRecDates = New Collection
        Set mcolHolConf = New Collection
        Set mobjHolidays = New clsStatHolidays
        mobjHolidays.StartDate = mdteStart
        mobjHolidays.EndDate = mdteEnd
        bDoneFlag = False
Class_Initialize_End:
         Exit Sub
Class_Initialize_Err:
         Msg = &quot;Error #: &quot; & Format$(Err.number) & vbCrLf
         Msg = Msg & Err.Description
         Err.Raise vbObjectError, &quot;Cannot Create Appointment Recurring Object&quot;, Msg
         Resume Class_Initialize_End
End Sub

'Free class and instance of Textfile
Private Sub Class_Terminate()
    On Error Resume Next
    Set mcolApptDays = Nothing
    Set mcolRecDates = Nothing
    Set mcolHolConf = Nothing
    Set mobjHolidays = Nothing
End Sub

Property Let StartDate(ByVal dte As Date)
    mdteStart = dte
    mobjHolidays.StartDate = dte
    'every time this variable is reset signal needs recalc
    bDoneFlag = False
End Property

Property Get StartDate() As Date
    StartDate = mdteStart
End Property

Property Let EndDate(ByVal dte As Date)
    mdteEnd = dte
    mobjHolidays.EndDate = dte
     'every time this variable is reset signal needs recalc
    bDoneFlag = False
End Property

Property Get EndDate() As Date
    EndDate = mdteEnd
End Property
Property Let IncludeHolidays(ByVal b As Boolean)
    mbInclHol = b
End Property
Property Get IncludeHolidays() As Boolean
    IncludeHolidays = mbInclHol
End Property
Property Get NumberOfApptments() As Integer
    NumberOfApptments = mintNumOfRec
End Property
Property Get ApptDays() As Collection
 Set ApptDays = mcolApptDays
End Property
Public Sub AddADate(iDay As Integer)
    On Error Resume Next
    mcolApptDays.Add iDay, CStr(iDay)
End Sub
Public Sub RemoveADate(iDay As Integer)
    On Error Resume Next
    mcolApptDays.Remove CStr(iDay)
End Sub
Public Sub AddStatHoliday(dte As Date)
    mobjHolidays.AddStatHoliday (dte)
End Sub
Public Sub AddVariableDateHoliday(iMonth As Integer, iWk As 
Integer, iDay As Integer)
    mobjHolidays.AddVariableStatHoliday iMonth, iWk, iDay
End Sub
Public Function getRecurringAppts() As Collection
    '''''''
    If Not bDoneFlag Then DoAppments
    Set getRecurringAppts = mcolRecDates
End Function
Public Function getHolidayConflicts() As Collection
    '''''''
    If Not bDoneFlag Then DoAppments
    Set getHolidayConflicts = mcolHolConf
End Function

Private Sub DoAppments()
'''the meat of the class is here'''
    Dim iCount       As Integer
    Dim iMonth       As Integer
    Dim iDay         As Integer
    Dim iYear        As Integer
    Dim dteStartFrom As Date
    
    'iterate through the days collection
    For iCount = 1 To mcolApptDays.Count
        'get the first start date for this particular date
        iMonth = month(mdteStart): iDay = Day(mdteStart): iYear = Year(mdteStart)
        Do
            dteStartFrom = DateSerial(iYear, iMonth, iDay)
            iDay = iDay + 1
        Loop Until mcolApptDays.Item(iCount) = Weekday
(dteStartFrom)
        
        'now keep adding weeks until greater than end date
        Do While dteStartFrom <= mdteEnd
            If IsHoliday(dteStartFrom) And Not mbInclHol 
Then
                mcolHolConf.Add dteStartFrom, CStr
(dteStartFrom)
            Else
                mcolRecDates.Add dteStartFrom, CStr
(dteStartFrom)
                mintNumOfRec = mintNumOfRec + 1
            End If
            
            'reset new start date
            dteStartFrom = DateAdd(&quot;ww&quot;, 1, dteStartFrom)
        Loop
    Next iCount
    
    'done the calcs for these parameters
    bDoneFlag = True
End Sub

Private Function IsHoliday(dte As Date) As Boolean
'simple iteration looling to see if the date passed is in 
the holiday collection
    Dim iCount      As Integer
    Dim colHolidays As Collection
    Set colHolidays = mobjHolidays.StatHolidays
    IsHoliday = False
    For iCount = 1 To colHolidays.Count
        If Int(dte) = Int(colHolidays.Item(iCount)) Then IsHoliday = True
    Next iCount
    Set colHolidays = Nothing
End Function

Here's the form's module that I implemented the class. The DoCheck function just checks that all the appropriate text boxes are filled etc. There are seven checkboxes, one for each day and the tag property is set to correspond to the vbConstant that each day represents:
Code:
Private Sub CmdRecur_Click()
    Dim objRecurrences As New clsRecurrDates
    Dim colRecAppts    As New Collection
    Dim ctl            As Control
    Dim iCount         As Integer
    Dim sMsg           As String
    
    If doCheck Then
        With objRecurrences
            .StartDate = Me.Date_Start
            .EndDate = Me.Date_Finish
            .AddVariableDateHoliday 5, 3, 
vbMonday       'Victoria Day
            .AddVariableDateHoliday 8, 1, 
vbMonday       'Civic Holiday for Alberta
            .AddVariableDateHoliday 9, 1, 
vbMonday       'Canadian Labour Day
            For Each ctl In Me
                If IsNumeric(ctl.Tag) Then
                    If ctl.Value Then .AddADate CInt
(ctl.Tag)
                End If
            Next ctl
            
            If .getRecurringAppts.Count > 0 Then
                Set colRecAppts = .getRecurringAppts
                sMsg = &quot;The Following Appointment were 
added:&quot; & vbCrLf
                For iCount = 1 To colRecAppts.Count
                    sMsg = sMsg & colRecAppts.Item(iCount) 
& vbCrLf
                Next iCount
            End If
            
            If .getHolidayConflicts.Count > 0 Then
                sMsg = sMsg & vbCrLf & &quot;The following 
Appointments were not made:&quot; & vbCrLf
                For iCount = 1 To .getHolidayConflicts.Count
                    sMsg = sMsg & .getHolidayConflicts.Item
(iCount) & vbCrLf
                Next iCount
            End If
        End With
        MsgBox sMsg, vbInformation
    End If
End Sub

Hope this helps,
Rewdee
Code:
 
Rewdee,

See faq181-261. It may be of some use in the appointment arena. I opted for 'static' maintenance of the holiday dates, as virtually every instance of implementing the routine and variations of it observed both a different number of holidays and observed them on different dates. So, in my experience, I have seen as few as 7 and as many as 13 holidays for an organization, but have also seen (U.S. &quot;Presidents Day&quot; which actually occurs in March) 'observed' in December (this particular company 'collected' several holidays to Dec to provide a week of holiday time from christmas to New Year).

Given the vagaries of corporate management, I would hesitate to provide an invariant assignment of holiday dates.

MichaelRed
m.red@att.net

There is never time to do it right but there is always time to do it over
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top