vba code to find dates and display in another sheet? - vba

I have the following code, that isn't quite doing what I want it to
Sub returnDates()
Dim StartD As Date, EndD As Date
StartD = Cells(5, 3)
EndD = Cells(6, 3)
For Row = 1 To (EndD - StartD)
Sheet3.Cells(Row, 3) = StartD + Row - 1
Next Row
End Sub
At the moment it looks for the start and end date on another worksheet. it then returns the dates in-between. At the moment though it doesn't display the start and end date just the ones in-between. I am also having problems specifying the exact cell I want the dates to appear. I am wanting first date to appear in B2

Try this:
Sub returnDates()
Dim StartD As Date, EndD As Date
StartD = Cells(5, 3)
EndD = Cells(6, 3)
For Row = 0 To (EndD - StartD) + 1
Sheet3.Cells(Row + 2, 2) = StartD + Row
Next Row
End Sub

Here I have declared the worksheets to work with and added some comments.
Sub returnDates()
Dim StartD As Date, EndD As Date
Dim lRow As Long
'Create the worksheet objects
Dim ws As Excel.Worksheet
Set ws = Application.ActiveSheet
Dim wsTarget As Excel.Worksheet
Set wsTarget = ActiveWorkbook.Sheets("Sheet3")
'Get the start and end.
StartD = ws.Range("C5")
EndD = ws.Range("C6")
'Loop through and put the dates on the target sheet
For lRow = 1 To (EndD - StartD)
wsTarget.Range("C" & lRow) = StartD + lRow - 1
Next lRow
'Put the end date on the target sheet.
wsTarget.Range("C" & lRow) = EndD
End Sub

Your code at the moment does show the start date but not the end date. I added + 1 to your For loop so that it will get that end date. I have changed you code to put the dates starting from B2 as you specify above.
Sub returnDates()
Dim StartD As Date, EndD As Date
StartD = Cells(5, 3)
EndD = Cells(6, 3)
For Row = 1 To (EndD - StartD) + 1
Sheet3.Cells(Row + 1, 2) = StartD + Row - 1
Next Row
End Sub
Please note that technically it is bad practice to use a variable without dimension / declaring it. You are using Row which is probably a reserved name which is why it doesn't complain. Also note that sheet3 is assumed as the active sheet if you want to use another sheet let me know and I will modify to accommodate it.

Related

VBA Code to loop through a start and and end date and only paste weekdays

I have a code that I have written and its very basic so forgive me. In section 3, i need to basically autofill a date series but excludes weekends based on a start date and end date.
sh9 is the start date and sh10 is the end date so if sh9 was 10/12/2020 and sh10 is 10/22/2020, it would loop through but only paste weekdays for the 9 weekdays that occur between the two dates.
'3. Same date, Start time to End time 1 by 1
ws2.Range("BA" & rLoop + lr2).Value = ((sh9) + rLoop) + (Sh14)
ws2.Range("BB" & rLoop + lr2).Value = ((sh9) + rLoop) + (Sh15)
This is what the code might look like:-
Sub WriteWorkdayDates()
' 107
Dim Ws2 As Worksheet
Dim MyDate As Variant
Dim EndDate As Variant
Dim R As Long ' row number
Dim C As Long ' column number
Set Ws2 = ActiveSheet
R = 9
With Ws2
MyDate = .Cells(R, "SH").Value
EndDate = .Cells(R + 1, "SH").Value
If IsDate(MyDate) And IsDate(EndDate) Then
C = Columns("BA").Column
Do
MyDate = MyDate + 1
If MyDate > EndDate Then Exit Do
If WorksheetFunction.Weekday(MyDate) <> vbSunday And _
WorksheetFunction.Weekday(MyDate) <> vbSaturday Then
.Cells(R, C).Value = MyDate
C = C + 1
End If
Loop
End If
End With
End Sub
On review I find that I misunderstood your variables Sh9 and Sh10. Please feel with me on this subject because the same thing will happen to you when you review your code a year from now. That's why they always recommend to give "meaningful names". However, once you get used to my fetish for avoiding syntax intended for addressing ranges for the purpose of addressing cells you should find it easy to bend my code to your intentions.

VBA: find second largest value

I have the following problem: I try to filter a date column (A) in a worksheet (HISTORICALS) to return the highest and second highest date value. Currently, there is dates ranging from the 25th to the 31st of December in this column. Unfortunately, below formula (using the Large function) returns the 31st two times (and not the 30th and 31st as intended).
Sub Select_Last_Two_Days()
With Worksheets("HISTORICALS")
Highest_Max = Format(WorksheetFunction.Large(Worksheets("HISTORICALS").Range("A:A"), 1), "Short Date")
Second_Highest_Max = Format(WorksheetFunction.Large(Worksheets("HISTORICALS").Range("A:A"), 2), "Short Date")
Debug.Print Highest_Max, Second_Highest_Max
End With
End Sub
The column has approx. 2000 rows, with dates occuring multiple times. So ideally I would want to filter for distinct values and then return the two highest dates. Any idea how I can do that?
Simply translate Barry Houdinis answer from How to find the first and second maximum number? to VBA:
Sub Select_Last_Two_Days()
With Worksheets("HISTORICALS")
Highest_Max = Format(WorksheetFunction.Max(.Range("A:A")), "Short Date")
Second_Highest_Max = Format(WorksheetFunction.Large(.Range("A:A"), WorksheetFunction.CountIf(.Range("A:A"), WorksheetFunction.Max(.Range("A:A"))) + 1), "Short Date")
Debug.Print Highest_Max, Second_Highest_Max
End With
End Sub
The recommendations given in the comments are probably the simplest and least amount of code way to do things but here is another sugggestion:
Sub test()
Dim wb As Workbook
Dim ws As Worksheet
Set wb = ThisWorkbook
Set ws = wb.Worksheets("HISTORICALS")
Dim lastRow As Long
lastRow = ws.Cells(ws.Rows.Count, "A").End(xlUp).Row
Dim loopArr()
loopArr = ws.Range("A1:A" & lastRow).Value
Dim maxVal As Date
maxVal = Application.WorksheetFunction.Large(ws.Range("A1:A" & lastRow), 1)
Dim i As Long
Dim secondVal As Date
For i = UBound(loopArr, 1) To LBound(loopArr, 1) Step -1
If loopArr(i, 1) < maxVal Then
secondVal = loopArr(i, 1)
Exit For
End If
Next i
End Sub

How to debug my simple Excel VBA macro?

I'm just trying to check the latest Date cell, which is in Column A, against the current date. If the difference is 30 days, I write a new row.
When I execute, it says I can't call CheckAttendance on Sheet("Occurences"). But why?
Option Explicit
Public LastCell As Long
Public today As Date
Function CheckAttendance()
Dim DaysSinceOcc As Integer
'returns last occupied row
LastCell = Cells(Rows.Count, 1).End(xlUp).Row
'gets current date
today = Date
'subtracts last cell in specified column from today's date.
DaysSinceOcc = today - Cells(LastCell, 1).Value
'writes what I want written in the cells I want it written in.
If DaysSinceOcc > 29 Then
Cells(LastCell, 1).Offset(1, 1) = "winback"
Cells(LastCell, 1).Offset(1, 2) = -0.5
Cells(LastCell, 1).Offset(1, 4) = "Earned back 0.5 pts for 30 days perfect attendance (AutoGenerated)"
Cells(LastCell, 1).Offset(1, 5) = "AUTO"
Cells(LastCell, 1).Offset(1, 0) = today
Else
End If
End Function
Sub Attendance()
Sheets("Occurences").CheckAttendance
'yes Occurences is suppose to be spelled like that (don't ask)
End Sub
edit: there are probably multiple problems with this. I fixed a lot of things, but then got stuck.
It seems that you want to parametrize your CheckAttendance routine so that it can be called on different worksheets. To do that, make it a Sub that takes a worksheet as parameter. Moreover
qualify you cells and ranges in the code
dim your variables and use Option Explicit
Option Explicit
Sub CheckAttendance(ws As Worksheet)
Dim DaysSinceOcc As Long, lastRow As Long, today As Long
lastRow = ws.Cells(ws.Rows.count, 1).End(xlUp).row
'gets current date
today = Date
'subtracts last cell in specified column from today's date.
DaysSinceOcc = today - ws.Cells(lastRow, 1).Value2
'writes what I want written in the cells I want it written in.
If DaysSinceOcc > 29 Then
ws.Cells(lastRow + 1, 1) = today
ws.Cells(lastRow + 1, 2) = "winback"
ws.Cells(lastRow + 1, 3) = -0.5
ws.Cells(lastRow + 1, 5) = "Earned back 0.5 pts for 30 days perfect attendance (AutoGenerated)"
ws.Cells(lastRow + 1, 6) = "AUTO"
End If
End Sub
Sub Attendance()
CheckAttendance Sheets("Occurences") ' <-- this is how you call it on any worksheet
End Sub

Delete rows based on comparing cells to user entered date and skipping blank cells

I am looking for a way to remove rows based on the termination date of an employee. I don't want blank cells to be deleted because those employees are still active. I have a text box that pops up and asks for the dates and then show the entered date. Then it is supposed to search column G for any dates prior to the entered date and delete those rows, skipping any row that is blank.
I have been searching everywhere for a way to do this but I can't get the Macro to stop deleting every row but the headers. The dates are in column G and it's about 46 rows but that can change.
Sub DateSelectandClean()
'
' DateSelectandClean Macro
' User enters date and spreadsheet deletes everything prior to that date, ignoring empty cells.
Application.ScreenUpdating = False
Dim W2Year As Date, N As Long
Dim dt As Date
W2Year = CDate(Application.InputBox(Prompt:="Please enter W2 Year as xx/xx/xxxx Date:", Type:=2))
MsgBox W2Year
N = Cells(Rows.Count, "G").End(xlUp).Row
For i = N To 2 Step -1
dt = Cells(i, 1).Value
If (Cells(i, 1).Value <> "" And dt < W2Year) Then
Cells(i, 1).EntireRow.Delete
End If
Next i
Application.ScreenUpdating = True
End Sub
Sample Data
The main issue is you're checking the "A" column for your date info, and deleting based on that. If your dates are in "G", you should check Cells(x,7), not Cells(x,1).
Sub DateSelectandClean()
'
' DateSelectandClean Macro
' User enters date and spreadsheet deletes everything prior to that date, ignoring empty cells.
Application.ScreenUpdating = False
Dim W2Year As Date, lastRow As Long, i As Long, dateCol As Long
Dim dt As Date
dateCol = 7 ' for column G
Do While W2Year = "00:00:00"
W2Year = Format(Application.InputBox(Prompt:="Please enter W2 Year as xx/xx/xxxx Date:", Type:=2), "mm/dd/yyyy")
MsgBox W2Year
Loop
lastRow = Cells(Rows.Count, dateCol).End(xlUp).Row
For i = lastRow To 2 Step -1
'If Cells(i, dateCol).Value <> "" Then
If IsDate(Cells(i,dateCol)) Then
dt = CDate(Format(Cells(i, dateCol).Value, "mm/dd/yyyy"))
If dt <= W2Year Then
Cells(i, dateCol).EntireRow.Delete
End If
End If
Next i
Application.ScreenUpdating = True
End Sub
I also change the variables from Date to String which allows a little bit of error catching when the user inputs info. You can edit that back if you wish, I was just thinking of a time when someone puts in "wrong" or incorrectly formatted info.
Your code might be getting an issue in blank date.
I separate the IF so that it won't continue the validation on date.
e.g. IF "" < #01/01/2017#
Try this, not much changes though:
Sub DateSelectandClean()
' DateSelectandClean Macro
' User enters date and spreadsheet deletes everything prior to that date, ignoring empty cells.
Application.ScreenUpdating = False
Dim dateCol, iRow
Dim W2Year As Date, N As Long
Dim dt As String
W2Year = CDate(Application.InputBox(Prompt:="Please enter W2 Year as dd/mm/yyyy Date:", Type:=2))
MsgBox W2Year
N = Cells(Rows.Count, "G").End(xlUp).Row
dateCol = 7
For iRow = N To 2 Step -1
dt = Cells(iRow, dateCol).Value
If (Cells(iRow, dateCol).Value <> "") Then
If (CDate(dt) < CDate(W2Year)) Then
Cells(iRow, dateCol).EntireRow.Delete
End If
End If
Next
Application.ScreenUpdating = True
End Sub

Calculating Due Dates based on Frequency using VBA

So, right now I have this Excel sheet where there is a last revision date. I have named this column "LastRevisionDate". And then I have a column named "RevisionFrequency" . The "RevisionFrequency" contains a drop-down menu (data validation) consisting of terms, Annually, Semi-Annually, and Quarterly. And then I have a column where it states the "NextRevisionDate".
So I want to write some VBA code that would calculate the NextRevisionDate from the LastRevisionDate and the RevisionFrequency.
For example. Say in column "A" I have the RevisionFrequency to be Semi-Annually, And the last revision date was Mar-14 in column "B", then I would want the NextRevisionDate in column "C" to state September. That's basically saying that the item gets revised twice a year.
So I would want to create a macro where Column "C" is based off the RevisionFrequency and LastRevisionDate. I realize I could do this with a formula, but I have new items being added constantly so I do not want to keep copying formulas into each cell. Also for some items, they do not need revision, I would also like to have a blank cell if there is no LastRevisionDate.
So far, I have this code:
Private Sub Worksheet_Change(ByVal Target As Range)
Dim ws As Worksheet
Set ws = Sheets(1)
'For this reference of the Column Named LastCalDate I am getting an error
If Not Intersect(Target, ws.Range("LastCalDate").Value) Is Nothing Then
Dim Lastdate As Date
Dim DueDate As Variant
Dim Frequency As String
Dim R As Variant
Dim C As Variant
Dim R1 As Variant
Dim C1 As Variant
Dim R2 As Variant
Dim C2 As Variant
R = Range("LastCalDate").Row
C = Range("LastCalDate").Column
R1 = Range("CalDueDate").Row
C1 = Range("CalDueDate").Column
R2 = Range("CalFrequency").Row
C2 = Range("CalFrequency").Column
Lastdate = Cells(R, C).Value 'Last Cal Date
DueDate = Cells(R1, C1).Value 'Cal Due Date
Frequency = Cells(R2, C2)
If Frequency = "Annually" Then
DueDate = DateAdd("mmm", 12, Lastdate)
End If
If Frequency = "Semi-Annually" Then
DueDate = DateAdd("mmm", 6, Lastdate)
End If
If Frequency = "Quarterly" Then
DueDate = DateAdd("mmm", 3, Lastdate)
End If
End Sub
This is what I have so far. I'm not sure If I am doing this correctly?
Using the Worksheet_Change method is a great way to create the new cell value without having to copy and paste a formula. I included checks in my code as well to make sure if the date or frequency is not set, then the value is cleared out.
Private Sub Worksheet_Change(ByVal Target As Range)
' declare and set worksheet
Dim ws As Worksheet
Set ws = Sheets(1)
' declare and set default date
Dim DefaultDueDate As Date
' declare needed variables
Dim StartDate As Date
Dim Frequency As String
Dim DueDate As Date
' make sure the change only occured on the "A" or "B" column
If Target.Column = 1 Or Target.Column = 2 Then
StartDate = ws.Range("A" & Target.Row)
Frequency = ws.Range("B" & Target.Row)
' if start date does not equal the default due date and the frequency is not blank, set due date variable
If StartDate <> DefaultDueDate And Frequency <> "" Then
' add months to the provided start date
If Frequency = "Annually" Then
DueDate = DateAdd("m", 12, StartDate)
ElseIf Frequency = "Semi-Annually" Then
DueDate = DateAdd("m", 6, StartDate)
ElseIf Frequency = "Quarterly" Then
DueDate = DateAdd("m", 3, StartDate)
End If
' Make sure frequency selection is correct and due date was set
If DueDate <> DefaultDueDate Then
ws.Range("C" & Target.Row) = DueDate
End If
Else
' clear Next Revision Date when Frequency or Start Date is blank
ws.Range("C" & Target.Row) = ""
End If
End If
End Sub