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
Related
I am trying to compare Dates in a vba script. I believe the main issue is my formatting however I am not sure how to solve it.
Sub Rem9()
Dim i As Long
Dim lr As Long
Dim ws As Worksheet
Set ws = ThisWorkbook.Worksheets("Sheet1")
wsName = ws.Name
lr = ws.Cells(ws.Rows.Count, "A").End(xlUp).Row
FirstDateRead = CDate("1, 1,2018") 'Initialize the first Day of the year as the last day
For i = 1 To lr
Debug.Print FirstDateRead
Debug.Print ws.Cells(i, 1).Value
If FirstDateRead > ws.Cells(i, 1).Value Then
ws.Cells(i, 3).Value = 121325
End If
Next i
End Sub
According to my output the First Date Read is never greater than the values I am pulling, Which it is for all cases. I have included here an example of the debug.print from the script I am running to show the date formats. Additionally I want to confirm the values I am drawing from are indeed datevaluse as when I run them through the IsDate() Function it returns True.
One other issue if that my date format for the value I call is swapping the year and day. Does anyone know how to solve that. When I use the format function it returns the date as.
Assuming the cells containing the dates are in text format, try wrapping the comparison value in a cDate:
If FirstDateRead > Cdate(ws.Cells(i, 1).Value) Then
ws.Cells(i, 3).Value = 121325
End If
Try using the DateDiff function instead:
Sub dateDifference()
Dim d1 As Date, d2 As Date
d1 = CDate("1, 2,2018")
d2 = Range("A1").Value ' insert a date in A1 to test
Debug.Print DateDiff("d", d1, d2) ' first parameter set to days
End Sub
Edit #1
Use Format to compare apples with apples, so to speak:
d2 = Format(Range("A1").Value, "dd/mm/yyyy")
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
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.
I have data that is similar to:
A1: ID
B1: Start date
C1: End Date
I have another worksheet (call it New) that has
A1: ID and
B1: Date
I need to find out if the date for the ID in New worksheet was already in the previous Worksheet. If the date is start date, end date or anything in between, I want it to show that there is a record that already exist.
Solution here assuming something more practical:
A Master sheet with ID, Start Date, End Date (multiple rows)
Other sheets with ID and Date (multiple rows)
Uses a User Defined Function (UDF) and the ID cell as input
One drawback is that you will need "Calculate Sheet" if other sheets has been updated
Sample screenshots:
Formula for Sheet1 D2: =FindDuplicates(A2)
Code in a Module:
Option Explicit
Function FindDuplicates(oRngID As Range) As String
Dim sID As String, dStart As Date, dEnd As Date, lCount As Long, sWhere As String
Dim oWS As Worksheet, oRngFound As Range, dFound As Date, sFirstFound As String
sID = oRngID.Text
dStart = oRngID.Offset(0, 1).Value
dEnd = oRngID.Offset(0, 2).Value
lCount = 0
sWhere = ""
For Each oWS In ThisWorkbook.Worksheets
' Find all IDs in other worksheeets
If oWS.Name <> oRngID.Worksheet.Name Then
sFirstFound = ""
Set oRngFound = oWS.Cells.Find(What:=sID)
If Not oRngFound Is Nothing Then
sFirstFound = oRngFound.Address
' Keep searching until the first found address is met
Do
' Check the dates, only add if within the dates
dFound = oRngFound.Offset(0, 1).Value
If dStart <= dFound And dFound <= dEnd Then
lCount = lCount + 1
If lCount = 1 Then
sWhere = sWhere & lCount & ") '" & oWS.Name & "'!" & oRngFound.Address
Else
sWhere = sWhere & vbCrLf & lCount & ") '" & oWS.Name & "'!" & oRngFound.Address
End If
End If
Set oRngFound = oWS.Cells.Find(What:=sID, After:=oRngFound)
Loop Until oRngFound.Address = sFirstFound
End If
End If
Next
If lCount = 0 Then sWhere = "Not Found"
FindDuplicates = Replace(sWhere, "$", "") ' Removes the $ sign in Addresses
End Function
So, you have 3 problems:
1) Take value from another worksheet:
Dim startDate As Date
startDate = ActiveWorkbook.worksheets("OtherSheetName").cells(row,col).Value
2) Compare data:
If startDate <= actualDate And actualDate <= endDate Then
...
Else
...
End If
3) Set cell value:
ActiveWorkbook.worksheets("SheetName").cells(row,col).Value = something
Combine those steps and you'll obtain a solution for your problem.
I kinda liked this question...
The way I would do it would be using the SUMPRODUCT() function to check for multiple criteria (there are many references both on this site and Google explaining how that works)
In your New worksheet, assuming the first row is for headers, in cell C2 put in the following formula:
=SUMPRODUCT(--(Sheet1!$A$2:$A$180=A2),--((B2-Sheet1!$B$2:$B$180)>=0),--((Sheet1!$C$2:$C$180-B2)>=0)) > 0
And drag it down for your entire range (Obviously, adjusting the 180 row reference to suit your data-set)
Basically, what you're saying is:
Give me a TRUE only if there is at least one row in my other sheet where:
- The IDs match
- [My Date] minus row's [Start date] >= 0
- Row's [End Date] - [My Date] >= 0
Hope that makes sense!
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