VBA - Date Reference - vba

I am new to VBA and have a pretty simple question. I built a macro that updates a data set periodically and one of the functions of the macro is that it checks to make sure that if there is data for a previous date that it replaces it.
It uses a cell reference to cross check the data set, however I found that if the date is nor formatted the same then it will not replace it. Ex. if 03/07/18 then it will not replace 03/07/2018.
Any ideas on what I could possibly do to circumvent this issue? Thank you in advance!
Here is the code I have for this part of the macro
Dim i As Integer, ValueToFind As Integer, LRow As Integer
intValueToFind = Sheet8.Range("L6")
Sheet3.Activate
LRow = Range("J" & Rows.Count).End(xlUp).Row
For i = 1 To LRow
If Cells(i, 10).Value = intValueToFind Then
MsgBox ("Found value on row " & i)

I might do something like this:
Dim i As Long, dateToFind As Date, LRow As Long, checkValue As Variant
dateToFind = CDate(Sheet8.Range("L6").Value)
Sheet3.Activate
LRow = Range("J" & Rows.Count).End(xlUp).Row
For i = 1 To LRow
checkValue = Cells(i, 10).Value
If IsDate(checkValue) Then
If dateToFind = CDate(checkValue) Then
MsgBox ("Found value on row " & i)
End If
End If
Next
It seems like you were declaring everything to be an integer, when it's not really. Also, I use Long data types out of habit, which is why I changed your two Integers (that ARE integers) to Longs.
VBA has a built in "Date" data type, which you can read more about here. Basically, we're assigning the date to dateToFind (a Date data type), and we're using the function CDate to force the value of cell L6 to be a Date data type.
Then, we're looping through your cells, and assigning the value to a variant. We're then checking that value to see if it's "like" a date, as determined by VBA's IsDate function. If it is, we'll force it into a Date data type with CDate, and check it against dateToFind.
Note, we had to check if the value was a date with IsDate before using CDate because if the cell value is NOT in a date format, forcing it to be a Date type will throw an error. We didn't check the first time because we can be reasonably confident that the specific date cell you're using (L6) is, in fact, a Date.
Hopefully that works for you.

Did you try to clear and reapply date format ?
Something like this :
Dim i As Integer, ValueToFind As Integer, LRow As Integer
'Clear and apply a date format
Sheet8.Range("L6").ClearFormats
Sheet8.Range("L6").NumberFormat = "yyyy/mm/dd"
Columns(10).Select
Columns(10).ClearFormats
Columns(10).NumberFormat = "yyyy/mm/dd"
intValueToFind = Sheet8.Range("L6")
Sheet3.Activate
LRow = Range("J" & Rows.Count).End(xlUp).Row
For i = 1 To LRow
If Cells(i, 10).Value = intValueToFind Then
MsgBox ("Found value on row " & i)

Related

Date Comparison Issue VBA

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")

Formatting a cell value to "MMMM" using excel vba

I have the below code that I need some assistance modifying.
Sub CopyDataBasedOnTimeRangeMonth()
Dim i, LastRow
Dim Cell As Range
LastRow = Sheets("OPA").Range("A" & Rows.Count).End(xlUp).Row
Sheets("Sheet2").Range("A3:U500").ClearContents
For i = 2 To LastRow
If Sheets("OPA").Cells(i, "G").Value >= Range("U1") And Sheets("OPA").Cells(i, "G").Value < Range("AC1") Then
Sheets("OPA").Cells(i, "R").EntireRow.Copy Destination:=Sheets("Sheet2").Range("A" & Rows.Count).End(xlUp).Offset(1)
End If
Next i
End Sub
For the calculation of "G" I want to use the format of "MMMM" of the value being calculated. In an excel formula I can use something like Text("G12","MMMM") and then continue with the formula but I don't know how to modify the about code to just to use the Month only value of "G".
Thanks in advance for any help you can provide.
It would be best to have [U1] as a date as well, then formatted as month
If Month(Sheets("OPA").Cells(i, "G")) >= Month(Range("U1"))
you can see below the variant of your code, which has been updated using variant provided by Davesexcel (+1), and also some correction from my side, just to simplify readability:
1) absolute reference to Range() replaced to [] shorthand method;
2) removed Destination:= as excessive, also destination range replaced by row, because when you copy the row then destination shall be the row;
3) applied with (object) method;
4) added type of the variables, e.g. i replaced by i& (means i as long)
Sub CopyDataBasedOnTimeRangeMonth()
Dim i&, LastRow&, Cl As Range
LastRow = Sheets("OPA").Range("A" & Rows.Count).End(xlUp).Row
Sheets("Sheet2").[A3:U500].ClearContents
With Sheets("OPA")
For i = 2 To LastRow
If Month(.Cells(i, "G")) >= Month(.[U1]) And _
Month(.Cells(i, "G")) < Month(.[AC1]) Then
.Rows(i).Copy Sheets("Sheet2").Rows(Sheets("Sheet2").Range("A" & Rows.Count).End(xlUp).Row + 1)
End If
Next i
End With
End Sub
tested, works fine.
source:
destination:

Create a list of dates and avoid variable not set (VBA error 91)

I have financial data about certain stocks in five sheets and am trying to create a function which will calculate the exponential moving average of a given range.
[columns(1) = date ; columns(2) = closing price]
The arguments of this function are the number of days taken into account to calculate the EMA, and an integer kol to calculate several EMAs on several columns, side by side (no use for now). Here is my code so far:
Public Function MME(Lmme As Double, kol As Long)
Dim Cmme As Range
Dim Todate, rcell As Range
Dim alpha, period, Udate, i, j, k As Long
Dim Ustock As String
Dim wsDest As Worksheet
Udate = ThisWorkbook.Worksheets("UserForm").Range("B2").Value
period = ThisWorkbook.Worksheets("UserForm").Range("B3").Value
Ustock = ThisWorkbook.Worksheets("UserForm").Range("B4").Value
' MsgBox (Udate)
Set wsDest = ThisWorkbook.Sheets(Ustock)
wsDest.Activate
With wsDest.Range("A2:A392")
Set Todate = Cells.Find(What:=Udate, _
LookIn:=xlValues, _
LookAt:=xlWhole, _
SearchOrder:=xlByRows, _
SearchDirection:=xlNext, _
MatchCase:=False)
If Todate Is Nothing Then
MsgBox ("todate wasn't found")
Else
End If
End With
i = Todate.Row
j = i + period
k = i - Lmme
Set Cmme = Range(Cells(i, 9 + kol), Cells(j, 9 + kol))
alpha = (2 / (Lmme + 1))
With Cmme
For Each rcell In Cmme
If rcell.Row <> i Then
rcell.Formula = "=B" & rcell.Row & "*" & alpha & "+I" & rcell.Row - 1 & "*" & 1 - alpha & ""\
Else: rcell.Formula = "=AVERAGE(B" & k & ":B" & i & " ) "
End If
Next rcell
End With
End Function
I created a list on a separate sheet which allows the user to select a date in 2008, and another which lets him select a Stock. So I did set new variables in order to do the trick but it doesn't work.
Usaction, USdate and Uperiod are the name ranges in which the values selected by the user are stored. But I got "error 91 or object required" on the set = period.
I really want the EMA to be calculated only for a specific period, starting the date selected.
EDIT: I updated the code with the latest version i have. I still have an error 91 on endate
EDIT2: Code updated. I don't understand why the date is not found. On the sheet UserForm the date selected by the user is in "B2" (USdate). It is in format general, but with the CDate in the find function it should be considered a date right? I tried with the date format, it didn't change anything ...
EDIT3: Thanks to Branislav I managed to make the find works by switching every date to General format. Since the Find is working, anyway to make it work using date format? So that the user can see actual date, instead of the integer associated.
Another question: How can i bypass the Cells.Formula to operate directly within vba, and makes it so formula shows in the formula bar in excel once the code ran, except the result of SMAs and EMAs operation within the range?
ToDate is already a range
Set Endate = Todate.Row + period
Also, before you get to that point, you set ToDate by using .Find(). Since it's entirely possible that someone would enter an invalid date or a date that you don't have data for, I'd strongly recommend adding:
if ToDate is Nothing then
'do some date not found stuff here
else
'do your date found stuff here
End If
You may also want to consider changing LookIn:=xlFormulas to LookIn:=xlValues because I believe you're looking for a cell value, not a cell formula.

Extremely slow VBA code when formatting cells

When attempting to format 2 columns in a workbook, the execution of the macro is extremely slow. To format approximately 4000 rows, it takes over 10 minutes.
The dates are populated from an external source that stores them as strings.
When commenting the code, it loads under 60 seconds.
The code
'Discover last row of data
RowsToProcess = Range("A" & Rows.Count).End(xlUp).Row
For i = 6 To RowsToProcess
Worksheets("Data").Range("B" & i).Select
Selection.NumberFormat = "dd/mm/yy;;"
Selection.Value = CDate(Selection.Value)
Worksheets("Data").Range("C" & i).Select
Selection.NumberFormat = "dd/mm/yy;;"
Selection.Value = CDate(Selection.Value)
Next i
The code below does not format cells in the required format either.
Worksheets("Data).Columns("C").NumberFormat = dd/mm/yy;;"
The post #aelgoa linked to is spot on. When the standard Application.ScreenUpdating options for speeding up your code aren't enough, I turn to Variant arrays.
(If you wanted to see how I use Application.ScreenUpdating etc., wrapped in a GoFast function, check out my answer here: VBA code optimization)
The script below works like this:
Load the Range defined in columns B and C into a Variant array
Apply CDate logic there (rather than accessing the Sheet every time)
Write the CDate-modified array out to Sheet
One caveat though -- my question in the comment above about differentiating between mm/dd and dd/mm (say May 6th, 2014 vs June 5th, 2014) still stands. I'll modify the code below based on your thoughts there. Thanks!
Option Explicit
Sub ProcessDates()
Dim AryColBandC As Variant
Dim DateFormatB As Date, DateFormatC As Date
Dim RngColBandC As Range
Dim LastRow As Long, Counter As Long
Dim MySheet As Worksheet
'set references up-front
Set MySheet = ThisWorkbook.Worksheets("Sheet1")
With MySheet
LastRow = .Range("A" & .Rows.Count).End(xlUp).Row
Set RngColBandC = .Range(.Cells(6, 2), .Cells(LastRow, 3))
End With
'load the B-C column range into a variant array
AryColBandC = RngColBandC
'loop through the variant array, applying the date
'conversion to each entry in the array and writing back
For Counter = LBound(AryColBandC) To UBound(AryColBandC)
DateFormatB = CDate(AryColBandC(Counter, 1)) '<~ temporarily store
DateFormatC = CDate(AryColBandC(Counter, 2)) '<~ dates here
AryColBandC(Counter, 1) = DateFormatB
AryColBandC(Counter, 2) = DateFormatC
Next Counter
'write the results out to the sheet
For Counter = LBound(AryColBandC) To UBound(AryColBandC)
MySheet.Cells(5 + Counter, 2) = AryColBandC(Counter, 1)
MySheet.Cells(5 + Counter, 3) = AryColBandC(Counter, 2)
Next Counter
End Sub

VBA countif statement only returns 0

I'm working on a macro that is supposed to count the number of times the term "GM" appears in a column. I decided to use a countif statement, as I have before and it worked well. However, for some reason when I run my code it outputs 0 every time, which definitely is not correct. I've run this same code with other columns and strings and it has worked fine, but for some reason if I search this certain column for the term "GM" it fails. The only thing I can think of is maybe countif only works if the string you're searching for is the only string in a cell, because in all cases where this is true the code works fine. In this particular case the string I'm looking for is not the only string in the cell and the code is failing. I've tried to find more info on whether or not this is true but I can't find anything online. Here's the code if anyone would like to take a look:
Function OemRequest() As Long
Sheets("CS-CRM Raw Data").Select
Sheets("CS-CRM Raw Data").Unprotect
Dim oem As Long
Dim LastRow As Long
Dim LastColumn As Long
'Determines size of table in document
LastRow = Range("A" & Rows.Count).End(xlUp).row
LastColumn = Cells(1, Columns.Count).End(xlToLeft).Column
oem = Application.WorksheetFunction.CountIf(Range(2 & "2:" & 2 & LastRow), "gm")
OemRequest = oem
End Function
You are correct that the COUNTIF as written will only match cells where the whole content is "gm". The criteria in the COUNTIF function will also accept wildcards, so to match on cells that contain "gm" do:
.CountIf(Range(2 & "2:" & 2 & LastRow), "*gm*")
Update
As you noted there is also an issue with your Range call. As it is, the expression inside the parens will evaluate to "22:2<LastRow>" (where <LastRow> is the value of the LastRow variable).
The 2's in there should be a variable containing the column name you're interested in. Something like:
Dim col as String
col = "B"
... Range(col & "2:" & col & LastRow) ...
This will evaluate to "B2:B<LastRow>", which is what you want.
Another possibility:
oem = WorksheetFunction.CountIf(Columns(LastColumn).Cells(2).Resize(rowsize:=LastRow - 1), "gm")
This will count cells containing "gm" (use wilcards if needed) in the LAST column of the table, except the one in the first row. (It assumes the table upper left corner is in cell "A1")
Of course you can create a variable if you would like to count any other column:
Dim lngCol as Long
lngCol = ...
oem = WorksheetFunction.CountIf(Columns(lngCol).Cells(2).Resize(rowsize:=LastRow - 1), "gm")
I think in this way
Sub Main()
Application.ScreenUpdating = 0
Dim Count As Double
Range("C1").Activate 'Firs row in the column
Do While ActiveCell.Value <> ""
If InStr(ActiveCell.Value, "MyText") Then
Count = Count + 1
End If
ActiveCell.Offset(1, 0).Activate
Loop
Application.ScreenUpdating = 1
End Sub
This will work, only if the data cell is not empty, if there is an empty space in middle of the worksheet, do this:
Sub Main()
Application.ScreenUpdating = 0
Dim Count As Double
Range("C1").Activate
Do While ActiveCell.Row <> Rows.Count ' This wil evaluate all the rows in the 'C' Column
If InStr(ActiveCell.Value, "MyText") Then
Count = Count + 1
End If
ActiveCell.Offset(1, 0).Activate
Loop
Application.ScreenUpdating = 1
End Sub
Hope it's work for you.