Getting specific dates from a date range in VBA - vba

I have a sheet that have a range of dates in column A. I already found the way to get the last row with:
LastRow = Worksheets("TIME").Cells(Rows.Count, "A").End(xlUp).Row
Now I am trying to get specific dates. The range contains no weekends, but since the dates are proprietary, I could not use the WorkDay function to find what I need.
Case 1:
From the last date available, I am trying to get the date 1 year before (if the date is not available, pick the next available one).
What I did here was to use the date function and subtract 1 year..
day1Y = date(year(LastRow)-1,month(LastRow),day(LastRow))
To match, I transformed the date range into an array, and used a function do determine if it is in the array. If it is, get it, but if it is not, I don't know how to get the next available.
Dim DateArray() as Variant
Dim WantedDate1 as date
Dim WantedDate2 as date
DateArray() = Worksheets("TIME").Range("A2:A" & LastRow).Value
If IsInArray(day1Y) = True then
WantedDate1 = .Cells(1,LastRow).Value
End if
Case 2:
From the last available date, I am trying to get the first date in the same year (if last date is 10/08/2015, it gets the first available date of 2015, according to the dates available in the range).
WantedDate2 = Year(.Cells(1,LastRow).Value)
I got the year of the last date, but again, I can't find the first date of that year.
Any help will be deeply appreciated.

Use a loop to increase the days 1 by 1 and test if it is the array on the go :
Option Explicit
Sub DGMS89()
Dim wsT As Worksheet
Dim LastRow As Double
Dim DateArray() As Variant
Dim LastDate As Date
Dim Day1y As Date
Dim WantedDate1 As Date
Dim WantedDate2 As Date
Set wsT = ThisWorkbook.Sheets("TIME")
LastRow = wsT.Cells(wsT.Rows.Count, "A").End(xlUp).Row
DateArray() = wsT.Range("A2:A" & LastRow).Value
LastDate = DateArray(UBound(DateArray, 1))
Day1y = DateAdd("yyyy", -1, LastDate)
WantedDate1 = Day1y
If IsInArray(WantedDate1) Then
Else
Do While Not IsInArray(WantedDate1)
WantedDate1 = DateAdd("d", 1, WantedDate1)
Loop
End If
WantedDate2 = DateSerial(year(LastDate), 1, 1)
Do While Not IsInArray(WantedDate2)
WantedDate2 = DateAdd("d", 1, WantedDate2)
Loop
End Sub

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

vba code to find dates and display in another sheet?

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.

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

Excel VBA script for loop with dates

I am calculating the number of work hours (8am to 8pm) between the 2 given dates, excluding Weekends and Public holidays, but my code syntax is incorrect.
Sample data:
Start day: 17/06/2011 08:00:00 AM
End day: 19/06/2011 08:00:00 PM
Sub SLA_Days_Resolved_F()
Dim x As Integer
' Set numrows = number of rows of data.
NumRows = Range("F2", Range("F2").End(xlDown)).Rows.Count
Dim total As Integer 'to count the total hours
Dim st As String 'start date cell
Dim en As String 'end date cell
Dim destCell As String
Dim d As Date ' for the loop
total = 0
' Establish "For" loop to loop "numrows" number of times.
For x = 2 To NumRows + 1
st = "G" & CStr(x) 'reference to the cells
en = "D" & CStr(x)
'loop from start date to end date
For d = Date(Range(st)) To Date(Range(en))
'check if the current date is found is a Public holiday in the range or if a weekend
If ((Vlookup(d,lookups!$o$3:$p$26,2,false))=1) or (weekend(d))Then
'minus 8 to remove hours before 8am.
total = (total + Hour(d) + minutes(d) / 60) - 8
End If
Next
Next
End Sub
You are not assigning any values to variables st or en.
Date is not a function available in VBA. You will probably need to use DateSerial function. Here is a simple example of looping over dates which you should be able to modify.
Sub LoopDates()
Dim d As Date
'Loop the days beteween today and March 1, 2013.
For d = DateSerial(Year(Now), Month(Now), Day(Now)) To DateSerial(2013, 3, 1)
Debug.Print d 'Prints the "d" value in the immediate window.
Next
End Sub
Also, you can't just put worksheet formulae in VBA. This line is definitely wrong syntax for Vlookup, and Weekend is not a formula that I'm aware of (testing it seems to confirm it is not a valid call on worksheet or in VBA.
If ((Vlookup(d,lookups!$o$3:$p$26,2,false))=1) or (weekend(d))Then
Rewrite as:
If Application.WorksheetFunction.Vlookup(d,Sheets("lookups").Range("$o$3:$p$26"),2,false)=1 _
or Not Application.WorksheetFunction.Weekday(d) Then
ANOTHER EXAMPLE of a date loop where I have dimensioned the variables in what I believe to be a more efficient manner:
Sub Test()
Dim st As Range
Dim x As Integer
Dim stDate As Date
Dim enDate As Date
Dim d As Date
Dim numRows as Long
NumRows = Range("F2", Range("F2").End(xlDown)).Rows.Count
For x = 0 To NumRows-2
'SET YOUR VARIABLES HERE
' This may seem redundant or unnecessary for this case, but it makes structuring nested
' loops easier to work with, and then there are fewer places to make changes,
' if you need to make changes.
Set st = Range("G2").Offset(x, 0)
Set en = Range("D2").Offset(x, 0)
stDate = DateSerial(Year(st), Month(st), Day(st))
enDate = DateSerial(Year(en), Month(en), Day(en))
'Then, loop through the dates as necessary
For d = stDate To enDate
Debug.Print d
'Do your code here.
Next
Next
End Sub