Enable rotating biweekly calendar schedule in Projects 2007 VBA - vba

Based on the code by Rachel Hettinger.
I would like to modify this code in such a way that I have a calendar 2 weeks of 6am to 2:30pm Monday through Friday then Monday through Thursday from 2pm to 12:30am (ending Friday morning). I require it to be this way as my human resources rotate between two weeks days and two weeks nights. I have gotten the calendar to the point that it will apply the two different shift starting times, but not the two different durations, or the non-working Friday afternoon.
I tried an IIF, and tired to have it accomplish this based on the cycle number, but I don't think I have my setup correct. I tried putting the code to select each shift as their own sub, but I'm a beginner and don't think I did it correctly.
Any assistance is appreciated.
'# is the original code
'##add an If qualifier to select between two different set conditions?
' Set e = Iif(CycleNum = "1" Or "2", c.Exceptions.Add(Type:=pjDaily _
' , Start:=CyclesStart + (i - 1) * 7 _
' , Occurrences:=5 _
' , Name:=ExceptionName & CycleNum),
' c.Exceptions.Add(Type:=pjDaily _
' , Start:=CyclesStart + (i - 1) * 7 _
' , Occurrences:=5 _
' , Name:=ExceptionName & CycleNum))
'# Set e = c.Exceptions.Add(Type:=pjDaily _
'# , Start:=CyclesStart + (i - 1) * 7 _
'# , Occurrences:=5 _
'# , Name:=ExceptionName & CycleNum)
'# If Err.Number = 0 Then
'# e.Shift1.Start = StartTime
'# e.Shift1.Finish = StartTime + #8:30:00 AM# '-=adjust length of each shift, #X = hours=-
Else
'switch to day-by-day to not overwrite holidays inherited from the Standard calendar
Dim d As Long
For d = 1 To 5
Set e = c.Exceptions.Add(Type:=pjDaily _
, Start:=CyclesStart + (i - 1) * 7 + d - 1 _
, Occurrences:=1 _
, Name:=ExceptionName & CycleNum & " day " & d)
e.Shift1.Start = StartTime
e.Shift1.Finish = StartTime + #6:00:00 AM#
Next d
End If
On Error GoTo 0
Next i
Full Code:
Sub CreateRotatingCalendar()
'ORIGINAL AUTHOR: Rachel Hettinger
' Create a calendar with a cycle of 4 rotating weekly schedules by utilizing the calendar exceptions.
Const CalName As String = "Rotating Shift Alpha" 'name of calendar
Const ExceptionName As String = "Cycle "
'set number of weeks and start times per calendar
Const Week1Start As Date = #6:00:00 AM#
Const Week2Start As Date = #6:00:00 AM#
Const Week3Start As Date = #2:00:00 PM#
Const Week4Start As Date = #2:00:00 PM#
'Set Calendar start and end dates
Const CyclesStart As Date = #8/1/2022# ' must be a Monday
Const CyclesEnd As Date = #8/8/2032# '
' create calendar, but ignore errors in case calendar already exists
On Error Resume Next
BaseCalendarCreate Name:=CalName, FromName:="Standard"
On Error GoTo 0
Dim c As Calendar
Set c = ActiveProject.BaseCalendars(CalName)
' in case this routine is run again to update the rotating calendar, delete exceptions related to the rotation (and leave holidays)
Dim e As Exception
For Each e In c.Exceptions
If e.Name Like ExceptionName & "*" Then
e.Delete
End If
Next e
' remove other shifts and set shift hours
Dim wd As PjWeekday
For wd = pjMonday To pjFriday
c.WeekDays(wd).Shift1.Start = #6:00:00 AM#
c.WeekDays(wd).Shift1.Finish = #2:30:00 PM#
c.WeekDays(wd).Shift2.Start = #2:00:00 PM#
c.WeekDays(wd).Shift2.Finish = #12:30:00 AM#
c.WeekDays(wd).Shift3.Clear
c.WeekDays(wd).Shift4.Clear
c.WeekDays(wd).Shift5.Clear
Next wd
' create the exceptions week by week
Dim NumWeeks As Long
NumWeeks = (CyclesEnd - CyclesStart) \ 7
Dim i As Long
Dim CycleNum As String
Dim StartTime As Date
For i = 1 To NumWeeks + 1
Select Case i Mod 4
Case Is = 1: StartTime = Week1Start: CycleNum = "1"
Case Is = 2: StartTime = Week2Start: CycleNum = "2"
Case Is = 3: StartTime = Week3Start: CycleNum = "3"
Case Is = 0: StartTime = Week4Start: CycleNum = "4"
Case Else
End Select
On Error Resume Next
'##add an If qualifier to select between two different set conditions?
' Set e = Iif(CycleNum = "1" Or "2", c.Exceptions.Add(Type:=pjDaily _
' , Start:=CyclesStart + (i - 1) * 7 _
' , Occurrences:=5 _
' , Name:=ExceptionName & CycleNum),
' c.Exceptions.Add(Type:=pjDaily _
' , Start:=CyclesStart + (i - 1) * 7 _
' , Occurrences:=5 _
' , Name:=ExceptionName & CycleNum))
'# Set e = c.Exceptions.Add(Type:=pjDaily _
'# , Start:=CyclesStart + (i - 1) * 7 _
'# , Occurrences:=5 _
'# , Name:=ExceptionName & CycleNum)
'# If Err.Number = 0 Then
'# e.Shift1.Start = StartTime
'# e.Shift1.Finish = StartTime + #8:30:00 AM# '-=adjust length of each shift, #X = hours=-
Else
'switch to day-by-day to not overwrite holidays inherited from the Standard calendar
Dim d As Long
For d = 1 To 5
Set e = c.Exceptions.Add(Type:=pjDaily _
, Start:=CyclesStart + (i - 1) * 7 + d - 1 _
, Occurrences:=1 _
, Name:=ExceptionName & CycleNum & " day " & d)
e.Shift1.Start = StartTime
e.Shift1.Finish = StartTime + #6:00:00 AM#
Next d
End If
On Error GoTo 0
Next i
End Sub
My two added non-functional subs
'# Set Parameters for Shift 1
Sub Day_Shift()
Set e = c.Exceptions.Add(Type:=pjDaily _
, Start:=CyclesStart + (i - 1) * 7 _
, Occurrences:=5 _
, Name:=ExceptionName & CycleNum)
If Err.Number = 0 Then
e.Shift1.Start = StartTime
e.Shift1.Finish = StartTime + #8:30:00 AM# '-=adjust length of each shift, #X = hours=-
Else
'switch to day-by-day to not overwrite holidays inherited from the Standard calendar
Dim d As Long
For d = 1 To 5
Set e = c.Exceptions.Add(Type:=pjDaily _
, Start:=CyclesStart + (i - 1) * 7 + d - 1 _
, Occurrences:=1 _
, Name:=ExceptionName & CycleNum & " day " & d)
e.Shift1.Start = StartTime
e.Shift1.Finish = StartTime + #6:00:00 AM#
Next d
End If
End Sub
'# Set Parameters for Shift 2
'name
Sub Night_Shift()
'Start day per cycle, how many per week, and name
Set e = c.Exceptions.Add(Type:=pjDaily _
, Start:=CyclesStart + (i - 1) * 7 _
, Occurrences:=4 _
, Name:=ExceptionName & CycleNum)
If Err.Number = 0 Then
e.Shift2.Start = StartTime
e.Shift2.Finish = StartTime + #10:30:00 AM# '-=adjust length of each shift, #X = hours=-
Else
'switch to day-by-day to not overwrite holidays inherited from the Standard calendar
Dim d As Long
For d = 1 To 5
Set e = c.Exceptions.Add(Type:=pjDaily _
, Start:=CyclesStart + (i - 1) * 7 + d - 1 _
, Occurrences:=1 _
, Name:=ExceptionName & CycleNum & " day " & d)
e.Shift1.Start = StartTime
e.Shift1.Finish = StartTime + #6:00:00 AM#
Next d
End If
End Sub

In this case only one set of exceptions is needed--for the night shifts. However, since those shifts run across midnight the shift needs to be split into 2 parts: 2:00 PM - midnight and midnight - 12:30 AM. This results in the need for 3 exceptions for each of the night shift weeks:
Monday 2:00 PM - 12:00 AM
Tue-Thu 12:00 AM - 12:30 AM and 2:00 PM - 12:00 AM
Friday 12:00 AM - 12:30 AM
Here is updated code that does this:
Sub CreateRotatingCalendar()
' Create a calendar with a cycle of 4 rotating weekly schedules by utilizing the calendar exceptions.
Const CalName As String = "Rotating Shift Alpha" 'name of calendar
Const ExceptionName As String = "Nights "
' Set Calendar start and end dates
Const CyclesStart As Date = #8/1/2022# ' must be a Monday
Const CyclesEnd As Date = #8/8/2032#
' create calendar, but ignore errors in case calendar already exists
On Error Resume Next
BaseCalendarCreate Name:=CalName, FromName:="Standard"
On Error GoTo 0
Dim c As Calendar
Set c = ActiveProject.BaseCalendars(CalName)
' in case this routine is run again to update the rotating calendar, delete exceptions related to the rotation (and leave holidays)
Dim e As Exception
For Each e In c.Exceptions
If e.Name Like ExceptionName & "*" Then
e.Delete
End If
Next e
' remove other shifts and set shift hours
Dim wd As PjWeekday
For wd = pjMonday To pjFriday
c.WeekDays(wd).Shift1.Start = #6:00:00 AM#
c.WeekDays(wd).Shift1.Finish = #2:30:00 PM#
c.WeekDays(wd).Shift2.Clear
c.WeekDays(wd).Shift3.Clear
c.WeekDays(wd).Shift4.Clear
c.WeekDays(wd).Shift5.Clear
Next wd
' create the exceptions week by week
Dim NumWeeks As Long
NumWeeks = (CyclesEnd - CyclesStart) \ 7
Dim WeekNum As Long
For WeekNum = 1 To NumWeeks + 1
If WeekNum Mod 4 = 3 Or WeekNum Mod 4 = 0 Then
' create exceptions for the night shift
On Error Resume Next
Set e = c.Exceptions.Add(Type:=pjDaily _
, Start:=CyclesStart + (WeekNum - 1) * 7 _
, Occurrences:=1 _
, Name:=ExceptionName & " Monday")
e.Shift1.Start = #2:00:00 PM#
e.Shift1.Finish = #12:00:00 AM#
Set e = c.Exceptions.Add(Type:=pjDaily _
, Start:=CyclesStart + (WeekNum - 1) * 7 + 1 _
, Occurrences:=3 _
, Name:=ExceptionName & " Tu-Th")
e.Shift1.Start = #12:00:00 AM#
e.Shift1.Finish = #12:30:00 AM#
e.Shift2.Start = #2:00:00 PM#
e.Shift2.Finish = #12:00:00 AM#
Set e = c.Exceptions.Add(Type:=pjDaily _
, Start:=CyclesStart + (WeekNum - 1) * 7 + 4 _
, Occurrences:=1 _
, Name:=ExceptionName & " Friday")
e.Shift1.Start = #12:00:00 AM#
e.Shift1.Finish = #12:30:00 AM#
End If
On Error GoTo 0
Next WeekNum
End Sub
Note that since the working hours per week switches between 8.5 hours and 10.5 hours, scheduling should be done in hours rather than days. Review the calendar options for the project and change as needed (File: Options: Schedule)

Related

Calculate the number of workdays between two dates in HOURS

I have a code that I found on microsoft website :
Function Work_Days(BegDate As Variant, EndDate As Variant) As Integer
Dim WholeWeeks As Variant
Dim DateCnt As Variant
Dim EndDays As Integer
On Error GoTo Err_Work_Days
BegDate = DateValue(BegDate)
EndDate = DateValue(EndDate)
WholeWeeks = DateDiff("w", BegDate, EndDate)
DateCnt = DateAdd("ww", WholeWeeks, BegDate)
EndDays = 0
Do While DateCnt <= EndDate
If Format(DateCnt, "ddd") <> "Sun" And _
Format(DateCnt, "ddd") <> "Sat" Then
EndDays = EndDays + 1
End If
DateCnt = DateAdd("d", 1, DateCnt)
Loop
Work_Days = WholeWeeks * 5 + EndDays
Exit Function
Err_Work_Days:
' If either BegDate or EndDate is Null, return a zero
' to indicate that no workdays passed between the two dates.
If Err.Number = 94 Then
Work_Days = 0
Exit Function
Else
' If some other error occurs, provide a message.
MsgBox "Error " & Err.Number & ": " & Err.Description
End If
End Function
It works fine, but I would like to get the difference in hours, but it's not working. I Changed the "d" for "h", but instead of giving me the exact hours, it's giving me 24 hours.
When I change "d" for "h" and multiply 1 by 24, and 5 by 24, I dont get the exact difference in hours. This is what I get :
Date1 Date2 DIFFERENCE
2022-05-05 09:05:19; 2022-05-05 15:45:14; 24
it's giving me 24h instead of 6h
The Work_Days function returns the number of weekdays as a whole number. Time is not considered.
This code returns weekday hours between two dates with a time component.
Option Explicit
Function Weekday_Hours(BegDateTime As Variant, EndDateTime As Variant) As Long
' Weekday hours between two dates with a time component
Dim WholeWeeks As Long
Dim DateCurrent As Date
Dim BegDate As Date
Dim EndDate As Date
Dim BegTime As Date
Dim EndTime As Date
Dim EndDays As Long
Dim Week_Days As Long
Dim FirstDay_Hours As Long
Dim LastDay_Hours As Long
Dim FirstDay_Minutes As Long
Dim LastDay_Minutes As Long
' If the weekend, move the start date to Monday 12AM
If Weekday(BegDateTime) = vbSaturday Then ' 7 - Saturday
Debug.Print Weekday(BegDateTime) & ": Saturday"
BegDateTime = DateAdd("d", 1, BegDateTime)
BegDateTime = Format(BegDateTime, "yyyy-mm-dd")
Debug.Print " BegDateTime:" & Format(BegDateTime, "yyyy-mm-dd hh:nn:ss")
End If
If Weekday(BegDateTime) = vbSunday Then ' 1 - Sunday
Debug.Print Weekday(BegDateTime) & ": Sunday"
BegDateTime = DateAdd("d", 1, BegDateTime)
BegDateTime = Format(BegDateTime, "yyyy-mm-dd")
Debug.Print " BegDateTime:" & Format(BegDateTime, "yyyy-mm-dd hh:nn:ss")
End If
BegDate = DateValue(BegDateTime)
Debug.Print " BegDate: " & BegDate
EndDate = DateValue(EndDateTime)
Debug.Print " EndDate: " & EndDate
If BegDate >= EndDate Then
Debug.Print "Adjusted BegDate >= EndDate"
Weekday_Hours = 0
Exit Function
Else ' (BegDate < EndDate)
FirstDay_Hours = DateDiff("h", BegDateTime, BegDate + 1)
Debug.Print " FirstDay_Hours: " & FirstDay_Hours
FirstDay_Minutes = DateDiff("n", BegDateTime, BegDate + 1)
Debug.Print "FirstDay_Minutes: " & FirstDay_Minutes
BegDateTime = DateAdd("d", 1, BegDateTime)
BegDateTime = Format(BegDateTime, "yyyy-mm-dd")
BegDate = DateValue(BegDateTime)
End If
EndTime = TimeValue(EndDateTime)
Debug.Print " BegDateTime: " & Format(BegDateTime, "yyyy-mm-dd hh:nn:ss")
EndDate = DateValue(EndDateTime)
EndTime = TimeValue(EndDateTime)
Debug.Print " EndDateTime: " & EndDate & " " & EndTime
WholeWeeks = DateDiff("w", BegDate, EndDate)
Debug.Print " " & " WholeWeeks: " & WholeWeeks
DateCurrent = DateAdd("ww", WholeWeeks, BegDate)
Debug.Print " DateCurrent: " & DateCurrent
Do While DateCurrent < EndDate
If Weekday(DateCurrent) <> vbSunday And _
Weekday(DateCurrent) <> vbSaturday Then
EndDays = EndDays + 1
Debug.Print " EndDays: " & EndDays
Else
DateCurrent = Format(DateCurrent, "yyyy-mm-dd")
End If
DateCurrent = DateAdd("d", 1, DateCurrent)
Debug.Print " DateCurrent: " & Format(DateCurrent, "yyyy-mm-dd hh:nn:ss")
Loop
Week_Days = WholeWeeks * 5 + EndDays
Debug.Print " Week_Days: " & Week_Days
Debug.Print " Whole day hours: " & Week_Days * 24
BegTime = TimeValue(BegDateTime)
Debug.Print " BegDateTime: " & BegDate & " " & BegTime
LastDay_Hours = DateDiff("h", BegTime, EndTime)
Debug.Print " LastDay_Hours: " & LastDay_Hours
LastDay_Minutes = DateDiff("n", BegTime, EndTime)
Debug.Print " LastDay_Minutes: " & LastDay_Minutes
' Weekday hours
Weekday_Hours = FirstDay_Hours + Week_Days * 24 + LastDay_Hours
Debug.Print " Weekday_Hours: " & Weekday_Hours
Weekday_Hours = (FirstDay_Minutes + LastDay_Minutes) / 60 + Week_Days * 24
Debug.Print " Weekday_Hours: " & Weekday_Hours
End Function
Private Sub Weekday_Hours_test()
Dim BegDateTime As Variant
Dim EndDateTime As Variant
' Whole day hours: 0
' LastDay_Hours: 6
' LastDay_Minutes: 400
' Weekday_Hours: 6 / 7 when using minutes
BegDateTime = "2022-05-05 09:05:19"
EndDateTime = "2022-05-05 15:45:14"
' ?
'BegDateTime = "2022-05-05 15:45:14"
'EndDateTime = "2022-05-05 09:05:19"
' Weekday_Hours: 0
'BegDateTime = "2022-04-30 09:05:19" ' Saturday
'EndDateTime = "2022-05-01 15:45:14" ' Sunday
' FirstDay_Hours: 9
' FirstDay_Minutes: 495
' Whole day hours: 0
' LastDay_Hours: 9
' LastDay_Minutes: 545
' Weekday_Hours: 18 / 17 when using minutes
'BegDateTime = "2022-05-04 15:45:14"
'EndDateTime = "2022-05-05 09:05:19"
' FirstDay_Hours: 15
' FirstDay_Minutes: 895
' Whole day hours: 96
' LastDay_Hours: 15
' LastDay_Minutes: 945
' Weekday_Hours: 126 / 127 when using minutes
'BegDateTime = "2022-05-05 09:05:19"
'EndDateTime = "2022-05-12 15:45:14"
' *** Monday starts at 12AM
' FirstDay_Hours: 24
' FirstDay_Minutes: 1440
' Whole day hours: 48
' LastDay_Hours: 15
' LastDay_Minutes: 945
' Weekday_Hours: 87 / 88 when using minutes)
'BegDateTime = "2022-04-30 09:05:19" ' Saturday
'EndDateTime = "2022-05-05 15:45:14"
' FirstDay_Hours: 15
' FirstDay_Minutes: 895
' Whole day hours: 24
' LastDay_Hours: 15
' LastDay_Minutes: 945
' Weekday_Hours: 54 / 55 when using minutes)
'BegDateTime = "2022-05-05 09:05:19"
'EndDateTime = "2022-05-07 15:45:14" ' Saturday
' *** When time is not entered the default is 12AM ***
' Weekday_Hours: ?
'BegDateTime = "2022-05-05"
'EndDateTime = "2022-05-05"
' One day not two
' FirstDay_Hours: 24
' FirstDay_Minutes: 1440
' Weekday_Hours: 24
'BegDateTime = "2022-05-04"
'EndDateTime = "2022-05-05"
' Three days not four
' FirstDay_Hours: 24
' FirstDay_Minutes: 1440
' Whole day hours: 48
' LastDay_Hours: 0
' LastDay_Minutes: 0
' Weekday_Hours: 72
'BegDateTime = "2022-04-30" ' Saturday
'EndDateTime = "2022-05-05"
If EndDateTime > BegDateTime Then
Debug.Print " Weekday_Hours: " & Weekday_Hours(BegDateTime, EndDateTime)
Else
Debug.Print "?"
End If
End Sub

MS ACCESS VBA, working days function incl. holiday when faling on weekend

When deploying MSDN function for calculating working days, beside a problem with date formatting I found an issue with Holiday count.
Calculation is correct, but only if Holiday is on working day. If it is on a saturday or sunday, it also substract it and produce a false result.
illustration of a false reading
A Function for Workdays:
Public Function Workdays(ByRef startDate As Date, ByRef endDate As Date, Optional ByRef strHolidays As String = "Holidays") As Integer
On Error GoTo Workdays_Error
Dim nWeekdays, nHolidays As Integer
Dim strWhere As String
startDate = DateValue(startDate)
endDate = DateValue(endDate)
nWeekdays = Weekdays(startDate, endDate)
If nWeekdays = -1 Then
Workdays = -1
GoTo Workdays_Exit
End If
strWhere = "[Holiday] >= #" & Format(startDate, "yyyy\/mm\/dd") & "# AND [Holiday] <= #" & Format(endDate, "yyyy\/mm\/dd") & "#"
nHolidays = DCount(Expr:="[Holiday]", Domain:=strHolidays, Criteria:=strWhere)
Workdays = nWeekdays - nHolidays
Workdays_Exit:
Exit Function
Resume Workdays_Exit
End Function
And here is a function for calculating weekdays:
Public Function Weekdays(ByRef startDate As Date, ByRef endDate As Date) As Integer
' Returns the number of weekdays in the period from startDate
' to endDate inclusive. Returns -1 if an error occurs.
On Error GoTo Weekdays_Error
Const ncNumberOfWeekendDays As Integer = 2 'The number of weekend days per week.
Dim varDays As Variant 'The number of days inclusive.
Dim varWeekendDays As Variant 'The number of weekend days.
Dim dtmX As Date 'Temporary storage for datetime.
' If the end date is earlier, swap the dates.
If endDate < startDate Then
dtmX = startDate
startDate = endDate
endDate = dtmX
End If
' Calculate the number of days inclusive (+ 1 is to add back startDate).
varDays = DateDiff(Interval:="d", date1:=startDate, date2:=endDate) + 1
' Calculate the number of weekend days.
varWeekendDays = (DateDiff(Interval:="ww", date1:=startDate, date2:=endDate) _
* ncNumberOfWeekendDays) + IIf(DatePart(Interval:="w", _
Date:=startDate) = vbSunday, 1, 0) + IIf(DatePart(Interval:="w", Date:=endDate) = vbSaturday, 1, 0)
' Calculate the number of weekdays.
Weekdays = (varDays - varWeekendDays)
Weekdays_Exit:
Exit Function
Weekdays_Error:
Weekdays = -1
MsgBox "Error " & Err.Number & ": " & Err.Description, vbCritical, "Weekdays"
Resume Weekdays_Exit
Please advise how to ignore holiday if holiday = 1 or holiday = 7.
Plus, in extended period of time, there could be more than one holidays, falling or not on weekend.
It is much simpler just to loop the dates and count:
Public Function DateDiffWorkdays( _
ByVal datDate1 As Date, _
ByVal datDate2 As Date, _
Optional ByVal booWorkOnHolidays As Boolean) _
As Long
' Calculates the count of workdays between datDate1 and datDate2.
' 2014-10-03. Cactus Data ApS, CPH
Dim aHolidays() As Date
Dim lngDiff As Long
Dim lngSign As Long
Dim lngHoliday As Long
lngSign = Sgn(DateDiff("d", datDate1, datDate2))
If lngSign <> 0 Then
If booWorkOnHolidays = True Then
' Holidays are workdays.
Else
' Retrieve array with holidays between datDate1 and datDate2.
aHolidays = GetHolidays(datDate1, datDate2)
End If
Do Until DateDiff("d", datDate1, datDate2) = 0
Select Case Weekday(datDate1)
Case vbSaturday, vbSunday
' Skip weekend.
Case Else
' Check for holidays to skip.
' Ignore error when using LBound and UBound on an unassigned array.
On Error Resume Next
For lngHoliday = LBound(aHolidays) To UBound(aHolidays)
If Err.Number > 0 Then
' No holidays between datDate1 and datDate2.
ElseIf DateDiff("d", datDate1, aHolidays(lngHoliday)) = 0 Then
' This datDate1 hits a holiday.
' Subtract one day before adding one after the loop.
lngDiff = lngDiff - lngSign
Exit For
End If
Next
On Error GoTo 0
lngDiff = lngDiff + lngSign
End Select
datDate1 = DateAdd("d", lngSign, datDate1)
Loop
End If
DateDiffWorkdays = lngDiff
End Function
and the holidays function:
Public Function GetHolidays( _
ByVal datDate1 As Date, _
ByVal datDate2 As Date, _
Optional ByVal booDesc As Boolean) _
As Date()
' Finds the count of holidays between datDate1 and datDate2.
' The holidays are returned as an array of dates.
' DAO objects are declared static to speed up repeated calls with identical date parameters.
' 2014-10-03. Cactus Data ApS, CPH
' The table that holds the holidays.
Const cstrTable As String = "tblHoliday"
' The field of the table that holds the dates of the holidays.
Const cstrField As String = "HolidayDate"
' Constants for the arrays.
Const clngDimRecordCount As Long = 2
Const clngDimFieldOne As Long = 0
Static dbs As DAO.Database
Static rst As DAO.Recordset
Static datDate1Last As Date
Static datDate2Last As Date
Dim adatDays() As Date
Dim avarDays As Variant
Dim strSQL As String
Dim strDate1 As String
Dim strDate2 As String
Dim strOrder As String
Dim lngDays As Long
If DateDiff("d", datDate1, datDate1Last) <> 0 Or DateDiff("d", datDate2, datDate2Last) <> 0 Then
' datDate1 or datDate2 has changed since the last call.
strDate1 = Format(datDate1, "\#yyyy\/mm\/dd\#")
strDate2 = Format(datDate2, "\#yyyy\/mm\/dd\#")
strOrder = Format(booDesc, "\A\s\c;\D\e\s\c")
strSQL = "Select " & cstrField & " From " & cstrTable & " " & _
"Where " & cstrField & " Between " & strDate1 & " And " & strDate2 & " " & _
"Order By 1 " & strOrder
Set dbs = CurrentDb
Set rst = dbs.OpenRecordset(strSQL, dbOpenSnapshot)
' Save the current set of date parameters.
datDate1Last = datDate1
datDate2Last = datDate2
End If
lngDays = rst.RecordCount
If lngDays = 0 Then
' Leave adatDays() as an unassigned array.
Else
ReDim adatDays(lngDays - 1)
' As repeated calls may happen, do a movefirst.
rst.MoveFirst
avarDays = rst.GetRows(lngDays)
' rst is now positioned at the last record.
For lngDays = LBound(avarDays, clngDimRecordCount) To UBound(avarDays, clngDimRecordCount)
adatDays(lngDays) = avarDays(clngDimFieldOne, lngDays)
Next
End If
' DAO objects are static.
' Set rst = Nothing
' Set dbs = Nothing
GetHolidays = adatDays()
End Function
without delving into your code, I'd suggest doing a count of holidays in your holiday table that fall on weekends and which also fall inside the range of days you are considering. Subtract that total from an (I presume) otherwise correctly calculated total and you should have the proper adjustment taking weekend holidays into account.
You can use the function below to get the number of working days (excluding public holidays) between two dates.
It requires a table named tbHolidays with a single field named _Date which holds the public holidays.
Public Function WorkingDaysInDateRange(ByVal DateFrom As Date, _
ByVal DateTo As Date, _
Optional ByVal includeStartDate As Long = 0) As Long
On Error GoTo ErrorTrap
'Calculate the number of days
Dim lngTotalDays As Long
lngTotalDays = DateDiff("y", DateFrom, DateTo) + includeStartDate
'Calculate the number of weekend days.
Dim lngWeekendDays As Long
lngWeekendDays = (DateDiff("ww", DateFrom, DateTo) * 2) + _
IIf(DatePart("w", DateFrom) = vbSunday, 1, 0) + _
IIf(DatePart("w", DateTo) = vbSaturday, 1, 0)
'Get Non working days count from tbHolidays excluding weekends
Dim lngHolidays As Long
lngHolidays = DCount("[_Date]", "tbHolidays", _
StringFormat("[_Date] Between #{0}# AND #{1}# AND Weekday([_Date]) Not In ({2}, {3})", Format(DateFrom, "mm/dd/yyyy"), _
Format(DateTo, "mm/dd/yyyy"), _
vbSaturday, vbSunday))
Dim lngWrkDays As Long
lngWrkDays = lngTotalDays - (lngWeekendDays + lngHolidays)
'Return
WorkingDaysInDateRange = lngWrkDays
Leave:
On Error GoTo 0
Exit Function
ErrorTrap:
MsgBox Err.Description, vbCritical
Resume Leave
End Function
The helper StringFormat function:
Public Function StringFormat(ByVal Item As String, ParamArray args() As Variant) As String
Dim idx As Long
For idx = LBound(args) To UBound(args)
Item = Replace(Item, "{" & idx & "}", args(idx))
Next idx
StringFormat = Item
End Function

Message to report the number of days worked

I used below code to be informed if anyone registered in the payroll that he worked less than 30 days. It works great but the issue I have is that the message given contains the name of the employee eg. Nabil Amer worked H days.
Here I want the message to define the number of days instead of the letter "H"
Private Sub Workbook_Open()
On Error Resume Next
Dim EmpName As String
Dim RowNrNumeric As Long
Dim RowNrString As String
Dim CloumnEmpName As String
Dim CloumnNameRemStatus As String
Dim RemStatus As String
Dim Daysworked As String
Sheets("Master Payroll").Select
CloumnEmpName = "C"
CloumnNameRemStatus = "K"
Daysworked = "H"
RowNrNumeric = 2
RowNrString = RowNrNumeric
EmpName = Range(CloumnEmpName + RowNrString).Value
DueDate = Range(CloumnNameDate + RowNrString).Value
RemStatus = Range(CloumnNameRemStatus + RowNrString).Value
Do While EmpName <> ""
If Cells(RowNrNumeric, 8).Value < 30 _
And Not IsEmpty(Cells(RowNrNumeric, 2)) Then
MsgBox "WARNING: " + EmpName + " worked for " + Daysworked + " Days "
Range(Daysworked + RowNrString).Interior.ColorIndex = 3
Range(Daysworked + RowNrString).Select
End If
RowNrNumeric = RowNrNumeric + 1
RowNrString = RowNrNumeric
EmpName = Range(CloumnEmpName + RowNrString).Value
RemStatus = Range(CloumnNameRemStatus + RowNrString).Value
Loop
End Sub
Screen shot:
Change this:
MsgBox "WARNING: " + EmpName + " worked for " + Daysworked + " Days "
To this:
MsgBox "WARNING: " + EmpName + " worked for " & Range(Daysworked & RowNrNumeric).Value & " Days "

Difference between "MM" standing for Months and "mm" standing for minutes when using Format function

My issue is that I am trying to set up a rolling count since our last accident at work. The code below works in a fashion. The rolling count works fine, but there is a problem with removing the "s" from the words (hours, minutes, seconds ect) with the Month and Minute.
Here's my code:
Sub LTI_Sub()
Static etime
'Exit Sub ' Uncomment to Stop
etime = Now() + TimeSerial(0, 0, 1)
Sheets("LTI").Range("Time") = LTI(Sheets("LTI").Range("Last"))
Application.OnTime etime, "LTI_Sub", , True
End Sub
Function LTI(LastLTI As Date)
x = Now() - LastLTI
Yx = Format(x, "yy")
If Yx = 1 Then
YS = ""
Else
YS = "s"
End If
Mx = Format(x, "mm")
If Mx = 1 Then
MS = ""
Else
MS = "s"
End If
Dx = Format(x, "DD")
If Dx = 1 Then
Ds = ""
Else
Ds = "s"
End If
Hx = Format(x, "HH")
If Hx = 1 Then
Hs = ""
Else
Hs = "s"
End If
MMx = Format(x, "MM")
If MMx = 1 Then
MMs = ""
Else
MMs = "s"
End If
Sx = Format(x, "SS")
If Sx = 1 Then
Ss = ""
Else
Ss = "s"
End If
LTI = Format(x, "YY \Y\e\a\r\" & YS & ", mm \M\o\n\t\h\" & MS & ", DD \D\a\y\" & Ds & "," & vbNewLine & "HH \H\o\u\r\" & Hs & ", MM \M\i\n\u\t\e\" & MMs & ", \A\n\d SS \S\e\c\o\n\d\" & Ss)
End Function
Now I'm not sure how VBA knows the difference between mm and MM when it comes to actually formatting the time, but on the lines where Mx and MMx are determined if the "s" is needed, it always treats it as a month value. How do I tell it to be minutes?
There's also a weird "fault" with the line x = Now() - LastLTI (where LastLTI is the date of the last accident). When returned in VBA it comes back with an extra month and day on it, but when done in Excel it returns the correct value. So for example, if it's been exactly 1 day since the lat accident (down to the second), VBA returns the following string: "00 Years, 01 Month, 02 Days, 00 Hours, 00 Minute , 00 Seconds" <-- Notice that the minutes has dropped the S because "Month" is equal to 1.
I hope this explains what I'm trying to achieve!
Thanks in advance
I use a few different date functions including DateDiff which returns the difference between two dates given to a specified interval, as well as DateAdd does the inverse of that by allowing you to add specified intervals to a date value. I also use the TimeValue function which returns only the time portion of the date.
I think this gets what you want, or at least should get you very very close.
Function LTI(LastLTI As Date)
Dim yx As Long
Dim mx As Long
Dim dx As Long
Dim hx As Long
Dim mmx As Long
Dim sx As Long
Dim ys As String
Dim ms As String
Dim ds As String
Dim hs As String
Dim mms As String
Dim ss As String
Dim dtNow As Date
dtNow = Now()
yx = DateDiff("yyyy", dtNow, LastLTI)
ys = IIf(yx = 1, "", "s")
mx = DateDiff("m", DateAdd("yyyy", yx, dtNow), LastLTI)
ms = IIf(mx = 1, "", "s")
dx = Format(dtNow - LastLTI, "dd")
ds = IIf(dx = 1, "", "s")
hx = DateDiff("h", TimeValue(dtNow), TimeValue(LastLTI))
hs = IIf(hx = 1, "", "s")
'compute the remaining minutes not allocated to a whole hour, above:
mmx = Format(TimeValue(dtNow), "n") - Format(TimeValue(LastLTI), "n")
mms = IIf(mmx = 1, "", "s")
' compute the remaining seconds not allocated to a whole minute, above:
sx = Format(TimeValue(dtNow), "ss") - Format(TimeValue(LastLTI), "ss")
ss = IIf(sx = 1, "", "s")
LTI = yx & "\Y\e\a\r\" & ys & ", " & _
mx & "\M\o\n\t\h\" & ms & ", " & _
dx & "\D\a\y\" & ds & "," & vbNewLine & _
hx & "\H\o\u\r\" & hs & ", " & _
mmx & "\M\i\n\u\t\e\" & mms & ", \A\n\d " & _
sx & "\S\e\c\o\n\d\" & ss
End Function
Instead of using Format (expression, "mm") for minutes, try Format (expression, "n").

VBA generating formula via variables

Can someone please help me to fix the formula in the sub. I need to enter dates into it via variables but it always gives me an error '13' data types
I'm talking about the bit:
Cells(5, field).FormulaLocal = "=SUMMEWENNS(Rawdata!K2:K3446;Rawdata!I2:I3446;""bezahlt"";Rawdata!A2:A3446;" >= " & weekstart & "";Rawdata!A2:A3446;" <= " & weekend & "")"
The Sub apart from that formula works.....
Sub get_cal_weeks()
Dim weeks As Integer, i As Integer, col As String, weekstart As Date, weekend As Date, calweeks() As Variant
'start column is D
col = "D"
'get amount of weeks
weeks = countcalweeks()
'populate array calweeks
calweeks = fillcalweeks(weeks)
For i = 0 To weeks
field = i + i + 4
weekstart = calweeks(i, 0)
weekend = calweeks(i, 1)
Cells(5, field).FormulaLocal = "=SUMMEWENNS(Rawdata!K2:K3446;Rawdata!I2:I3446;""bezahlt"";Rawdata!A2:A3446;" >= " & weekstart & "";Rawdata!A2:A3446;" <= " & weekend & "")"
Next
End Sub
Thank you
I suggest you convert to long (or double if you need times)
Cells(5, field).FormulaLocal = "=SUMMEWENNS(Rawdata!K2:K3446;Rawdata!I2:I3446;""bezahlt"";Rawdata!A2:A3446;"">=" & CLng(weekstart) & """;Rawdata!A2:A3446;""<=" & CLng(weekend) & """)"