VBA range.find finds wrong date value - excel

I am looking up a price multiplier for a given date in an Excel sheet, but range.find gives the wrong date. Here are the relevant code snippets:
If Not IsWbOpen("daily_prices.xlsx") Then
Workbooks.Open "C:\.....\daily_prices.xlsx"
End If
Set daily = Workbooks("daily_prices.xlsx").Sheets(1)
daily.Range("A:A").NumberFormat = "dd/mm/yyyy"
End If
This part makes sure that the column A:A, which contains the dates, is formatted correctly.
The code then loops through a date range where d is a date between d1 and d2. In my test script, d1= Jan 1, 2023 (formatted as "01/01/2023"), and d2 = Jan 30, 2023 (formatted as "30/01/2023").
The Excel sheet "daily" has a header in row 1 and 49 different dates in column A, ordered chronologically from "11/01/2023" to "01/01/2024", and the related multiplier (k), starting in row 2.
Only the dates that have a multiplier defined are present in the sheet. The first group of dates is from "11/01/2023" at row 2 to "22/01/2023" at row 13, the next starts with "17/02/2023" at row 14, and so on. Dates in between have no multiplier, and thus are not present.
I search the multiplier with this part of the code:
Set c = daily.Range("A:A").Find(what:=d, After:=Range("A1"), LookIn:=xlValues) 'is this date in the daily prices?
If Not c Is Nothing Then
k = CDec(daily.Cells(c.Row, 4)) ' multiplier is in column 4 (D:D)
rate = rate * k
End If
As expected, c is NOTHING for all dates below "11/01/2023". When d = "11/01/2023", c becomes "not NOTHING" but strangely it returns "11/11/2023" instead of "11/01/2023", which is in row 40 instead of row 2, thus it fetches the multiplier of row 40 instead of row 2.
I have re-checked all the code after reading several similar cases and made sure that column A is formatted correctly as "date", but I still get "11/01/2023" wrongly 'found' at row 40 instead of row 2, and the value returned is "11/11/2023" instead of "11/01/2023".
Instead of "12/01/2023", it finds "31/12/2023", and instead of "14/01/2023", which is part of the dataset, it finds nothing.
What did I overlook, where is the error?

Eventually, I found the solution.
It appears that using dates formatted as dates leads to ambiguity with Range.Find(d) as it's very similar to a text search, and depending what date format you use, their partial strings (month and day) may create confusion in certain language versions of Windows. I noticed this when I was looking for a date with day=11 or 12 and found a date with month=11 or 12. This is typical for the confusion that occurs sometimes when you use data formats like "mm-dd-yyyy" (US) and "dd/mm/yyyy" (other) and looks like a Windows bug to me. Windows should distinguish the two formats clearly, but sometimes it doesn't.
There was a similar problem at VBA Range.Find method not finding a value that IS in the range that helped me find the solution.
I needed to format the search object temporarily as numbers
daily.Range("A:A").NumberFormat = "0"
and also search for the long integer value of the running date, thus the starting value is
d = CLng(date1)
where d is type number and date1 is type date. The search term is then simply
Set c = daily.Range("A:A").Find(d)
If Not c Is Nothing Then ...
At the end I reformat the date column with
daily.Range("A:A").NumberFormat = "dd/mm/yyyy;#"
This resolved the problem.
Thank you all for your kind help.
#Ron Rosenfeld's hint came closest as he proposed to search for What:=CDbl(date), which is what I am basically doing, except that I prefer CLng over CDbl as I need the date as an integer and not as a floating point number. The only problem with that idea was that you cannot search for .value2. You can only search for xlValues (which is not the same) or xlFormulas. Neither would find this long integer number in the cell's properties.
Transforming both the search object and the search item to numbers resolves the ambiguity completely.

Related

How to loop through rows and changing the reference worksheet in each cell

I'm new to VBA and was just trying to figure this out.
I have a the following range CR7, CR9:CR24, CR28:CR39, CR45:CR50, CR52
Currently, each of these cells reference a value from another worksheet.
CR7='Trend'!CO7
CR9='Trend'!CO9
CR10='Trend'!CO10
This will follow the entire range.
What I was wanting to do was change the reference to 3 columns to the right.
CR7='Trend'!CO7 will then be CR7='Trend'!CR7
What I was trying to do was remove the last 3 characters after the "!" and then replace it with "RC[-3]" but I don't know what I'm doing at all. I just looked up different codes on this site and tried to piece it together, knowing that I would probably run into some obstacles because I'm new at this.
Here's the code I was trying to implement but running into an error.
For Each C In Range("CR7,CR9:CR24,CR28:CR39,CR45:CR50,CR52")
C.Value = Left(C.Value, InStr(C.Value, "!") - 1)
C.Value = C.Value & "RC[-3]"
Next
Any suggestions on how to get this corrected or is there a better way in doing this? Thanks in advance for looking into this!!
Solution
As stated in the comments, since you are looking for a specific header, the solution might be faster as follows:
Assuming the headers are in Sheet 1 as follows
Jan-22 (IE:actual data is 01-01-2022)
Feb-22
Mar-22
10
30
60
20
40
70
Where A1 = Jan-22, B2= Feb-22, etc.
Just use a combination of index and match, seems like you need to bring the row at the same level, so it will be as follows:
Set a referece in your formula to where you will have the criteria to look: In this example will be A1
=INDEX(Sheet1!$A:$C,ROW(),MATCH($A$1,Sheet1!$1:$1,0))
I'll explain:
Index will bring the column where you want the data, in this case will be the data under the month (Col A), seems like the row should be at the same level, so we will say the row will be equal to where the formula row is being casted, that's why the row(), finally, it will be in where the column matches the criteria that you want where the header is found, if you need an offset even after finding the column name, just sum it up at that part
MATCH($A$1,Sheet1!$1:$1,0) + 3
Demo:

How to extract data from a field that is apparently of the date / time format but is not really such a format

I have copied (copy/paste) a part of a home-page into an empty excel. One of the fields looks like this: 3140:01:00. If I check the format, it shows that the category is custom, and that the type is [t]:mm:ss. The problem is that I am only interested in the first 4 digits shown plus digits 6 and 7. If I change the format to e.g. text, I end up with a number. Probably a number, that identifies a specific date. In fact the first 4 digits are the length of a horse race! :-) I'm new at VB, but I have managed to clean up the rest of the information - but not this. Probably a known problem. Please help!
You will need to understand the difference between the value and the representation of your data (note that this is not VBA but Excel related). When you enter 3140:01:00 in a cell in Excel, Excel tries to understand what you enter. With the colon, it looks somehow like a time value, so Excel guesses that this a a time, convert what you enter into a date value (a date in Excel has automatically a time part) and put a number format that displays this date+time as [h]:mm:ss.
As I said, internally, what you entered is converted into a Date. Now a Date in Excel in internally stored as a number. If you set the number format to "Number", the cell will display 130.83402. This is because 3140 hours = 130 days + 20 hours. The 20 hours (plus the 1 minute) are stored as a fraction of a day (0.83402).
If you format the same value as Date/Time, you will see (depending on your regional settings) something like 05/09/1900 20:01:00 - because that is the 130th day in the Excel calendar (day 1 in Excel is 1/1/1900). Note that the value of the cell doesn't change, only the way it is displayed.
If you could prevent Excel to convert your input into a date, the solution would be to do string-handling, eg use the Split-function. When you format a cell as Text and enter 3140:01:00 manually, Excel leaves the string untouched and this would work. However, it seems that when you Paste the value into the cell, the number format is set automatically and the value is converted into a date even if the cell was formatted as Text before. I don't know if there is a way to tell Excel to not convert the data if it is pasted.
So what we can do instead is to convert the date value back into "hours", "minutes" and "seconds" - even if the "hours" are in fact something else (meters? yards? horse length?), and the minutes are probably also not minutes but whatever.
Several ways to do so.
If you don't mind that the strange pseudo-date value remains in your Excel (you can hide the column with that value), use just 2 simple formulas. Assuming your "date" is in D2:
use the formula =TRUNC(24*D2) to get the horse race length (the first number). We cannot use the Hour-formula here as this would return only 20 and not 3140.
use the formula =MINUTE(D2) to get the second number
use the formula =SECOND(D2) to get the third number
If you want to involve VBA:
Sub SplitStrangeDate(cell As Range)
If Not IsDate(cell) Then Exit Sub
Dim d As Date
d = cell.Value
Dim v1 As Long, v2 As Long, v3 As Long
v1 = CLng(d * 24)
v2 = Minute(d)
v3 = Second(d)
Debug.Print v1, v2, v3
End Sub

Getting Current Date and Return as Integer?

I have a date like this (mm/yy) in row 1
A B C D E F
1/19 2/19 3/19 4/19 5/19 6/19 ...
I want the VBA to recognize today's date and match it to current column and return as integer.
Ignoring the days (only matching month and year).
For example, if today is 4/13/2019, it would be 4 (column D)
I would need this in VBA because I will be using it to define a range:
For today To x month
It appears OP was looking for a VBA solution, so here is an alternative.
I can think of a few completely different methods of accomplishing this within VBA. Your question isn't very clear of what you are wanting the end result to be, but it appears you are looking for a function that will return the column number - perhaps so you can use to pinpoint a range.
Function DateCol(ByVal InputDate As Date) As Long
Dim colDate As Date
colDate = InputDate - Day(InputDate) + 1
Dim srcRng As Range
Set srcRng = ThisWorkbook.Worksheets("Sheet1").Rows(1)
DateCol = srcRng.Find(What:=colDate, LookAt:=xlWhole).Column
End Function
You simply take an input date, subtract the days (and add 1 since the first day of the month isn't 0). Then you take this new date and use the .Find() method to locate the range that contains your date on the worksheet, and finally the .Column property to get the number you are looking for.
Here is a small sample usage:
Sub test()
' Example Usage
Cells(10, DateCol(#6/11/2019#)).Value = "Test"
End Sub
In the above test sub, the DateCol() function would have returned the value of 6 in your sample worksheet, making the result:
Cells(10, 6).Value = "Test"
Only issue is that this function doesn't contain any error handling. You will raise an error if the date is not found in .Find(), so ensure that you take this into consideration.
Also, don't forget to change this line to use the true Worksheet:
ThisWorkbook.Worksheets("Sheet1").Rows(1)
I had to redo my answer after messing with the data. this is what i ended up with.
On Row 1 I entered the dates as: 1/1/2019, 2/1/2019, 3/1/2019... and custom formatted the row to only show it as mm/yy.
With the formula below I grab the month and year from the given date and convert it into the first of the month. I am very positive there is a better way to make it but my brain is fried for the day.
=MATCH(NUMBERVALUE(TEXT(A3,"mm")&"/1/"&TEXT(A3,"yy")),$1:$1,0)
Edit: (Edit formula to make it permanent on Row 1 [$1:$1])
Assuming that the date 4/13/2019 is on Cell A3

Excel - filter table results by dates

I have a refreshable table in excel and I want to filter the rows by a couple of date ranges. Each row has a date and other information.
I want to find the rows that are in the first date range (F1:F2) and are not in the second date range (H1:H2).
The table is refreshable, and can change size. It currently spans A3:X6146. The table is a query, so it will change sizes when a separate date range is used to find the table values.
I don't have much VBA experience, so this problem is tripping me up. Any ideas?
Thanks
EDIT:
I'll try to make the issue clearer.
I have a table that is created via a query that pulls in data that falls between the Starting Date and the Ending Date, 1/1/2016 and 12/31/2017 here. It lists each time an item was purchased, so each one can be listed multiple times.
I want to find which items were purchased (listed in the table) between the Active Date Range start and end dates (cells F1 and F2), and NOT purchased between the Inactive Date range (cells H1 and H2).
Starting Date: 1/1/2016 Active Date Range Start: 3/1/2016 Inactive Date Start: 3/2/2017
Ending Date: 12/31/2017 Active Date Range End: 3/1/2017 Inactive Date End: 9/22/2017
item date
1 9/21/2017
2 9/20/2017
3 9/20/2017
Yes, I can say to you what I would do.
Create one additional column to keep the result value, if is in or out of the date range. Then
dim t() as string, lin as long, linf as long
linf=Range("F65536").End(xlUp).Row 'or any other more precise way to get the final line
redim t(1 to linf-2,1 to 1)
'range dates - are they on the worksheet?
dim rg_dates as Range,r as range,b as boolean
set rg_dates=Sheets("xxxx").Range("B1:B4") ' just an example, the range would be B1:C4 but use only the first column - see below
For lin=3 to linf
b=False
For each r in rg_dates
If cells(lin,"F").value>= r.Cells(1,1) and cells(lin,"G").Value<=r.Cells(1,2).value then
b=true
Exit for
End If
Next r
If b then t(lin-2,1)="Y" else t(lin-2,1)="N"
Next l
Range("Z3:Z" & linf).Value = T
'Then just filter the table.
There would be then many things to do to keep it error free, and how to apply it at the concrete situation. Hopefully with what I wrote above you can get an idea about things you can do using VBA. If you are using code to filter the table you can do all this invisible to the user, creating an extra column for the filter criteria, filtering, and then deleting the whole column..

Getting Percentage value to show in the correct format

I have two worksheets with data from different sources. I need to copy the data to a single worksheet and remove duplicates. To achieve this objective, I need all the data formatted the same on both worksheets. All of this is already coded except with one column of data I am having issues. These columns contain a representation for percentage. In worksheet A, the value is showing as .4386 which equates to 43.86%. I use this code that converts the value without issue:
Worksheets("Verification").Range("F2:F2000").NumberFormat = "0.00%"
In worksheet B, the same data is shown as 43.86, but the above code changes it to 4386.00%. I also tried changing this line to .NumberFormat = "General\%" and this almost works, but returns a value of 44%. What do I need to add to my code to get this to show 43.86% on worksheet B?
Sorry for the slow reply in comments - I will just submit an answer.
Like Ralph said, it's really better to make sure they are the same number.
43.1 and .431 are not the same number.
For Each c In [A1:A10]
If c.Value < 1 Then
c.Value = c.Value * 100
End If
c.NumberFormat = "0.00\%"
Next c
Results:
You are stating that .4386 on worksheet A is the same data [...] as 43.86 on worksheet B. So, Excel is correct to convert 43.86 to 4386.00%. Maybe you need a conditional formatting: when the number is smaller or equal to 1 then format it "0.00%" and otherwise format it as "0.00""%""".
Yet, I would assume that you'll be running into problems when comparing the data between the sheets with this solution. Hence, I would divide all numbers on sheet B by a 100 first to really make them comparable.
Note, that just by making numbers "look alike" they are not the same. Example: write in cell A1 the value 1000 and in cell B1 also 1000. Then change the number format for A1 to 0 and the number format for B1 to 0, (or to 0. outside the US). A1 will show 1000 while B1 will show 1. If you ask in cell C1 =A1=B1 you will get a TRUE as the answer.

Resources