Compare dates to fill a cell using VBA Excel - vba

I'm trying to compare two dates with the format DD/MM/YYYY HH:MM AM/PM, the idea is that if one date is greater than the other it puts in a field the word "Before" or "After" if it's lower.
I've tried doing this but failed miserably.
For col = 0 To objSheet.UsedRange.Rows.Count
objSheet.Range("A2").Formula = "=IF(Value < B&(col+2), Before,After)"
Next
Line objSheet.Range("A2").Formula = "=IF(Value < B&(col+2), Before,After)" seems to not throw any error but all I get in my excel file is #NAME?
Value is a string that contains the value to which the other string is going to be compared to. Example
' Value = 5/4/2016 8:00:00 PM
' Value in B column: 5/5/2016 12:00:00 PM
then the if should output = BEFORE but if
'Value in B column: 5/5/2016 2:00:00 AM
then the output should be AFTER
I've seen some diff_date function but it doesn't seem to be integrated because it doesn't show up as an option.
It's there a way to achieve this? What am I doing wrong?

Edited This works for me in Excel 2013, but I've had to make some assumptions about what you're doing. The below code fills in column A depending on whether the date in Value is before or after the value in column B.
Option Explicit
Option Base 0
Public Sub PopulateDates()
Dim dateString As String
Dim row As Long
dateString = "5/4/2016 8:00:00 PM"
Dim dateRepresentation As String
dateRepresentation = "VALUE(""" & dateString & """)"
Dim newFormula As String
For row = 1 To ActiveSheet.UsedRange.Rows.Count
newFormula = "=IF(" & dateRepresentation & " < B" & CStr(row) & ", ""Before"",""After"")"
ActiveSheet.Cells(row, 1).Formula = newFormula
Next row
End Sub
Lots going on here.
dateString instead of Value per Scott Craner's note, and because Value is so generic you might forget what it means.
dateRepresentation is a way to represent a date as a literal in a worksheet formula. It is, e.g., VALUE("5/4/2016 8:00 PM").
newFormula is on its own line so you can check it in the debugger before you make the assignment. :)
the row (you had col+2) is part of your VBA, so it is not part of your formula. CStr(row) makes a string (e.g., 42), and then & pastes that string after B and before ,.
ActiveSheet.Cells instead of ActiveSheet.Range because the latter didn't work for me. You would use objSheet instead of ActiveSheet
The result of all this is to put, e.g., in cell A1:
=IF(VALUE("5/4/2016 8:00:00 PM") < B1, "Before","After")

Related

VBA: Search for a value in a column, find the next cell that match the value if adjacent cell not empty

I'm pretty newbie at coding in vba (i'm actually learning on the spot as i've been required to do it so), and I'm having a little trouble getting this right.
What I need is to be able to search for a value {clave} and then insert the current date into the adjacent cell, but I haven't found the way to do it without having it overwrite the very first match.
At first I thought I could do it with a Loop, but I can't quite put my finger on it and I've been running in circles.
Is I haven't found the solution, I just left it as it is, but heres my code:
Private Sub buscarbtn_Click()
Dim clv1
Dim rnng As Range
clv1 = clavebx.Value
'Insert date
prontuario1.Range("V:Z").Find(what:=clv1, LookIn:=xlValues, LookAt:=xlWhole).Offset(0, -6).Value = Date
'This isn't really relevant, just calling some data into the userform
busbox.Value = Hoja4.Range("D7").Value
mrcbox.Value = Hoja4.Range("D5").Value
corridabox.Value = Hoja4.Range("D8").Value
namebox.Value = Hoja4.Range("D4") & " - " & Hoja4.Range("D6")
fechabox.Value = Date
End Sub
And a quick look at my table so you can picture what I'm trying to do.
Thank you in advance!
Once you found the CLAVE ID check if the cell value is empty, if not empty place the date value.
Private Sub buscarbtn_Click()
Dim clv1 As String
Dim rnng As Range
Dim clave_found As Range
clv1 = clavebx.Value
'Insert date
Set clave_found = prontuario1.Range("V:Z").Find(what:=clv1, LookIn:=xlValues, LookAt:=xlWhole).Offset(0, -6)
With clave_found
If .Value = vbNullString Then
.Value = Date
Else
MsgBox "The [CLAVE] ID found with date: " & .Value
End If
End With
'This isn't really relevant, just calling some data into the userform
busbox.Value = Hoja4.Range("D7").Value
mrcbox.Value = Hoja4.Range("D5").Value
corridabox.Value = Hoja4.Range("D8").Value
namebox.Value = Hoja4.Range("D4") & " - " & Hoja4.Range("D6")
fechabox.Value = Date
End Sub

Extracting Date/Time from comment cell

I have a comment field with cells containing text like this:
Cancelled by user at 2018-01-03 03:11:57 without charge
I want to get the date and time information, but it may not always be in the 3rd/4th from last spaces, otherwise I might try to do some sort of complicated split of the cell. Is there an "in cell" way extract the date time information? Or will this need a VBA script? I prefer the former, but I'm trying to make a macro to simplify my life anyway, so VBA would work too.
I'd propose the following formula:
=MID(A1,FIND("at 20",A1)+3,19)
This would require that the date is always preceded by the word 'at' and the date string starts with 20.
You can try this function. It splits the string checking for items that have the first letter numeric, and builds a result string of just the date information.
Public Function ParseForDate(sCell As String) As String
Dim vSplit As Variant
Dim nIndex As Integer
Dim sResult As String
vSplit = Split(sCell, " ")
For nIndex = 0 To UBound(vSplit)
If IsNumeric(Left$(vSplit(nIndex), 1)) Then
sResult = sResult & vSplit(nIndex) & " "
End If
Next
ParseForDate = Trim$(sResult)
End Function
If you wanted to use it in a formula it would look something like this:
=ParseForDate(A1)
To use it in a VBA routine:
Dim s as String
s = ParseForDate(Range("A1"))
Non-VBA solution: (this is assuming the date format is always the same for all cells)
= MAX(IFERROR(DATEVALUE(MID(A1,ROW(INDEX($A:$A,1):INDEX($A:$A,LEN(A1)-19)),20)),0))
+MAX(IFERROR(TIMEVALUE(MID(A1,ROW(INDEX($A:$A,1):INDEX($A:$A,LEN(A1)-19)),20)),0))
Note this is an array formula, so you must press Ctrl+Shift+Enter instead of just Enter when typing this formula.
You will obviously then need to format the cell as a date and time, but this formula gets the numerical value that Excel uses for its internal date and time system.
Using a regex will enable you to fetch the date and time, irrespective of its placement in the string. The following solution will work if the date and time are of the same format as shown in the example string.
Code:
Sub getDateTime()
Dim objReg, matches, str
str = Sheet1.Cells(1, 1).Value 'Change this as per your requirements
Set objReg = CreateObject("vbscript.regexp")
objReg.Global = True
objReg.Pattern = "\d{4}(?:-\d{2}){2}\s*\d{2}(?::\d{2}){2}"
If objReg.test(str) Then
Set matches = objReg.Execute(str)
strResult = matches.Item(0)
MsgBox strResult
End If
End Sub
Click for Regex Demo
Regex Explanation:
\d{4} - matches 4 digits representing the year
(?:-\d{2}){2} - matches - followed by 2 digits. {2} in the end repeats this match 2 times. Once for getting MM and the next time for DD
\s* - matches 0+ whitespaces to match the space between the Date and Time
\d{2} - matches 2 digits representing the HH
(?::\d{2}){2} - matches : followed by 2 digits. The {2} in the end repeats this match 2 times. First time for matching the :MM and the next time for matching the :SS
Screenshots:
Output:
This will be good for about 90 years (using cell C3 for example):
Sub GetDate()
Dim s As String
s = Range("C3").Comment.Text
arr = Split(s, " ")
For i = LBound(arr) To UBound(arr)
If Left(arr(i), 2) = "20" Then
msg = arr(i) & " " & arr(i + 1)
MsgBox msg
Exit Sub
End If
Next i
End Sub

Creating a lot number using Julian/Ordinal date from a yyyy-mm-dd and concatenate in VBA

I need to create a lot number which consists of:
Digits: 1,2,3 ----> Three digit reagent code ----> For example:141 (this is a constant)
Digit: 4 ----> Identifier ----> For example: 2 (this is a constant)
Digits: 5,6,7 ----> Julian/Ordinal Calendar day ----> 001-365 (366 for leap year)
Digit: 8 ----> The last digit of the expiry year ----> 0-9
Therefore: 14120039 (Expiry date would be 2019-01-03)
The expiry date can be found on a sheet called "CP_sequencer" in cell "S7". This will be in the format yyyy-mm-dd.
The following is the code I’m using so far but I know something is wrong and it may not be the most efficient way of doing things. There are a few cell references that are correct but I know it may be hard to follow without the actual spreadsheet.
Dim Julian_Day As String
Dim Split_Date As String
Dim valueYear, valueLastDigit As Integer
Range("F31").Select
Julian_Day = _
ActiveCell.FormulaR1C1 = _
"=VALUE(RIGHT(YEAR('CP sequencer'!R[-24]C[13]),2)&TEXT('CP sequencer'!R[-24]C[13]-DATE(YEAR('CP sequencer'!R[-24]C[13]),1,0),""000""))"
Split_Date = _
Range("F31") = Year(CP_Sequencer.Range("S7"))
Range("F31").Select
Select Case Len(value1) 'gives a number depending on the length of the value1
Case 4 ' e.g., 2017 = 201, 7
valueYear = Left(value1, 3) ' 201
valueLastDigit = Right(value1, 7) ' 7
End Select
ActiveCell.Value = "1412" & Julian_Day & valueLastDigit
I know something isn't right because at the moment when I run this code the output is 1412False0
Any help would be much appreciated
I assume you want a VBA solution to write back your lot number code to a given cell. Your code includes many errors (references without values, undeclared variables, double assignments and so on). Maybe the code with explainations below will be of some help. I use a type Definition to structure your results and make the code more readable.
Code
Option Explicit ' obliges you to declare your variables
Type TData ' declaration head of your module
ReagentCode As String ' 3 dig .. 141
ID As String ' 1 dig .. 2
JulDays As String ' 3 dig .. 1-365/366
YearDigit As String ' 1 dig .. 7 (2017 -> 7)
End Type
Sub CreateLotNo()
' Declare variables
Dim MyData As TData
Dim yr As Integer ' expiry year extracted from cell Sz
Dim ws As Worksheet
' set fully qualified reference to your worksheet
Set ws = ThisWorkbook.Worksheets("CP Sequencer")
' get expiry year from cell S7
yr = Year(ws.Range("S7").Value) ' expiry year
With MyData
' assign values to MyData
.ReagentCode = "141" ' constant
.ID = "2" ' constant
' julian days = expiry date minus last year's ultimo date
.JulDays = Format(ws.Range("S7").Value - CDate("12.31." & yr - 1), "000")
.YearDigit = Right(yr, 1) ' last digit of the expiry year
' write lot number back to cell XY
ws.Range("F31").Value = .ReagentCode & .ID & .JulDays & .YearDigit & ""
End With
End Sub
This should return the LotNumber you're after.
I'm quite not sure what's wrong with your code, but it will be in this line:
Julian_Day = _
ActiveCell.FormulaR1C1 = _
"=VALUE(RIGHT(YEAR('CP sequencer'!R[-24]C[13]),2)&TEXT('CP sequencer'!R[-24]C[13]-DATE(YEAR('CP sequencer'!R[-24]C[13]),1,0),""000""))"
This is asking the question is the formula in the activecell the same as the text string "=VALUE(RIGHT...." and place the result in the Julian_Day variable. Pretty much guaranteed that the value won't be the same so FALSE is returned.
If you wanted to get the result of the formula using that method you'd need to place the formula in the cell first and then read the result... but I'd advise against using that method. Easier to reference the values within VBA.
The LotNumber function below should return the value you're after. You can use it as I have in the Test procedure or as a worksheet function entered directly in a cell: =LotNumber(A1,B1,C1)
Sub Test()
'Passing values to the LotNumber function.
Debug.Print LotNumber(141, 2, DateValue("3 January 2019"))
'Getting values from Sheet1.
With ThisWorkbook.Worksheets("Sheet1")
Debug.Print LotNumber(.Range("A1"), .Range("B1"), .Range("C1"))
End With
End Sub
Public Function LotNumber(Reagent As Long, Identifier As Long, Expiry As Date) As String
Dim Ordinal As Long
Ordinal = Expiry - DateSerial(Year(Expiry), 1, 1) + 1
LotNumber = Format$(Reagent, "000") & Identifier & Format$(Ordinal, "000") & Right(Format(Expiry, "yyyy"), 1)
End Function
Edit:
As an afterthought you could define the LotNumber function as:
Public Function LotNumber(Expiry As Date, Optional Reagent As Long = 141, Optional Identifier As Long = 2) As String
Using this method you must pass the date to the function, but the Reagent and Identifier will default to 141 and 2 if no alternative values are supplied.
If entered today (30th November 17) then Debug.Print LotNumber(Date) will return 14123347. As a worksheet function with 3rd Jan 2019 in cell C1: =LotNumber(C1) will return 14120039

vba excel - How to delineate a cell with multiple times in it?

I am writing a macro to separate some data in one cell to multiple columns using the text to columns function. The problem I am running into is figuring out a way to separate a cell with multiple times in it, like so: "9:0011:008:0012:30".
I would like to separate it out into: "9:00" "11:00" etc. If I separate by ":" I'm going to get 9, 00, 11, 00. If I do it by ":**" I'm only going to get 9, 11, 8, 12, cutting off the 12:30 time.
Thanks in advance!
This is my golf attempt:
Option Explicit
Public Sub TestMe()
Dim strInput As String
Dim counter As Long
Dim strCurrent As String
strInput = "9:0011:008:0012:30"
For counter = 1 To Len(strInput) - 2
If Mid(strInput, counter, 1) = ":" Then
Debug.Print strCurrent & Mid(strInput, counter, 3)
counter = counter + 2
strCurrent = vbNullString
Else
strCurrent = strCurrent & Mid(strInput, counter, 1)
End If
Next counter
End Sub
It nicely returns:
9:00
11:00
8:00
12:30
It assumes that the minutes are always with two digits. You can easily change it to a function, returning Array().
The TextToColumns() function requires a Delimiter that is essentially "sacrificed", and you do not have one in those strings! Therefore, the TextToColumns() approach is not viable.
I suggest you use VBA string manipulation functions instead:
Find the position of the first ":" in the string; call it "p"
Extract your first item (the characters from the 1st to the p+2) into an output variable, call it x
Remove the x from the original string
Go to Step 1

Excel VBA date formats

I've got a spreadsheet that contains a number of dates. These generally appear in either mm/dd/yyyy or mm/dd/yyyy hh:mm.
The problem is that the dates aren't always put in correctly and I want to have checks to make sure they are dates in the code.
My original thought was to use IsDate to check or CDate but this didn't seem to work: it was still returning strings instead of dates.
I've since set up a small experiment which shows that these functions don't work the way I expect them to. Methodology is:
In a cell A1 I enter the formula =DATE(2013,10,28)
Cell B1 formula =A1*1 which should equal a number (41575)
Run this little script
Sub test()
MsgBox ("Start:" & TypeName(ActiveCell.Value) & " " & IsDate(ActiveCell.Value))
ActiveCell.Value = Format(ActiveCell.Value, "mm/dd/yyyy")
MsgBox ("After format: " & TypeName(ActiveCell.Value) & " " & IsDate(ActiveCell.Value))
ActiveCell.Value = CDate(ActiveCell.Value)
MsgBox ("After Cdate: " & TypeName(ActiveCell.Value) & " " & IsDate(ActiveCell.Value))
End Sub
When the script starts the cell is a of type date and IsDate returns true. After it is run through Format it is of type string but IsDate still returns true. CDate will also convert the cell to a string. Cell B1 will also now return 0 (since its a string*1).
So I guess to summarize the questions:
Why are Format and CDate changing my cells to strings?
How can I ensure that a cell will return a date value and not just a string that looks like a date?
It's important to distinguish between the content of cells, their display format, the data type read from cells by VBA, and the data type written to cells from VBA and how Excel automatically interprets this. (See e.g. this previous answer.) The relationship between these can be a bit complicated, because Excel will do things like interpret values of one type (e.g. string) as being a certain other data type (e.g. date) and then automatically change the display format based on this. Your safest bet it do everything explicitly and not to rely on this automatic stuff.
I ran your experiment and I don't get the same results as you do. My cell A1 stays a Date the whole time, and B1 stays 41575. So I can't answer your question #1. Results probably depend on how your Excel version/settings choose to automatically detect/change a cell's number format based on its content.
Question #2, "How can I ensure that a cell will return a date value": well, not sure what you mean by "return" a date value, but if you want it to contain a numerical value that is displayed as a date, based on what you write to it from VBA, then you can either:
Write to the cell a string value that you hope Excel will automatically interpret as a date and format as such. Cross fingers. Obviously this is not very robust. Or,
Write a numerical value to the cell from VBA (obviously a Date type is the intended type, but an Integer, Long, Single, or Double could do as well) and explicitly set the cells' number format to your desired date format using the .NumberFormat property (or manually in Excel). This is much more robust.
If you want to check that existing cell contents can be displayed as a date, then here's a function that will help:
Function CellContentCanBeInterpretedAsADate(cell As Range) As Boolean
Dim d As Date
On Error Resume Next
d = CDate(cell.Value)
If Err.Number <> 0 Then
CellContentCanBeInterpretedAsADate = False
Else
CellContentCanBeInterpretedAsADate = True
End If
On Error GoTo 0
End Function
Example usage:
Dim cell As Range
Set cell = Range("A1")
If CellContentCanBeInterpretedAsADate(cell) Then
cell.NumberFormat = "mm/dd/yyyy hh:mm"
Else
cell.NumberFormat = "General"
End If
Format converts the values to strings. IsDate still returns true because it can parse that string and get a valid date.
If you don't want to change the cells to string, don't use Format. (IOW, don't convert them to strings in the first place.) Use the Cell.NumberFormat, and set it to the date format you want displayed.
ActiveCell.NumberFormat = "mm/dd/yy" ' Outputs 10/28/13
ActiveCell.NumberFormat = "dd/mm/yyyy" ' Outputs 28/10/2013
Thanks for the input. I'm obviously seeing some issues that aren't being replicated on others machines. Based on Jean's answer I have come up with less elegant solution that seems to work.
Since if I pass the cell a value directly from cdate, or just format it as a number it leaves the cell value as a string I've had to pass the date value into a numerical variable before passing that number back to the cell.
Function CellContentCanBeInterpretedAsADate(cell As Range) As Boolean
Dim d As Date
On Error Resume Next
d = CDate(cell.Value)
If Err.Number <> 0 Then
CellContentCanBeInterpretedAsADate = False
Else
CellContentCanBeInterpretedAsADate = True
End If
On Error GoTo 0
End Function
Example usage:
Dim cell As Range
dim cvalue as double
Set cell = Range("A1")
If CellContentCanBeInterpretedAsADate(cell) Then
cvalue = cdate(cell.value)
cell.value = cvalue
cell.NumberFormat = "mm/dd/yyyy hh:mm"
Else
cell.NumberFormat = "General"
End If
Use value(cellref) on the side to evaluate the cells. Strings will produce the "#Value" error, but dates resolve to a number (e.g. 43173).
To ensure that a cell will return a date value and not just a string that looks like a date, first you must set the NumberFormat property to a Date format, then put a real date into the cell's content.
Sub test_date_or_String()
Set c = ActiveCell
c.NumberFormat = "#"
c.Value = CDate("03/04/2014")
Debug.Print c.Value & " is a " & TypeName(c.Value) 'C is a String
c.NumberFormat = "m/d/yyyy"
Debug.Print c.Value & " is a " & TypeName(c.Value) 'C is still a String
c.Value = CDate("03/04/2014")
Debug.Print c.Value & " is a " & TypeName(c.Value) 'C is a date
End Sub