Getting an integer value from dates VBA - vba

I am trying to get a value difference (integer) between the years of 2 different dates in VBA and take also month (for example 2020 - 2019, the result I would want is 1) but my code only give me a date. Any ideas?
For i = 2 To PnLD1WS.Cells(1, 1).End(xlDown).Row
PnLD1WS.Cells(i, 164).Value = ((((DateSerial(Year(PnLD1WS.Cells(i, 13)), 0, 0) - DateSerial(Year(PnLD1WS.Cells(i, 3)), 0, 0)) * 12) + (DateSerial(0, (Month(PnLD1WS.Cells(i, 13))), 0) - (DateSerial(0, (Month(PnLD1WS.Cells(i, 3))), 0)))))
Next i

You can use DateDiff:
DateDiff("yyyy", dat1, dat2)
With your code:
...
With PnLD1WS
.Cells(i, 164).Value = DateDiff("yyyy", .Cells(i, 13), PnLD1WS.Cells(i, 3))
End with
....

Related

Auto filter with date (UK format) array issue

I am trying to auto filter datasets pertaining to a set date array which are stored in the UK format - for ex: 11/04/2019 (April 11, 2019), 15/04/2019 (April 15, 2019). The macro is converting the UK date format to US format, however, it is filtering only those dates which are under 12 and is discarding those above 12. The code I used is shown below:
i = 0
For Each iCell In Control.Range(Cells(5, 3), Cells(99, 3))
If iCell.Value <> "" Then
'CONVERT DATE TO US FORMAT FOR VBA
iDte = Format(iCell, "mm/dd/yyyy")
i = i + 2
ReDim Preserve DteAry(1 To i)
DteAry(i - 1) = 2
DteAry(i) = iDte
End If
Next iCell
[Dates to be filtered][1]
If you store true date values, not text, these carry no format, so:
iDte = iCell
i = i + 2
ReDim Preserve DteAry(1 To i)
DteAry(i - 1) = 2
DteAry(i) = iDte

Excel VBA Function : Calculate seconds(also counts the milliseconds) from two dates

This is the follow up post of this question and this question
I have created the following VBA function to calculate the seconds(also count the milliseconds) from two datetime.
Function:
Public Function ConvertDate(D1 As String, D2 As String) As Date
Dim StrD1 As Date
Dim StrD2 As Date
StrD1 = CDate(Left(D1, 10) & " " & Replace(Mid(D1, 12, 8), ".", ":"))
StrD2 = CDate(Left(D2, 10) & " " & Replace(Mid(D2, 12, 8), ".", ":"))
ConvertDate = DateDiff("s", StrD2, StrD1)
End Function
Scenario 1:
Given Dates:
2011-05-13-04.36.14.366004
2011-05-13-04.36.14.366005
Getting Result:
0
Expected Result:
0.000001
Scenario 2:
Given Dates:
2011-05-13-04.36.14.366004
2011-05-13-04.36.15.366005
Getting Result:
1
Expected Result:
1.000001
Scenario 3:
Given Dates:
2011-05-13-04.36.14.366004
2011-05-13-04.37.14.366005
Getting Result:
60
Expected Result:
60.000001
A day is 1. A date is 1 for every day past 31-Dec-1899. Today happens to be 42,556. Time is a decimal portion os a day. Today at noon will be 42,556.5 and today at 06:00 pm will be 42,556.75.
There are 24 hours in a day, 60 minutes in an hour and 60 seconds in a minute. That means that there are 86,400 seconds in a day (24 × 60 × 60) and a second is ¹⁄₈₆₄₀₀ (0.0000115740740740741) of a day. Excel's 15 digit floating point calculation sometimes fouls up (loses small amounts) time calculations due to the base-24 and base-60 numbering system.
Dim tm1 As String, tm2 As String
Dim dbl1 As Double, dbl2 As Double
Dim i As Long
With Worksheets("Sheet9")
For i = 1 To .Cells(.Rows.Count, "A").End(xlUp).Row Step 2
tm1 = .Cells(i, "A").Text
tm2 = .Cells(i + 1, "A").Text
dbl1 = CLng(CDate(Left(tm1, 10))) + _
TimeValue(Replace(Mid(tm1, 12, 8), Chr(46), Chr(58))) + _
(CDbl(Mid(tm1, 20)) / 86400)
dbl2 = CLng(CDate(Left(tm2, 10))) + _
TimeValue(Replace(Mid(tm2, 12, 8), Chr(46), Chr(58))) + _
(CDbl(Mid(tm2, 20)) / 86400)
.Cells(i + 1, "B") = (dbl2 - dbl1) * 86400
.Cells(i + 1, "B").NumberFormat = "0.000000"
Next i
End With
The above takes your time-and-date-as-text and calculates a pseudo-DateDiff to an accuracy of a millionth of a second. The results are displayed in seconds as an integer with fractions of a second as a decimal.

Placing values generated from one sheet, into a new sheet

I have a nested for loop that is looking to take values in one row, generate a value based on an equation, then do the same for many rows following the first one, all while adding the value together.
Essentially, if row one has a value of 15, and row 2 and 3 return values of 10 and 12, the variable storing the total value (named genCost) will be 37.
I want to place the summed total values of genCost in a new sheet, separated by day, but when I run the code I get a Run-time 1004 error. I realize that this has something to do with the sheet that I am working on, and the sheet that I am trying to place the values into (the 2nd to last line in the code).
I understand my code may be ugly and simple, but can somebody help me troubleshoot this?
'Nested For loop for ALL OTHER DAYS of genCost...only 1 formula
For j = 2 To dayNumber
For i = 1 To increments
'IF(AND(U7=1,U6=0),R7,0)
If Cells(rowValue, 21) = 1 And Cells(rowValue - 1, 21) = 0 Then
ifValue = Cells(rowValue, 18)
Else
ifValue = 0
End If
'calculate value variable with second half of equation
value = (((Cells(rowValue, 23) * Cells(rowValue, 16) * (1 / 6)) + (Cells(rowValue, 25) * Cells(rowValue, 17) * (1 / 6)) + (Cells(rowValue, 21) * Cells(rowValue, 19) * (1 / 6)) + ifValue))
genCost = genCost + value
'set value and ifValue back to zero and step down one row and do again
value = 0
ifValue = 0
rowValue = rowValue + 1
Next i
Cells(3, j) = genCost
Dim genCostRefNum As Integer: genCostRefNum = 6
ThisWorkbook.Sheets(1).Range(Cells(genCostRefNum, 4)) = genCost
genCost = 0
Next j
Instead of this
ThisWorkbook.Sheets(1).Range(Cells(genCostRefNum, 4)) = genCost
write this
ThisWorkbook.Sheets(1).Cells(genCostRefNum, 4) = genCost
There is a default property of Cells statement, which is Value (same for Range). So your code was exactly
ThisWorkbook.Sheets(1).Range(Cells(genCostRefNum, 4).Value).Value = genCost
and after executing Cells something like:
ThisWorkbook.Sheets(1).Range(6).Value = genCost
It's good practice to always write complete statements and don't rely on default properties.

If with multiple and & or

I've been trying to run the below IF condition, however it does not work as intended:
If (LandscapingDataRange(MailCounter, 6) = 1 And (LandscapingDataRange(MailCounter, 4) = 0 Or LandscapingDataRange(MailCounter, 5) = 0)) _
Or (LandscapingDataRange(MailCounter, 6) = 1 And ((LandscapingDataRange(MailCounter, 4) - LandscapingDataRange(MailCounter, 7)) / LandscapingDataRange(MailCounter, 4)) > 0.1) _
Or (LandscapingDataRange(MailCounter, 6) = 0 And LandscapingDataRange(MailCounter, 5) > 0) _
Or (0 < LandscapingDataRange(MailCounter, 6) < 1 And (LandscapingDataRange(MailCounter, 4) = 0 Or LandscapingDataRange(MailCounter, 5) = 0)) _
Or (0 < LandscapingDataRange(MailCounter, 6) < 1 And ((LandscapingDataRange(MailCounter, 4) - LandscapingDataRange(MailCounter, 7)) / LandscapingDataRange(MailCounter, 4)) > 0.1) Then
Those rows with 0 in (LandscapingDataRange(MailCounter, 6)
and 0 in LandscapingDataRange(MailCounter, 5)
Are still applied in the IF function.
I would take a guess that you are using Or where you should be using ElseIf and so your logic is wrong. In that case, use Select Case instead - it's quicker and cleaner. Here's an example with the first 3 conditions and an Else clause - just add the other conditions as required.
Select Case True
(LandscapingDataRange(MailCounter, 6) = 1 And (LandscapingDataRange(MailCounter, 4) = 0 Or LandscapingDataRange(MailCounter, 5) = 0))
'// Do Something
(LandscapingDataRange(MailCounter, 6) = 1 And ((LandscapingDataRange(MailCounter, 4) - LandscapingDataRange(MailCounter, 7)) / LandscapingDataRange(MailCounter, 4)) > 0.1)
'// Do Something
(LandscapingDataRange(MailCounter, 6) = 0 And LandscapingDataRange(MailCounter, 5) > 0)
'// Do Something
Case Else
'// Do Something if none of the criteria are met.
End Select
I don't get, what you want to achieve, but logic operator have an order, in which they are resolved, just like multiplication/division before addition/substraction.
Maybe that is the problem in your case. Before any Or is computed, all And are resolved. So you have to use brackets, if this isn't the order, you want it to be computed.
The precedence order in Visual Basic is Not before And before Or before Xor, and the same rules should apply to VBA too.

What's the best logic to return TRUE in VBA if month NAMES are 1 apart?

I want to return True for the following examples:
Example 1
Date 1: 7/31/14
Date 2: 8/1/14
Example 2
Date 1: 12/31/07
Date 2: 1/1/16 (notice one year is 2007)
and False for the following example:
Example 3
Date 1: 7/31/14
Date 2: 9/1/14
What is the smartest way to go about this? I know I can use the Month() function and test whether the months are either different by 1 or 11, but that seams like a bad solution
Simple boolean logic and maths should suffice.
Sub dts()
Dim Date1 As Date, Date2 As Date
Date1 = DateSerial(2014, 7, 31)
Date2 = DateSerial(2014, 8, 1)
Debug.Print CBool(Abs((Month(Date1) - (Month(Date1) = 1) * 12) - (Month(Date2) - (Month(Date2) = 1) * 12)) = 1)
Date1 = DateSerial(2007, 12, 31)
Date2 = DateSerial(2016, 1, 1)
Debug.Print CBool(Abs((Month(Date1) - (Month(Date1) = 1) * 12) - (Month(Date2) - (Month(Date2) = 1) * 12)) = 1)
Date1 = DateSerial(2014, 7, 31)
Date2 = DateSerial(2014, 9, 1)
Debug.Print CBool(Abs((Month(Date1) - (Month(Date1) = 1) * 12) - (Month(Date2) - (Month(Date2) = 1) * 12)) = 1)
End Sub
Results:
dts
True
True
False
You added both excel-vba and excel-formula to your question. If used as a worksheet formula, remember that TRUE is 1, not -1 as in a VBA True.
If you don't care about the year, then it seem's perfectly legit (and even best) to use the build in Month function, like this:
Public Function AreMonthsOneApart(date1 As Date, date2 As Date) As Boolean
Dim lMonthsApart As Long
lMonthsApart = Abs(Month(date1) - Month(date2))
AreMonthsOneApart = (lMonthsApart = 1 Or lMonthsApart = 11)
End Function
In fact, using anything besides the Month function is probably going to be more complex.
(Even #Jeeped's solution uses Month in his calculations. It's going to be very hard to get around--Use Month!)
I think the easiest way would be to combine Abs() with Mod() in the following formula:
Abs(Month(Date1) - Month(Date2)) Mod 10 = 1
For example:
Dim Date1 As Date, Date2 As Date
Date1 = DateSerial(2014, 7, 31)
Date2 = DateSerial(2014, 8, 1)
Debug.Print Abs(Month(Date1) - Month(Date2)) Mod 10 = 1
Date1 = DateSerial(2007, 12, 31)
Date2 = DateSerial(2016, 1, 1)
Debug.Print Abs(Month(Date1) - Month(Date2)) Mod 10 = 1
Date1 = DateSerial(2014, 7, 31)
Date2 = DateSerial(2014, 9, 1)
Debug.Print Abs(Month(Date1) - Month(Date2)) Mod 10 = 1
Output:
True
True
False