Function that returns next Friday from date - excel

When planning projects we give a approximate duration in weeks. A project starts on the next Monday from today.
Public Function NextMonday() As Date
Dim D As Integer
Dim N As Date
D = Weekday(Now)
N = Now() + (9 - D)
NextMonday = N
End Function
With this code I can assign the right date to a cell (Next Monday). Now i need to get the next Friday 12 weeks from that date. (84 Days)
For this i used the previous code and adjusted it a little.
Public Function NextFriday(AproxDate As Date) As Date
Dim E As Integer
Dim M As Date
E = Weekday(Now)
M = AproxDate + (14 - E)
NextFriday = M
End Function
Now for the main module the code looks like;
Private Sub Worksheet_Activate()
Dim wbCurrent As Workbook
Dim wsCurrent As Worksheet
Dim StartDate As Range
Dim AproxDate As Date
Dim EndDate As Range
Set wbCurrent = ThisWorkbook
Set wsCurrent = wbCurrent.Sheets("Sheet1")
Set StartDate = wsCurrent.Range("C15")
Set EndDate = wsCurrent.Range("C16")
Call NextMonday
StartDate.Value = NextMonday
'Setting Monday 12 weeks from StartDate
AproxDate = NextMonday + 84
Call NextFriday(AproxDate)
EndDate.Value = NextFriday
End Sub
The NextMonday part works fine, but when running the NextFriday i get an "Argument Not Optional" When trying to set the EndDate.Value = NextFriday.
I cant seem to find the problem with this code. Any thoughts?

This is not how you get a value from a function:
Call NextFriday(AproxDate)
EndDate.Value = NextFriday
If you want the value that function NextFriday returns based on (AproxDate) and you want that value in cell EndDate then (instead of both lines above) you would use:
EndDate.Value = NextFriday(AproxDate)
You only use Call with a function if you do not want a value to be returned from it.
Simplified Procedures:
Your procedure are needlessly overcomplicated.
Believe it or not, this is exactly the same as your procedures:
Public Function NextMonday() As Date
NextMonday = Now() + (9 - Weekday(Now))
End Function
Public Function NextFriday(AproxDate As Date) As Date
NextFriday = AproxDate + (14 - Weekday(Now))
End Function
Private Sub Worksheet_Activate()
.Range("C15")= NextMonday
ThisWorkbook.Sheets("Sheet1").Range("C16")= NextFriday(NextMonday + 84)
End Sub
Simplified even further:
Going a step further, this is 1 sub is exactly the same as the 3 procedures above:
Private Sub Worksheet_Activate()
With Sheets("Sheet1")
.Range("C15") = Now() + (9 - Weekday(Now))
.Range("C16") = (.Range("C15") + 84) + (14 - Weekday(Now))
End With
End Sub
One more thing to note, function Now returns the current date+time. Id it's only the date you're interested in, use function Date.

You have 2 mistakes in your Next Friday function.
1) from Monday to Friday in Weekdays is +4 and not +5 (so you get Next Monday = +9 and next Friday = +(9+4) )
2) You took the now() instead of aproxdate in the next friday code
Your code for next friday should be:
Public Function NextFriday(AproxDate As Date) As Date
Dim E As Integer
Dim M As Date
E = Weekday(AproxDate)
M = AproxDate + (13 - E)
NextFriday = M
End Function
Then you can call the function as in the other answer

var d = new Date();
d.setDate(d.getDate() + (5+7 - d.getDay()) % 7);
document.write(d);
This is a Javascript code which will result you next friday, the only condition where it fails if it is a friday.

Related

Divide month in week numbers with VBA

I am aware that this question has been asked in many different forms, but I would like to show my case as I have not found the perfect solution for it.
So, what I need to do is divide every month in 4 or 5 weeks, and type it into the corresponding cells.
Example :
I have tried this sample code written by User : danieltakeshi in this thread :
https://stackoverflow.com/a/47393516/11969596
But it has a flaw in it, for example if you type a date from October 2021 the result outputs 6 weeks which is impossible :
Sub WeeksInMonth()
Dim MonthYear As String, txt As String
Dim InputDate As Date, MonthYearDay As Date
Dim i As Long, intDaysInMonth As Long, j As Long
Dim MyArray As Variant
Dim arr As New Collection, a
ReDim MyArray(0 To 31)
j = 0
InputDate = ("1 / 10 / 2021") ' Date from October
MonthYear = Month(InputDate) & "/" & Year(InputDate)
intDaysInMonth = Day(DateSerial(Year(MonthYear), Month(MonthYear) + 1, 0))
For i = 1 To intDaysInMonth
MonthYearDay = DateSerial(Year(InputDate), Month(InputDate), i)
MyArray(j) = Application.WorksheetFunction.WeekNum(MonthYearDay)
j = j + 1
Next i
ReDim Preserve MyArray(0 To j - 1)
On Error Resume Next
For Each a In MyArray
arr.Add a, CStr(a)
Next
For i = 1 To arr.Count
Debug.Print arr(i)
Next
End Sub
Please help me find a solution, or tell me how I can adapt it to my current situation.
Cordially,
This routine checks for the first and last workingday (monday to friday) and then gives the calendar weeks for that date range
Option Explicit
Public Sub test_getWeeknumbersForMonth()
Dim arr As Variant
arr = getWeekNumbersForMonth("1.10.2021")
Debug.Print "1.10.2021: ", Join(arr, " - ")
arr = getWeekNumbersForMonth("1.1.2022")
Debug.Print "1.1.2022: ", Join(arr, " - ")
End Sub
Public Function getWeekNumbersForMonth(inputDate As Date) As Variant
Dim datStart As Date
datStart = getFirstWorkingDayOfMonth(inputDate)
Dim datEnd As Date
datEnd = getLastWorkingDayOfMonth(inputDate)
Dim arrWeekNumbers As Variant
ReDim arrWeekNumbers(1 To 6) 'max 6 weeks can be returned
Dim i As Long: i = 1
Dim dat As Date
dat = datStart
While dat <= datEnd
arrWeekNumbers(i) = getCalendarWeek(dat)
i = i + 1
dat = DateAdd("ww", 1, dat)
Wend
ReDim Preserve arrWeekNumbers(i - 1)
getWeekNumbersForMonth = arrWeekNumbers
End Function
Private Function getFirstWorkingDayOfMonth(inputDate As Date) As Date
Dim datToCheck As Date: datToCheck = DateSerial(Year(inputDate), Month(inputDate), 1) - 1
Dim isWorkingday As Boolean
Do
datToCheck = datToCheck + 1
isWorkingday = Weekday(datToCheck, vbMonday) <= 5
Loop Until isWorkingday = True
getFirstWorkingDayOfMonth = datToCheck
End Function
Private Function getLastWorkingDayOfMonth(inputDate As Date) As Date
Dim datToCheck As Date: datToCheck = DateSerial(Year(inputDate), Month(inputDate) + 1, 1)
Dim isWorkingday As Boolean
Do
datToCheck = datToCheck - 1
isWorkingday = Weekday(datToCheck, vbMonday) <= 5
Loop Until isWorkingday = True
getLastWorkingDayOfMonth = datToCheck
End Function
Private Function getCalendarWeek(inputDate As Date) As Long
'european iso week - CW 1 = week with first thursday
getCalendarWeek = Application.WorksheetFunction.IsoWeekNum(inputDate)
'use weeknum-function -adjust second parameter to your needs
'https://support.microsoft.com/en-us/office/weeknum-function-e5c43a03-b4ab-426c-b411-b18c13c75340
'getCalendarWeek = Application.WorksheetFunction.WeekNum(inputDate, 2)
End Function
First, some months have dates in six weeks.
Next, VBA natively can't return the correct ISO 8601 weeknumbers:
How to get correct week number in Access
Finally, week numbers don't care about workdays or weekends. If you wish to exclude weeks that don't include specific weekdays, filter on the dates of these.

Calculate Last Working Day of the Previous Month

Hi All can you please help me with a VBA code for Last Working Day, which excludes the weekends only, of the Previous Month?
The one I tried below is giving me only the Last date without the working day.
Range("B8") = Application.WorksheetFunction.EoMonth(Now, -1)
How about:
Sub marine()
Dim dt As Date, dt_LastMonth As Date, dt_LastWorkingDate_LastMonth As Date
Dim wf As WorksheetFunction
Set wf = Application.WorksheetFunction
dt = Now
dt_LastMonth = DateSerial(Year(dt), Month(dt) - 1, 1)
dt_LastWorkingDate_LastMonth = wf.WorkDay(wf.EoMonth(dt_LastMonth, 0) + 1, -1)
MsgBox dt_LastWorkingDate_LastMonth
End Sub
If today is a day in May 2021, the code produces:
Here is a simple function to return the last workday of the previous month.
Private Function LastWorkday() As Date
Dim Fun As Date ' function return value
Fun = DateSerial(Year(Date), Month(Date), 0)
Do
If (Weekday(Fun) < vbSaturday) And _
(Weekday(Fun) > vbSunday) Then Exit Do
Fun = Fun - 1
Loop
LastWorkday = Fun
Debug.Print Format(Fun, "ddd, d mmm yyyy")
End Function

Creating a string of month in a column

I would like to create a date column formatted via "mmm-yyyy" but starting with next year's date, i.e.
Jan-2020
Feb-2020
Mar-2020
Apr-2020
May-2020
Jun-2020
Jul-2020
Aug-2020
Sep-2020
Oct-2020
Nov-2020
Dec-2020
My code only created the same month for 12 times. Can any one help me with this?
My current code
Sub demo()
'month recurring till dec
Dim x As Integer
Dim i As Integer
For x = 1 To 12
For i = 1 To 12
StartDate = (month(x + 1)) & "-" & (Year(Now())) + 1
Cells(i, 1).Value = StartDate
Cells(i, 1).NumberFormat = "mmm-yyyy"
Next i
Next x
End Sub
Write month dates into column
You have several issues here. Basically you aren't incrementing the year (correct: Year(Now) + increment) and you are overwriting each target cell 12-times with the last calculated value.
Working example procedure
Option Explicit ' declaration head of your code module
Sub demo()
With Sheet1 ' << Reference the sheet's CodeName, e.g. Sheet1
Dim repetition As Long ' << Long, provide for counters greater than ~65K
For repetition = 1 To 10 ' << change to ..= 1 to 1 if only one column :-)
Dim mon As Long
For mon = 1 To 12
' calculate month date via DateSerial function (arguments year,month,day)
Dim StartDate As Date
StartDate = DateSerial(Year(Now) + repetition, mon, 1)
' write to fully referenced range - note the prefixed "." referring to the sheet object
.Cells((repetition - 1) * 12 + mon, 1).Value = StartDate
.Cells((repetition - 1) * 12 + mon, 1).NumberFormat = "mmm-yyyy"
Next mon
Next repetition
End With
End Sub

VBA Formula to calculate Hours Worked

I'm still getting the hang of more complex formulas in VBA.
I'm wanting to create a system that can calculate the worked hours for a certain projects. For example, say my shift hours are 6AM-330PM. I start a project at 7AM on 11/14 and end it at 9AM on 11/16.
How would I go about making calculations so that the returned value will be the hours I worked while on the clock, and not a rolling 24-hour calculation? (While also skipping weekends if possible?)
Thanks!! Heres the code that Im trying to use....
Public Function NetWorkHours(dteStart As Date, dteEnd As Date) As Integer
Dim StDate As Date
Dim StDateD As Date
Dim StDateT As Date
Dim EnDate As Date
Dim EnDateD As Date
Dim EnDateT As Date
Dim WorkDay1Start As Date
Dim WorkDay1end As Date
Dim WorkDay2Start As Date
Dim WorkDay2end As Date
Dim Result As Integer
Dim MinDay As Integer
StDate = CDate(dteStart)
EnDate = CDate(dteEnd)
WorkDay1Start = DateValue(StDate) + TimeValue("08:00:00")
WorkDay1end = DateValue(StDate) + TimeValue("17:00:00")
WorkDay2Start = DateValue(EnDate) + TimeValue("08:00:00")
WorkDay2end = DateValue(EnDate) + TimeValue("17:00:00")
If (StDate > WorkDay1end) Then
StDate = DateAdd("d", 1, WorkDay1Start)
End If
If (StDate < WorkDay1Start) Then
StDate = WorkDay1Start
End If
If (EnDate > WorkDay2end) Then
EnDate = DateAdd("d", 1, WorkDay2Start)
End If
If (EnDate < WorkDay2Start) Then
EnDate = WorkDay2Start
End If
StDateD = CDate(Format(StDate, "Short Date"))
EnDateD = CDate(Format(EnDate, "Short Date"))
If StDateD = EnDateD Then
Result = DateDiff("n", StDate, EnDate, vbUseSystemDayOfWeek)
Else
MinDay = (8 * 60) 'Number of minutes of a working day. Change this if you change the start and end times.
'Extract the time from the two timestamps
StDateT = Format(StDate, "Short Time")
EnDateT = Format(EnDate, "Short Time")
'
'Calculate the minutes of the first day and the second one. Don't know what to do yet if the start is after 5pm or the end is before 8am
Result = DateDiff("n", StDateT, TimeValue("17:00:00"), vbUseSystemDayOfWeek)
Result = Result + DateDiff("n", TimeValue("08:00:00"), EnDateT, vbUseSystemDayOfWeek)
'Check if there was a break on both days or not.
If DateDiff("n", StDateT, TimeValue("17:00:00"), vbUseSystemDayOfWeek) > (5 * 60) Then
Result = Result - 60
End If
If DateDiff("n", TimeValue("08:00:00"), EnDateT, vbUseSystemDayOfWeek) > (5 * 60) Then
Result = Result - 60
End If
'Add 1 day to start date. This is to start the loop to get all the days between both dates.
StDateD = DateAdd("d", 1, StDateD)
Do Until StDateD = EnDateD
'If the date is not a saterday or a sunday we add one day.
If (Weekday(StDateD) > 1) And (Weekday(StDateD) < 7) Then
Result = Result + MinDay
'Check for the holiday. If the date is a holiday, then we remove one day
If Not IsNull(DLookup("[HolDate]", "Holidays", "[HolDate] = #" & Int(StDateD) & "#")) Then
Result = Result - MinDay
End If
End If
StDateD = DateAdd("d", 1, StDateD)
Loop
End If
NetWorkHours = Result
End Function
You can use DateDiff to calculate the difference between dates (and times). The following should get you pretty close to what you want to do:
Dim datStart As Date
Dim datEnd As Date
Dim sngShiftStart As Single
Dim sngShiftEnd As Single
Dim sngShiftDuration As Single
Dim lngMinutesWorked As Long
Dim lngOfftime As Long
Dim sngHoursWorked As Single
' Calculate shift length
sngShiftStart = 6
sngShiftEnd = 15.5
sngShiftDuration = sngShiftEnd - sngShiftStart
' Set start and end times
datStart = CDate("11/07/19 7:00")
datEnd = CDate("11/09/19 8:30")
lngMinutesWorked = DateDiff("n", datStart, datEnd)
lngOfftime = ((24 - sngShiftDuration) * 60) * (DateDiff("d", datStart, datEnd))
sngHoursWorked = (lngMinutesWorked - lngOfftime) / 60
MsgBox sngHoursWorked
This does not take into account weekends but you should be able to easily add that. You can check, using the Weekday function, if the Weekday of the Start date is smaller than the End date. In that case, subtract 2 * sngShiftDuration from sngHoursWorked. If your project lasts more than a week, you can look for that and subtract more weekends:
' Remove weekends
Dim sngWeekendHours As Single
If Weekday(datStart) > Weekday(datEnd) Then
' Weekend included
sngWeekendHours = (2 * sngShiftDuration) * (DateDiff("w", datStart, datEnd) + 1)
End If
sngHoursWorked = ((lngMinutesWorked - lngOfftime) / 60) - sngWeekendHours

Calling a sub for different start and end times

#Variatus Ref the macro you have been working on, for a Friday production the shift will still start at 05:30 but end at 18:30 and if Saturday production the shift will start at 07:00 and finish at 14:00. am I correct in thinking I need to add another Enum called Day 5 Nsh with the start and end time and then same again for day 6 calling it Day 6 Nsh. If this is correct then in the sub Setcompletion how would I call this. would it need to be referenced any where else in the macro also?
Enum Nws ' Worksheet navigation
NwsFirstDataRow = 2
NwsQty = 1 ' Columns (not necessarily contiguous):
NwsTime ' time to produce one unit
NwsStart ' date/time
NwsEnd ' date/time
End Enum
Enum Nsh ' Shift (use 24h format)
NshStart = 530 ' read as 05:30 (on current day)
NshEnd = 2430 ' read as 00:30 (on next day)
End Enum
next
Sub SetCompletion(ws As Worksheet, R As Long)
' 25 Mar 2017
Dim Qty As Long
Dim ShiftQty As Long, DayQty As Long
Dim UnitTime As Double, StartTime As Double
Dim ComplDate As Double
Dim Days As Integer
With Rows(R)
Qty = .Cells(NwsQty).Value
UnitTime = .Cells(NwsTime).Value
StartTime = .Cells(NwsStart).Value
If Qty And (UnitTime > 0) And (StartTime > 0) Then
ComplDate = (UnitTime * Qty) + StartTime
ShiftQty = QtyTillShiftEnd(StartTime, UnitTime)
If ShiftQty < Qty Then
Qty = Qty - ShiftQty
DayQty = DailyProduction(UnitTime)
ComplDate = Int(StartTime) + 1 + NshToDays(NshStart) + Int(Qty / DayQty)
ComplDate = ComplDate + UnitTime * (Qty Mod DayQty)
End If
.Cells(NwsEnd).Value = ComplDate
End If
End With
End Sub
next
Private Function QtyTillShiftEnd(ByVal StartTime As Double, _
ByVal UnitTime As Double) As Double
' 20 Mar 2017
Dim ProdTime As Double
ProdTime = (Int(StartTime) + NshToDays(NshEnd) - StartTime)
QtyTillShiftEnd = (ProdTime + 0.0001) / UnitTime
End Function
next
Private Function DailyProduction(UnitTime As Double) As Integer
' 19 Mar 2017
DailyProduction = Int((NshToDays(NshEnd) - NshToDays(NshStart) + 0.000001) / UnitTime)
End Function
next
Private Function NshToDays(TimeCode As Nsh) As Double
' 19 Mar 2017
Dim H As Double, M As Double
H = Int(TimeCode / 100)
M = TimeCode Mod 100
NshToDays = (1 / 24 * H) + (1 / 24 / 60 * M)
End Function
next
Function AdjustedStartTime(ByVal StartTime As Double) As Double
' 19 Mar 2017
' return new StartTime or 0
Dim Fun As Double
Dim StartDate As Long
Dim ShiftStart As Double, ShiftEnd As Double
ShiftStart = NshToDays(NshStart)
ShiftEnd = NshToDays(NshEnd)
StartDate = Int(StartTime)
StartTime = StartTime - StartDate
Fun = StartTime
If ShiftEnd > 1 Then
If StartTime < (ShiftStart - Int(ShiftStart)) Then
If StartTime > (ShiftEnd - Int(ShiftEnd)) Then Fun = ShiftStart
End If
Else
If (StartTime - Int(StartTime)) < ShiftStart Then
Fun = ShiftStart
Else
If StartTime > ShiftEnd Then Fun = ShiftStart + 1
End If
End If
AdjustedStartTime = Fun + StartDate
End Function
Plese set up a named range in your workbook and call it "SpecialDays". I suggest you create a dedicated worksheet to house it and make it accessible throughout the workbook. The picture below illustrates and explains.
The program's logic will be like this:-
There will be no production on any day marked as Holiday
There will be a short shift on any day marked as "Half shift"
There will be a full shift on any day marked as "full shift"
There will be no work on weekends unless the days are marked as Special Days
There will be a full shift on all other days
The important part of this list is in the sequence: whichever condition applies first is the condition that prevails. For example, there will be a full shift on Sunday Dec 31 because the condition for "full shift" is met before that of the day being a Sunday, but if it were also listed as a holiday (in a second row, anywhere in the list) there would be no work because holidays are filtered out first.
In order to connect the range to your code, please add these three lines above the enums on your code sheet.
' a named range of this name must exist:
' it must have 2 columns, first with a date, 2nd with a number as Nsh
Const SpecialDays As String = "SpecialDays"
You can change the name of the range here, but must make sure that you use the same name here and on the worksheet where you name the range.
Now, as you already suggested, the Enum Nsh needs to be extended. Here it is. Just replace the existing with the new, in its entirety (meaning form the declaration to "End Enum".
Enum Nsh ' Shift
' 28 Mar 2017
NshFullShift = 0
NshHalfShift
NshNoShift
NshStart = 530 ' read as 05:30 (on current day)
NshEnd = 2430 ' read as 00:30 (on next day)
NshHalfStart = 700 ' (use 24h format)
NshHalfEnd = 1400
End Enum
The first three enumeration correspond to the numbers you may enter in the "SpecialDays" range's 2nd column.
Of course, the SetCompletion procedure must be modified. It now calculates each day's production individually instead of presuming that every day's output is the same.
Sub SetCompletion(Ws As Worksheet, R As Long)
' 28 Mar 2017
Dim Qty As Long, ShiftQty As Long
Dim CommenceTime As Double, UnitTime As Double
Dim ComplTime As Double
With Rows(R)
Qty = .Cells(NwsQty).Value
UnitTime = .Cells(NwsTime).Value
CommenceTime = .Cells(NwsStart).Value
If Qty And (UnitTime > 0) And (CommenceTime > 0) Then
ComplTime = CommenceTime + (UnitTime * Qty)
Qty = Qty - QtyTillShiftEnd(CommenceTime, UnitTime)
If Qty > 0 Then
Do While Qty > 0
ComplTime = Int(ComplTime) + 1
ShiftQty = DailyProduction(ComplTime, UnitTime)
If Qty > ShiftQty Then
Qty = Qty - ShiftQty
Else
ComplTime = ComplTime + StartTime(ShiftType(ComplTime)) + (Qty * UnitTime)
Exit Do
End If
Loop
End If
.Cells(NwsEnd).Value = ComplTime
End If
End With
End Sub
The daily production is different on different days. The new function therefore receives the date as an argument and calculates the production according to what kind of day it is: Holiday, half-shift or full-shift day. Of course, the same holds true for the calculation on the first day of production which starts at the CommenceTime, not the start of the shift.
Private Function QtyTillShiftEnd(ByVal CommenceTime As Double, _
ByVal UnitTime As Double) As Double
' 28 Mar 2017
Dim ProdTime As Double
Dim ShType As Nsh
ShType = ShiftType(CommenceTime)
ProdTime = Int(CommenceTime) + EndTime(ShType) - CommenceTime
QtyTillShiftEnd = Int((ProdTime + 0.0001) / UnitTime)
End Function
Private Function DailyProduction(ShiftDay As Double, _
UnitTime As Double) As Integer
' 28 Mar 2017
Dim ShType As Nsh
ShType = ShiftType(ShiftDay)
DailyProduction = Int((EndTime(ShType) - StartTime(ShType) + 0.0001) / UnitTime)
End Function
The NshToDays function remains unchanged in principle, but I modified the method by which the parameter is received (ByVal instead of the previous default ByRef).
Private Function NshToDays(ByVal TimeCode As Nsh) As Double
' 28 Mar 2017
Dim H As Double, M As Double
H = Int(TimeCode / 100)
M = TimeCode Mod 100
NshToDays = (1 / 24 * H) + (1 / 24 / 60 * M)
End Function
The next function has undergone a complete overhaul. It will now find the start of the next shift even if the entered start time is several days from where it should be.
Function AdjustedStartTime(ByVal CommenceTime As Double) As Double
' 28 Mar 2017
' return new CommenceTime or 0
Dim StartDate As Long
Dim ShType As Nsh
StartDate = Int(CommenceTime)
' if StartDate isn't a workday, then loop for a workday
Do
ShType = ShiftType(StartDate)
If ShType <> NshNoShift Then Exit Do
StartDate = StartDate + 1
Loop
If StartDate < CommenceTime Then
' StartDate is a workday:
If CommenceTime > (StartDate + EndTime(ShType)) Then
StartDate = StartDate + 1
End If
End If
If StartDate < CommenceTime Then
CommenceTime = CommenceTime - StartDate
CommenceTime = Application.Max(CommenceTime, StartTime(ShType))
Else
CommenceTime = StartTime(ShType)
End If
AdjustedStartTime = CommenceTime + StartDate
End Function
The FormatCells procedure is the sole survivor from March 25. I believe you have taken complete charge of it and know how to modify it to do what you want if it doesn't do so on its own.
There are three more new procedures which you can paste at the bottom of your code sheet (not the one with the even procedure). ShiftType is the function which refers to the SpecialDays and determines which, if any, are the start and end times for the daily shift.
Private Function ShiftType(ByVal ShiftDate As Double) As Nsh
' 28 Mar 2017
Dim Fun As Nsh
Dim Rng As Range, Fnd As Range
Dim Fmt As String
ShiftDate = Int(ShiftDate)
Set Rng = ThisWorkbook.Names(SpecialDays).RefersToRange.Columns(1)
With Rng
Fmt = .Cells(1).NumberFormat
.NumberFormat = "General"
Set Fnd = .Find(What:=ShiftDate, LookIn:=xlFormulas)
.NumberFormat = Fmt
End With
If Fnd Is Nothing Then
Fun = Application.Weekday(ShiftDate)
If (Fun = vbSaturday) Or (Fun = vbSunday) Then
Fun = NshNoShift
Else
Fun = NshFullShift
End If
Else
Fun = CLng(Val(Fnd.Offset(0, 1).Value))
Fun = Application.Min(Fun, NshNoShift)
Fun = Application.Max(Fun, NshFullShift)
End If
ShiftType = Fun
End Function
The last two functions return the StartTime and EndTime depending upon the ShiftType. Note that they return 0 (zero) for off-days.
Private Function StartTime(ShType As Nsh) As Double
' 28 Mar 2017
On Error Resume Next
StartTime = NshToDays(Array(NshStart, NshHalfStart, 0)(ShType))
Err.Clear
End Function
Private Function EndTime(ShType As Nsh) As Double
' 28 Mar 2017
On Error Resume Next
EndTime = NshToDays(Array(NshEnd, NshHalfEnd, 0)(ShType))
Err.Clear
End Function
Basically, your code works as before but it has become a lot more complex, meaning it has a lot more points where something may have gone without sufficient attention. My hope is that you will be able to find the procedure responsible for any fault you might discover and either can fix it yourself. After all, each procedure is rather simple in itself. Good luck!

Resources