I got problem with calendar in VBA. Wants to create a calendar that will show/paint the range of week numbers from 2022 depending on the date entered in columns A22 and B22. The problem occurs when the week numbers repeat between months.
Tydzien = Week
Sty = January
Lut = February
Option Explicit
Sub Kolorowaniedaty()
Dim rok As Integer
rok = Left(Cells(22, 2), 4)
Dim miesiacpocz As Integer
miesiacpocz = Mid(Cells(22, 2), 7, 1)
Dim miesiackon As Integer
miesiackon = Mid(Cells(22, 3), 7, 1)
Dim Datapocz As Integer
Datapocz = Application.WorksheetFunction.WeekNum(Cells(22, 2), 2)
Dim Datakon As Integer
Datakon = Application.WorksheetFunction.WeekNum(Cells(22, 3), 2)
Dim Rokzdaty As String
Rokzdaty = CStr(Mid(Cells(22, 2), 3, 2))
Dim Rok2022 As Byte
Rok2022 = 22
Dim kolumna As Byte
For kolumna = 1 To 20
If Rokzdaty = Rok2022 And miesiacpocz = miesiackon Then
Range(Cells(22, Datapocz + 4), Cells(22, Datakon + 4)).Interior.Color = vbYellow
Else: Range(Cells(22, Datapocz + 4), Cells(22, Datakon + 5)).Interior.Color = vbYellow
End If
Next kolumna
End Sub
I cant upload image of Makro and Calendar from excel cuz i dont have enought points of reputations. If someone can help from private chat i will be really really thankful. Its must have from to my work.
[![enter image description here][3]][3]
Its Its suppose to mark 11 weeks but its show only 10 weeks. Any advice?
[3]: https://i.stack.imgur.com/X8kwQ.png
Iterate over each day in the date range and increment the column number each monday or change of month. Store the column numbers in an array and use it as a lookup to determine the column number for a given date. Run this is a new clean workbook.
update - complete rewrite
Option Explicit
Const START_COL = 4
Const START_ROW = 22
Const MAX_YEARS = 4
Const START_YEAR = 2022
Sub CalendarDemo()
Dim ws As Worksheet
Dim dt As Date, dtDay1 As Date
Dim wkno As Long, dayno As Long
Dim colno As Long, i As Long, c As Long, r As Long
Dim arCol, arDate
ReDim arCol(1 To 2, 1 To MAX_YEARS * 12 * 7)
ReDim arDate(1 To MAX_YEARS * 366, 1 To 5) ' wkno, month no, column, date, dow
' start Jan 1
dtDay1 = DateSerial(START_YEAR, 1, 1)
colno = 1
wkno = 1
i = 1
' iterate through days built look up array
dt = dtDay1
Do While Year(dt) < START_YEAR + MAX_YEARS
arDate(i, 2) = Month(dt)
arDate(i, 5) = Weekday(dt, vbMonday)
If i > 1 Then
' change of week or month
If arDate(i, 5) = 1 Then
wkno = wkno + 1
If (wkno > 52) And (Month(dt) = 1) Then wkno = 1
colno = colno + 1
ElseIf arDate(i, 2) <> arDate(i - 1, 2) Then
colno = colno + 1
End If
End If
' reset wkno to 1 on jan 1st
If wkno >= 52 And arDate(i, 2) = 1 Then wkno = 1
arDate(i, 1) = wkno
arDate(i, 3) = colno
arDate(i, 4) = dt
' fill arCol
arCol(1, colno) = Format(dt, "mmm yyyy")
arCol(2, colno) = wkno
dt = dt + 1
i = i + 1
Loop
' paint cells
Dim lastrow As Long, dtStart As Date, dtEnd As Date
Dim colStart As Long, colEnd As Long, n As Long, m As Long
Set ws = Sheets(1)
Call testdata(ws)
With ws
lastrow = .Cells(.Rows.Count, "B").End(xlUp).Row
For r = START_ROW To lastrow
' check dates are valid
dtStart = .Cells(r, "B")
dtEnd = .Cells(r, "C")
If dtEnd < dtStart Then
MsgBox "End Date before Start Date on row " & r, vbCritical
Exit Sub
ElseIf dtStart < dtDay1 Then
MsgBox "Start Date before 1 Jan " & START_YEAR & " on row " & r, vbCritical
Exit Sub
End If
' calc day number relative to day1
m = DateDiff("d", dtDay1, dtStart, dtDay1) + 1
n = DateDiff("d", dtDay1, dtEnd, dtDay1) + 1
If n > UBound(arDate) Or m > UBound(arDate) Then
MsgBox "Increase MAX_YEARS for row " & r, vbCritical
Exit Sub
End If
' lookup col number
colStart = arDate(m, 3) + START_COL
colEnd = arDate(n, 3) + START_COL
' merge and color
With .Cells(r, colStart)
With .Resize(1, colEnd - colStart + 1)
.Interior.Color = vbYellow
.Borders.LineStyle = xlContinuous
.Merge
End With
.Value = Space(5) & Format(dtStart, "dd mmm") & " - " & Format(dtEnd, "dd mmm yyyy")
End With
Next
End With
' add headers
Call FormatSheet(ws, arCol, arDate, colno)
MsgBox "Generated " & colno & " Columns", vbInformation
End Sub
Sub FormatSheet(ws As Worksheet, arCol, arDate, colno As Long)
Dim c As Long, i As Long, n As Long, dt As Date
' format sheet header rows
With Sheet1
.Rows("10:21").Clear
.Cells.MergeCells = False
With .Range("E20").Resize(2, colno)
.NumberFormat = "#"
.HorizontalAlignment = xlCenter
.Value2 = arCol
End With
' merge months
i = 0
For c = 5 To colno + 4
If .Cells(20, c + 1) = .Cells(20, c) Then
i = i + 1
Else
With .Cells(20, c - i)
Application.DisplayAlerts = False
.Resize(1, i + 1).Merge
Application.DisplayAlerts = True
.Resize(2, 1).Borders(xlLeft).LineStyle = xlContinuous
End With
i = 0
End If
Next
End With
' calendar to check array
For i = 1 To UBound(arDate)
dt = arDate(i, 4) ' date
n = arDate(i, 5) ' weekday
If dt > 0 Then
n = Weekday(dt, vbMonday)
ws.Cells(10 + n, arDate(i, 3) + START_COL) = Day(dt)
End If
' mon,tue,wed
If i < 8 Then
ws.Cells(10 + n, START_COL) = WeekdayName(n)
End If
Next
End Sub
Sub testdata(ws)
With ws
.Cells(22, 2) = "2022-01-01": .Cells(22, 3) = "2022-03-08"
.Cells(23, 2) = "2022-02-01": .Cells(23, 3) = "2022-02-28"
.Cells(24, 2) = "2022-03-01": .Cells(24, 3) = "2022-03-31"
.Cells(25, 2) = "2022-03-15": .Cells(25, 3) = "2022-05-15"
.Cells(26, 2) = "2022-03-15": .Cells(26, 3) = "2024-03-20"
End With
End Sub
Related
My code run perfectly but when it comes to add duration more than 24 hours, the code return time of next day. Please see image:
For instance:
CELL(C3)-0500_1145-DURATION IS 6.45
CELL(D3)-CTC-THE CODE WILL IGNORE AND MOVE TO NEXT CELL
CELL(E3)-0500_1145-DURATION IS 6.45
CELL(F3)-0500_1145-DURATION IS 6.45----TOTAL
DURATION=6.45(C3)+6.45(E3)+6.45(F3)=20.15
CELL(G3) & CELL(I3)-OFF -THE CODE WILL IGNORE AND MOVE TO NEXT CELL
CELL(H3)-1000_1800(ACP)-DURATION IS 8
Although the code calculate the duration right here i.e 8 hours but when the system sum all the duration it should give 28:15 but the system is taking it as next day and return total duration as 4:15.
My issue is that how can i make the system to return 28 hours 15 mins(28:15) iso of 4:15 when duration is more than 24 hours.
Sub CalculateHourly()
Dim j As Long
Dim TextTime, wStart, wStop, midnight As String
Dim TrueTime, Temp As Date
Dim Parts As Variant
Dim lRow As Long
Application.Calculation = xlManual
midnight = "24" & ":" & "00"
'Find the last non-blank cell in column A(1)
lRow = Cells(Rows.Count, 1).End(xlUp).Row
For i = 3 To lRow
For j = 3 To 9
TextTime = ""
'copy content of the cells
TextTime = ThisWorkbook.Application.Sheets("Sheet1").Cells(i, j).Value
'loop only of cell does not contain any text
If InStr(1, TextTime, "CTC", vbTextCompare) = 0 And InStr(1, TextTime, "OFF", vbTextCompare) = 0 And InStr(1, TextTime, "LEAVE", vbTextCompare) = 0 _
And Not IsEmpty(TextTime) Then
Parts = Split(TextTime, "_")
'Left(Parts(0), 2) of 0430-04
'Right(Parts(0), 2) of 0430-30
wStart = Left(Parts(0), 2) & ":" & Right(Parts(0), 2)
'wStop = Left(Parts(1), 2) & ":" & Right(Parts(1), 2)
wStop = Left(Parts(1), 2) & ":" & Mid(Parts(1), 3, 2)
Debug.Print ("test : " & Format(wStart, "h:mm;#"))
'If timeout is less than timein
If wStart > wStop Then
'Add 24 hours and make the diff
TrueTime = 24 + CDate(CDate(CDate(Format(wStop, "h:mm;#")) - CDate(Format(wStart, "h:mm;#"))))
Else
'if timeout greater than timein
TrueTime = CDate(CDate(CDate(Format(wStop, "h:mm;#")) - CDate(Format(wStart, "h:mm;#"))))
End If
**If (Temp + TrueTime) > 24 Then
TrueTime = 24 + Temp + TrueTime**
Else
TrueTime = Temp + TrueTime
End If
Temp = TrueTime
End If
Next j 'move to the number column in the same row
Cells(i, 10).Value = CDate(Format(Temp, "h:mm;#"))
Temp = Temp - Temp
Next i 'move to the next row
End Sub
Use a function like this to format to hours:minutes only:
Public Function FormatHourMinute( _
ByVal datTime As Date, _
Optional ByVal strSeparator As String = ":") _
As String
' Returns count of days, hours and minutes of datTime
' converted to hours and minutes as a formatted string
' with an optional choice of time separator.
'
' Example:
' datTime: #10:03# + #20:01#
' returns: 30:04
'
' 2005-02-05. Cactus Data ApS, CPH.
Dim strHour As String
Dim strMinute As String
Dim strHourMinute As String
strHour = CStr(Fix(datTime) * 24 + Hour(datTime))
' Add leading zero to minute count when needed.
strMinute = Right("0" & CStr(Minute(datTime)), 2)
strHourMinute = strHour & strSeparator & strMinute
FormatHourMinute = strHourMinute
End Function
Sub CalculateHourly2()
Dim j As Long
Dim TextTime As String, wStart As Date, wStop As Date, midnight As String
Dim Parts As Variant
Dim lRow As Long
Dim vArray() As Variant, n As Integer
Application.Calculation = xlManual
'Find the last non-blank cell in column A(1)
lRow = Cells(Rows.Count, 1).End(xlUp).Row
For i = 3 To lRow
n = 0
For j = 3 To 9
TextTime = ""
'copy content of the cells
TextTime = ThisWorkbook.Application.Sheets("Sheet1").Cells(i, j).Value
'loop only of cell does not contain any text
If InStr(1, TextTime, "CTC", vbTextCompare) = 0 And InStr(1, TextTime, "OFF", vbTextCompare) = 0 And InStr(1, TextTime, "LEAVE", vbTextCompare) = 0 _
And TextTime <> "" Then '<~~ Unlike the case where the cell is empty, if you put an empty cell into a variable, it is not empty.
Parts = Split(TextTime, "_")
'Left(Parts(0), 2) of 0430-04
'Right(Parts(0), 2) of 0430-30
wStart = TimeValue(Left(Parts(0), 2) & ":" & Right(Parts(0), 2))
'wStop = TimeValue(Left(Parts(1), 2) & ":" & Right(Parts(1), 2))
wStop = Left(Parts(1), 2) & ":" & Mid(Parts(1), 3, 2) '<~~ Since other characters have been added, the mid sentence must be used.
n = n + 2
ReDim Preserve vArray(1 To n)
vArray(n - 1) = wStart
vArray(n) = wStop
End If
Next j 'move to the number column in the same row
'Cells(i, 10).Value = CDate(Format(Temp, "h:mm;#"))
If n > 0 Then
Cells(i, 10).Value = getTime(vArray)
Cells(i, 10).NumberFormat = "[hh]:mm"
End If
Next i 'move to the next row
End Sub
Function getTime(Other() As Variant)
Dim myTime As Date, s As Date, e As Date
Dim i As Integer
For i = LBound(Other) To UBound(Other) Step 2
s = Other(i)
e = Other(i + 1)
If s > e Then
e = e + 1
End If
myTime = myTime + e - s
Next i
getTime = myTime
End Function
Sheet Image
I have a very large Dataset ~ 100000 rows with 2 columns, I want to calculate a rolling count based on 2 criterias, basically how many times value in col 1 wrt col 2.
Dataset looks like this
I have written the following code
This is partial dataset, actual has 100000 rows, I want the answer in col c
Sub test()
Application.EnableEvents = False
Application.ScreenUpdating = False
Application.Calculation = xlCalculationManual
Dim id, data_week, ans, a As Variant
Dim p As Double
a = 100000
Debug.Print Now()
id = Sheet1.Range("A2:A" & a).Value
data_week = Sheet1.Range("B2:B" & a).Value
ans = Sheet1.Range("c2:c" & a).Value
For p = 1 To a
ans(p, 1) = Application.WorksheetFunction.CountIfs(Sheet1.Range("A2:A" & p + 1), id(p, 1),
Sheet1.Range("b2:b" & p + 1), data_week(p, 1))
Next p
Sheet1.Range("c2:c" & a).Value = ans
Debug.Print Now()
Application.Calculation = xlCalculationAutomatic
End Sub
This is taking awfully long in VBA, wondering if there's a faster way to do it interms optimising the code, appreciate your help.
Try. This takes 3 seconds to run.
Sub test3()
Dim vDB, ans()
Dim Ws As Worksheet
Dim a As Long
Dim i As Long, id, myDay
Dim n As Integer
Set Ws = Sheets(1)
a = 100000
Debug.Print Now()
With Ws
vDB = .Range("a2", .Range("b" & a))
ReDim ans(1 To UBound(vDB, 1), 1 To 1)
id = vDB(1, 1)
myDay = vDB(1, 2)
For i = 1 To UBound(vDB, 1)
If vDB(i, 1) <> "" Then
If id = vDB(i, 1) And myDay = vDB(i, 2) Then
n = n + 1
ans(i, 1) = n
Else
id = vDB(i, 1)
myDay = vDB(i, 2)
n = 1
ans(i, 1) = n
End If
End If
DoEvents
Next
.Range("c2").Resize(UBound(ans)) = ans
End With
Debug.Print Now()
End Sub
The problem is that it deletes values from both rows where the difference occurs.
It should delete values just from the top row where the difference occurs.
So I tried replacing ws.Cells(RowNo, 3) = " " with ws.Cells(FirstDate, 1) = " " but it doesen't do anything.
Any help would be greatly appreciated. Thanks!
Below is the code:
Sub CalculateDate()
Dim Result, RowNo As Long
Dim FirstDate, SecondDate As Date
Dim ws As Worksheet
Set ws = ThisWorkbook.Sheets(1)
RowNo = 2
Do Until ws.Cells(RowNo + 1, 2) = ""
FirstDate = ws.Cells(RowNo, 2)
SecondDate = ws.Cells(RowNo + 1, 2)
If DateDiff("d", FirstDate, SecondDate) < 2 Then
ws.Cells(RowNo, 3) = " "
End If
RowNo = RowNo + 1
Loop
End Sub
KEY:
Red = where difference between 2 dates <2days
Yellow = where the cell value should be blank
Blue = value should be blank Blue = where cells should not be deleted
may be you have to change
If DateDiff("d", FirstDate, SecondDate) < 2 Then
ws.Cells(RowNo, 3) = " "
End If
with
If DateDiff("d", FirstDate, SecondDate) < 2 Then
ws.Cells(RowNo, 3).ClearContents
RowNo = RowNo + 1
End If
I have the follow script to put a list of people with there know skills in an array and then match the first match with a customer with the same skill. Every time it runs the results are the same. I would like to have it be a random order of the array, but keeping the two columns in the array together. How can I shuffle(rearrange) the array that keeps the rows in the array the same? Or would it be better to erase the array, randomly sort the columns and set the array back up?
Sub Assign()
Dim arOne()
ReDim arOne(1000, 15)
Dim o As Integer
Dim p As Integer
Dim StartTime As Double
Dim MinutesElapsed As String
p = 0
o = 0
For i = 2 To 920
If Cells(i, 12).Value <> Cells(i - 1, 12) Then
p = p + 1
arOne(p, 0) = Cells(i, 12).Value
arOne(p, 1) = Cells(i, 13).Value
o = 2
Else
arOne(p, o) = Cells(i, 13).Value
o = o + 1
End If
Next
For i = 2 To 612
For o = LBound(arOne, 1) + 1 To UBound(arOne, 1)
If arOne(o, 0) <> "" Then
iUsed = Application.WorksheetFunction.CountIf(Range("C2:C" & i), "=" & arOne(o, 0))
If iUsed < Application.WorksheetFunction.VLookup(arOne(o, 0), Range("Q2:R62"), 2, False) Then
For j = LBound(arOne, 2) + 1 To UBound(arOne, 2)
If arOne(o, j) = Cells(i, 2).Value Then
Cells(i, 3).Value = arOne(o, 0)
ActiveSheet.Calculate
GoTo NextIR
End If
Next j
End If
End If
Next o
NextIR:
Next i
End Sub
Multiple loops and multiple access to range objects makes your code very, very slow (I don't know if performance is important).
I would read all necessary data to arrays and use filter and rnd to get a random person with the relevant skill:
Option Explicit
Sub PeopleBusiness()
Dim People, Customers, FilterArray
Dim I As Long, Idx As Long
People = Application.Transpose([L2:L920 & "|" & M2:M8])
Customers = Range("A2:C612").Value2
For I = 1 To UBound(Customers, 1)
FilterArray = Filter(People, Customers(I, 2))
If UBound(FilterArray) > -1 Then
Idx = Round(Rnd() * UBound(FilterArray), 0)
Customers(I, 3) = Left(FilterArray(Idx), InStr(1, FilterArray(Idx), "|") - 1)
End If
Next I
Range("A2:C612").Value = Customers
End Sub
I was able to get done what I needed by erasing the array and redimming it after sorting the data based on a rand() number in the table. It takes about 15 minutes to run 7000 assignment but it is a lot better than 7+ hours it takes to do manually.
Sub Assign()
Dim arOne()
ReDim arOne(1000, 15)
Dim o As Integer
Dim p As Integer
Dim StartTime As Double
Dim MinutesElapsed As String
Application.Calculation = xlAutomatic
StartTime = Timer
NextIR:
ReDim arOne(1000, 15)
p = 0
o = 0
QAlr = Sheets("Sheet1").Range("L" & Rows.Count).End(xlUp).Row
For I = 2 To QAlr
If Cells(I, 12).Value <> Cells(I - 1, 12) Then
p = p + 1
arOne(p, 0) = Cells(I, 12).Value
arOne(p, 1) = Cells(I, 13).Value
o = 2
Else
arOne(p, o) = Cells(I, 13).Value
o = o + 1
End If
Next
AQAlr = Sheets("Sheet1").Range("C" & Rows.Count).End(xlUp).Row
AgtLr = Sheets("Sheet1").Range("A" & Rows.Count).End(xlUp).Row
For I = AQAlr + 1 To AgtLr
For o = LBound(arOne, 1) + 1 To UBound(arOne, 1)
If arOne(o, 0) <> "" Then
iUsed = Application.WorksheetFunction.CountIf(Range("C2:C" & I), "=" & arOne(o, 0))
If iUsed < Application.WorksheetFunction.VLookup(arOne(o, 0), Range("Q2:R62"), 2, False) Then
For j = LBound(arOne, 2) + 1 To UBound(arOne, 2)
If arOne(o, j) = Cells(I, 2).Value Then
Cells(I, 3).Value = arOne(o, 0)
ActiveSheet.Calculate
Erase arOne()
ActiveWorkbook.Worksheets("Sheet1").ListObjects("Table1").Sort.SortFields.Clear
ActiveWorkbook.Worksheets("Sheet1").ListObjects("Table1").Sort.SortFields.Add _
Key:=Range("Table1[[#All],[Random '#]]"), SortOn:=xlSortOnValues, Order:= _
xlDescending, DataOption:=xlSortTextAsNumbers
With ActiveWorkbook.Worksheets("Sheet1").ListObjects("Table1").Sort
.Header = xlYes
.MatchCase = False
.Orientation = xlTopToBottom
.SortMethod = xlPinYin
.Apply
End With
GoTo NextIR
End If
Next j
End If
End If
Next o
Next I
MinutesElapsed = Format((Timer - StartTime) / 86400, "hh:mm:ss")
MsgBox "Assignments completed in " & MinutesElapsed & " minutes", vbInformation
End Sub
Not entirely sure I got your set-up right but you can try this:
Option Explicit
Sub Assign()
Randomize
Range("C2", Range("C" & Rows.Count).End(xlUp)).ClearContents
Dim R1 As Range: Set R1 = Range("L2:M920") 'People skill table
Dim R2 As Range: Set R2 = Range("A2:B612") 'Cusotmers skill talbe
Dim D0 As Object: Set D0 = CreateObject("scripting.dictionary")
Dim i As Integer, j As Integer, Rand as Integer
For i = 1 To R2.Rows.Count
Rand = Int(R1.Rows.Count * Rnd + 1)
For j = 1 To R1.Rows.Count
If R1.Cells(Rand, 2) = R2(i, 2) And Not D0.exists(Rand) Then
R2.Cells(i, 2).Offset(0, 1) = R1(Rand, 1)
D0.Add Rand, Rand
Exit For
End If
Rand = (Rand Mod R1.Rows.Count) + 1
Next j
Next i
End Sub
The idea is to check the people skill list starting from a random point and making sure a key is not used twice.
EDIT:
According to your comment I assume a "people / skill" can then be assigned more than once as there are 7000+ customers ?
Code below randomly assign with a fairly good distribution 1500 peoples to 7000 customers in +/- 1 second.
Have a try and see if you can adapt it to your project.
Option Explicit
Sub Assign()
Application.ScreenUpdating = False
Dim Start: Start = Timer
Randomize
Range("C2:C99999").ClearContents
Dim D1 As Object
Dim R1 As Range: Set R1 = Range("L2", Range("M" & Rows.Count).End(xlUp))
Dim R2 As Range: Set R2 = Range("A2", Range("B" & Rows.Count).End(xlUp))
Dim T1: T1 = R1
Dim T2: T2 = R2
Dim T3()
Dim a As Integer: a = 1
Dim i As Integer, j As Integer, k As Integer, Rnd_Val As Integer, j_loop As Integer
For i = 1 To (Int(R2.Rows.Count / R1.Rows.Count) + 1)
Set D1 = CreateObject("scripting.dictionary")
For j = (R1.Rows.Count * i - R1.Rows.Count + 1) To R1.Rows.Count * i
ReDim Preserve T3(1 To j)
Rnd_Val = Int(Rnd * R1.Rows.Count + 1)
For k = 1 To R1.Rows.Count
If T1(Rnd_Val, 2) = T2(j, 2) And Not D1.exists(Rnd_Val) And T3(j) = "" Then
T3(j) = T1(Rnd_Val, 1)
D1.Add Rnd_Val, Rnd_Val
Exit For
End If
Rnd_Val = (Rnd_Val Mod R1.Rows.Count) + 1
Next k
If T3(j) = "" Then
For k = 1 To R1.Rows.Count
If T1(Rnd_Val, 2) = T2(j, 2) Then
T3(j) = T1(Rnd_Val, 1)
Exit For
End If
Rnd_Val = (Rnd_Val Mod R1.Rows.Count) + 1
Next k
End If
a = a + 1
If a > R2.Rows.Count Then GoTo EndLoop
Next j
Set D1 = Nothing
Next i
EndLoop:
Range("C2").Resize(UBound(T3), 1) = Application.Transpose(T3)
Debug.Print Timer - Start
Application.ScreenUpdating = True
End Sub
The target is that I am making a small tool to be made in VBA Excel.
The task description is as follows:
1- Make a function in VBA code which would highlight the fixed holidays in the provided Calendar (New Year 01/01 , Labor Day 01/05 , Christmas Day 25/12 , Christmas Holiday 26/12)
2-Make a function in VBA code which would highlight the floating holidays in the provided Calendar (Easter Monday,Good Friday).
3-The worksheets in the workbook should be hyperlinked through the VBA code to a Business day ( Business days are from "Monday to Friday") , there is a condition here too. If the Business day in future calendar happen to be a Fixed Holiday or the Floating Holiday e.g There is New Year on a Tuesday so there would be a holiday observed, in such scenario the worksheet should not be available for this holiday date. In other words, the worksheets have tasks which are to be performed on Business Days only.So if there is a Holiday (irrespective of Fixed or Floating Holiday) the task worksheet containing the task information would not be available.
My issue is that I dont have much of knowledge in VBA.Through internet searches I have found the functions but how to integrate them to achieve the above?
My code and so far found stuff is following:
Public Sub Worksheet_Change(ByVal Target As Range)
Dim mth As Integer, b As Integer, dt As Integer, M As Integer, x As Integer, _
w As Integer, Y As Integer, Days As Integer, iRow As Integer
Dim dateDay1 As Date, dateLeapYear As Date, calYearCell As Range
Dim ws As Worksheet
Dim monthName(1 To 12) As String, weekDay(1 To 7) As String
On Error GoTo ResetApplication
'will enable events (worksheet change) on error
'check validity of worksheet name:
If Not ActiveSheet.Name = "Calendar" Then
MsgBox "Please name worksheet as 'Calendar' to continue"
Exit Sub
End If
Set ws = Worksheets("Calendar")
'address of cell/range which contains Calendar Year:
Set calYearCell = ws.Range("H7")
'At least one cell of Target is within the range - calYearCell:
If Not Application.Intersect(Target, calYearCell) Is Nothing Then
'turn off some Excel functionality so the code runs faster
Application.DisplayAlerts = False
Application.ScreenUpdating = False
Application.EnableEvents = False
Application.DisplayStatusBar = False
Application.Calculation = xlCalculationManual
If calYearCell = "" Then
MsgBox "Select Year to Generate Calendar"
GoTo ResetApplication
Exit Sub
End If
'clear first 7 columns and any previous calendar:
ws.Range("A:G").Clear
D = 0
'set names of 12 months for the array monthName:
monthName(1) = "January"
monthName(2) = "February"
monthName(3) = "March"
monthName(4) = "April"
monthName(5) = "May"
monthName(6) = "June"
monthName(7) = "July"
monthName(8) = "August"
monthName(9) = "September"
monthName(10) = "October"
monthName(11) = "November"
monthName(12) = "December"
'set names of 7 week days for the array weekDay:
weekDay(1) = "Monday"
weekDay(2) = "Tuesday"
weekDay(3) = "Wednesday"
weekDay(4) = "Thursday"
weekDay(5) = "Friday"
weekDay(6) = "Saturday"
weekDay(7) = "Sunday"
For mth = 1 To 12
'for each of the 12 months in a year
counter = 1
'determine day 1 for each month:
If mth = 1 Then
dateDay1 = "1/1/" & calYearCell
wkDay = Application.Text(dateDay1, "dddd")
If wkDay = "Monday" Then
firstDay = 1
ElseIf wkDay = "Tuesday" Then
firstDay = 2
ElseIf wkDay = "Wednesday" Then
firstDay = 3
ElseIf wkDay = "Thursday" Then
firstDay = 4
ElseIf wkDay = "Friday" Then
firstDay = 5
ElseIf wkDay = "Saturday" Then
firstDay = 6
ElseIf wkDay = "Sunday" Then
firstDay = 7
End If
Else
firstDay = firstDay
End If
'determine number of days in each month and the leap year:
dateLeapYear = "2/1/" & calYearCell
M = month(dateLeapYear)
Y = Year(dateLeapYear)
Days = DateSerial(Y, M + 1, 1) - DateSerial(Y, M, 1)
If mth = 1 Or mth = 3 Or mth = 5 Or mth = 7 Or mth = 8 Or mth = 10 Or mth = 12 Then
mthDays = 31
ElseIf mth = 2 Then
If Days = 28 Then
mthDays = 28
ElseIf Days = 29 Then
mthDays = 29
End If`Else
mthDays = 30
End If
`
'determine last used row:
If mth = 1 Then
iRow = 0
Else
iRow = ws.Cells(Rows.Count, "A").End(xlUp).Row
End If
dt = 1
'maximum of 6 rows to accomodate all days of a month:
For i = 1 To 6
'7 columns for each week day of Monday to Sunday:
For b = 1 To 7
'enter name of the month:
ws.Cells(iRow + 1, 1) = monthName(mth)
ws.Cells(iRow + 1, 1).Font.Color = RGB(0, 0, 200)
ws.Cells(iRow + 1, 1).Font.Bold = True
ws.Range("A" & iRow + 1 & ":G" & iRow + 1).Interior.Color = RGB(191, 191, 191)
ws.Range("A" & iRow + 1 & ":G" & iRow + 1).Borders(xlEdgeTop).LineStyle = XlLineStyle.xlContinuous
'enter week day (Monday, Tuesday, ...):
ws.Cells(iRow + 2, b) = weekDay(b)
ws.Range("A" & iRow + 2 & ":G" & iRow + 2).Font.Bold = True
ws.Range("A" & iRow + 2 & ":G" & iRow + 2).Interior.Color = RGB(0, 5000, 0)
ws.Range("F" & iRow + 2 & ":G" & iRow + 2).Interior.Color = RGB(5000, 0, 0)
'enter each date in a month:
If dt <= mthDays Then
'dates placement for the first row (for each month):
If firstDay > 1 And counter = 1 Then
For x = 1 To 8 - firstDay
ws.Cells(iRow + 2 + i, firstDay + x - 1) = x
Next x
dt = 9 - firstDay
'after placement of dates in the first-row for a month the counter value changes to 2, and then reverts
to 1 for the next month cycle:
counter = 2
w = 1
End If
'dates placement after the first row (for each month):
ws.Cells(iRow + 2 + i + w, b) = dt
dt = dt + 1
End If
Next b
Next i
w = 0
'determine placement of day 1 for each month after the first month:
firstDay = firstDay + mthDays Mod 7
If firstDay > 7 Then
firstDay = firstDay Mod 7
Else
firstDay = firstDay
End If
Next mth
'formatting:
iRow = ws.Cells(Rows.Count, "A").End(xlUp).Row
ws.Range("A" & iRow & ":G" & iRow).Borders(xlEdgeBottom).LineStyle = XlLineStyle.xlContinuous
ws.Range("G1:G" & iRow).Borders(xlEdgeRight).LineStyle = XlLineStyle.xlContinuous
With ws.Range("A1:G" & iRow)
.Font.Name = "Arial"
.Font.Size = 9
.RowHeight = 12.75
.HorizontalAlignment = xlCenter
.ColumnWidth = 9
End With
End If
Application.DisplayAlerts = True
Application.ScreenUpdating = True
Application.EnableEvents = True
Application.DisplayStatusBar = True
Application.Calculation = xlCalculationAutomatic
ResetApplication:
Err.Clear
On Error GoTo 0
Application.EnableEvents = True
End Sub
' for floating holidays
Public Sub floatingholidays(NDow As Date, Y As Integer, M As Integer, _
N As Integer, DOW As Integer)
NDow = DateSerial(Y, M, (8 - weekDay(DateSerial(Y, M, 1), _
(DOW + 1) Mod 8)) + ((N - 1) * 7))
End Sub
'for Easter date determination
Public Sub EasterDate(EasterDate2 As Date, Yr As Integer)
Dim D As Integer
D = (((255 - 11 * (Yr Mod 19)) - 21) Mod 30) + 21
EasterDate2 = DateSerial(Yr, 3, 1) + D + (D > 48) + 6 - ((Yr + Yr \ 4 + _
D + (D > 48) + 1) Mod 7)
End Sub
You will not get a question like this answered here. You specify a large requirement and provide a large chunk of code that does not obviously relate to the requirement.
You must break this question into parts, attempt to solve those parts yourself.
For example:
Public Sub floatingholidays(NDow As Date, Y As Integer, M As Integer, _
N As Integer, DOW As Integer)
NDow = DateSerial(Y, M, (8 - weekDay(DateSerial(Y, M, 1), _
(DOW + 1) Mod 8)) + ((N - 1) * 7))
End Sub
Add some comments to this sub-routine explaining what it does. When you return to this routine in 12 months, will you remember how it works?
Does this sub-routine set NDow to the correct value? Test it using macros like this:
Sub TestFH()
Call TestFHSub(2014, 1, 14, 5)
Call TestFHSub(2013, 1, 10, 1)
Call TestFHSub(2013, 2, 6, 2)
Call TestFHSub(2013, 5, 7, 3)
End Sub
Sub TestFHSub(ByVal Y As Integer, ByVal M As Integer, ByVal N As Integer, ByVal DOW As Integer)
Dim NDow As Date
Call floatingholidays(NDow, Y, M, N, DOW)
Debug.Print "If Y=" & Y & " M=" & M & " N=" & N & " DOW=" & DOW & " Then NDow=" & NDow
End Sub
I doubt the values I used in my calls of TestFHSub are sensible. Replace them with a good selection of values so you are convinced this routine works as required. If you need help ask a question about floatingholidays.
Do the same EasterDate.
Next think about how to call routine. Placing this code in a Worksheet_Change routine means it will be called every time you switch worksheet.
Discard the On Error code which just makes debugging more difficult. Consider adding it at the end of development if there is a need. There probably will not be a need.
Discard Application.DisplayAlerts = False etc. Do not worry about the speed of the macro until you have got the code working.
MonthName is a VBA function so you do not need the monthName array.
WeekdayName is a VBA function so you do not need the weekDay array.
Build your macro a few statements at a time and check they are having the effect you seek. If small block of code does not give the effect you seek, ask a question about it.
Good luck.