I have been given a list of enrollment dates for some sources for an assignment.
They want me to change the dates using vba
The question is:
"As with the previous assignment that the Director gave you, the enrollment date is in a “not normal” format. The director wants the date changed into the “normal” Danish format “DD-MM-YYYY”. You, therefore, must create a sub/function that changes the date in the field to the “correct” format. As the function needs to be able to run multiple times you should use the appropriate selection statements to check that the date is in the “not normal” format before converting it."
I have attached a picture of the excel
This function appends a 20 before the year part if its only 2 characters.
Function DateConvert(BtVal dt As String) As String
Dim parts As String()
parts = Split(dt, "-")
dd = parts(1)
mm = parts(2)
yy = parts(3)
DateConvert = dd & "-" & mm & "-" & IIf (Len(yy) = 2, "20" & yy, yy)
End Function
You might want to extend this logic, for example "65" can be replaced with "1965" instead of "2065", but I leave that part to you.
The function will test whether the string matches the pattern. If it does, it returns formatted date, otherwise it returns same cell's value.
Function GetDate(cell)
With CreateObject("VBScript.RegExp")
.Pattern = "^(\d{2})-(\d{2})-(\d{2})$"
With .Execute(cell)
If .Count > 0 Then
With .Item(0)
GetDate = Format(DateSerial(.SubMatches(2), .SubMatches(1), .SubMatches(0)), "dd-MM-yyyy")
End With
Else
GetDate = cell
End If
End With
End With
End Function
I am trying to pull all data entries that are within a userform selected month and year. I can get the code to run fine when I hard code the year but I want the year to come off of a text box. I converted the Textbox value to an integer using Cint() and dim'd it to "Year" in my if statement. I can get it to work if I write Cdate("3/1/2016"), but I want see if there is a way to run it like: Cdate("3/1/Year"). I tried it this way and get a typematch error on the Cdate Im pretty new to VBA so excuse my stupidity.
Ignore the "Month" variable I was just using that to put a stop on the code and step it through to see if it would enter my if statement.
Thanks in advance.
My Code
Private Sub OKBtn_Click()
Dim Sales As Range
Dim Year As Integer
Dim Month As Integer
Dim i As Integer
Year = CInt(YearText.Value)
Set Sales = Worksheets("Sales").Range("A4")
i = 0
If Sales.Offset(i, 1).Value >= CDate("3/1/2016") And Sales.Offset(i, 1).Value <= CDate(" 3/31/2016 ") Then
Month = 1
End If
In order for the CDate to work, you need to seperate the stings inside the brackets to 2 parts
1.The constant, in your case "3/1/".
2.And the variable, CInt(YearText.Value).
Option Explicit
Private Sub OKBtn_Click()
Dim DDate As Date
DDate = CDate("3/1/" & CInt(YearText.Value))
' for debug only
MsgBox "Date entered is :" & DDate
End Sub
I am currently learning VBA programming by doing, and have encountered the below situation with which I would appreciate your help. Ideally not just in finding a solution, but also to understand how and why the solution works.
Say that there is a database from which one can export a spreadsheet of data. One of the columns has date values, but they are badly formatted from the export. The system sends the dates as mm/dd/yyyy hh:mm AM/PM, for example, 04/11/2014 09:24 AM, but the spreadsheet has this identified as dd/mm/..., meaning it enters 04 as the day and 11 as the month.
Within this column, if the day is before or including 12 of the month, the cell is formatted as date. If the day is past the 12th, the cell is formatted with a general format.
My question is, could I write a VBA macro that could reverse the values for day and month and create the correct dates in a new column? I would think that it would first have to identify if a cell is formatted as date, and then somehow extract the date and month in the correct positions, or if it's formatted as a general format, and then use a different code to extract the correct date.
If this is too basic an issue for this community and there's another community more suited, I will gladly repost my question there.
EDIT:
After my comment below I played around with functions and looked for other similar functions that may help do what I need, switch the day value with the month value, and so far I have:
'for dates with general format: 04/14/2014 11:20 AM
=DATE(MID(A1,7,4),LEFT(A1,2),MID(A1,4,2)) 'in a column for the date
=TIME(MID(A1,12,2),MID(A1,15,2),"00") 'in a column for time, since I may need this
'for dates with a date format: 4/11/2014 7:35:00 PM
=DATE(TEXT(A1,"yyyy"),TEXT(A1,"dd"),TEXT(A1,"mm")) 'in a column for the date
=TEXT(A1,"hh:mm AM/PM") 'in a column for time
Now I just need to figure out a conditional function to identify when to apply each set of formulas according to the values or formatting or column A.
But are there equivalent functions to achieve this through VBA? I need these date and time columns to only hold values, not formulas, so that I may export the data out of them directly. And somehow putting this in VBA code seems more "clean" to me, using formulas feels to me like a volatile solution. I'm not sure how to explain this properly, but I'm somehow more confortable with proper coding behind my data manipulation.
EDIT2:
I've resolved the worksheet functions solution as below. It took me a while to figure out how to go around the FIND error with date formatted cells, and only found the IFERROR function by chance in the list Excel suggests when writing =IF.
'to get the correct date
=IF(IFERROR(FIND("/",A1),0)>0,DATE(MID(A1,7,4),LEFT(A1,2),MID(A1,4,2)),DATE(TEXT(A1,"yyyy"),TEXT(A1,"dd"),TEXT(A1,"mm")))
'to get the correct time
=IF(IFERROR(FIND("/",A1),0)>0,TIME(MID(A1,12,2),MID(A1,15,2),"00"),TEXT(A1,"h:mm AM/PM"))
Now at least I have a working solution, but I'm still interested in a VBA translation for these formulas and will continue searching for these.
Check this out. Let's take for example your formula:
=IF(IFERROR(FIND("/",A1),0)>0,DATE(MID(A1,7,4),LEFT(A1,2),MID(A1,4,2)),DATE(TEXT(A1,"yyyy"),TEXT(A1,"dd"),TEXT(A1,"mm")))
VBA equivalent functions:
Find = Instr
Date = DateSerial
Text = Format (not exactly the same but the nearest)
Code equivalent:
Dim mydate As Date
Dim myformat As String
myformat = "mm/dd/yyyy hh:mm AM/PM"
If InStr(1, [A1], "/") > 0 Then
mydate = DateSerial(Mid(Format([A1], myformat), 7, 4), _
Left(Format([A1], myformat), 2), Mid(Format([A1], myformat), 4, 2))
Else
mydate = DateSerial(Year([A1]), Month([A1]), Day([A1]))
End If
[B1] = mydate
Take note that [A1] is a shortcut Evaluate function which can also be written as Evaluate("A1").
I used that to refer to Cell A1 as in your formula. You can use the conventional Range Object reference like this: Range("A1"). I used the shortcut because it looks cleaner. But it is not advisable in huge data sets.
For your time formula:
=IF(IFERROR(FIND("/",A1),0)>0,TIME(MID(A1,12,2),MID(A1,15,2),"00"),TEXT(A1,"h:mm AM/PM"))
Code Equivalent:
Dim mytime As Date
If InStr(1, [A1], "/") > 0 Then
mytime = TimeValue([A1])
Else
'~~> myformat is declared above
mytime = TimeValue(Format([A1], myformat))
End If
[C1] = mytime
You can also check the format of the cell like below:
Select Case True
Case [A1].NumberFormat = "General"
mydate = DateSerial(Year([A1]), Month([A1]), Day([A1]))
mytime = TimeValue(Format([A1], myformat))
Case [A1].NumberFormat = myformat '~~> again this is declared above
mydate = DateSerial(Mid(Format([A1], myformat), 7, 4), _
Left(Format([A1], myformat), 2), Mid(Format([A1], myformat), 4, 2))
mytime = TimeValue([A1])
Case Else
MsgBox "Invalid Format. Cannot be evaluated"
End Select
[B1] = mydate: [C1] = mytime
Not sure if above will really solve your problem.
There are just many possibilities when you extract datetime stamp from a database.
If the scenarios you mentioned are only the problems you encounter, then above solutions might work.
This is now an old thread but in case anyone else stumbles upon it (as I did) with a similar problem, I'm just offering this up.
My suggested VBA function for this is shown below. Its style doesn't strictly follow purist programming practice (declaration of variables, etc); it's written, rather, to be relatively easily comprehensible.
Function Date_Text_Convert( _
date_text As String, _
return_with_month_letters As Boolean, _
return_as_date_time_value As Boolean)
' Patrick S., June 2018
' Intention: to enable mm/dd/yyyy[etc] imported text-string dates
' to be switched to dd/mm/yyyy[etc]. Can be adapted for other cases.
' Usage examples: if cell A2 contains the text-string:
' 06/26/2018 09:24 AM
' then in, for example, cell B2, type:
' =Date_Text_Convert(A2,TRUE,FALSE) or =Date_Text_Convert(A2,FALSE,FALSE)
' which returns:
' 26-Jun-2018 09:24 am or 26/06/2018 09:24 am
' To return a date-and-time value instead of a string, use, for example:
' =Date_Text_Convert(A2,TRUE,TRUE)
' establish the positions where the day and month digits start
daypos = 4
mthpos = 1
rempos = 7 ' starting position of remaining part of the string
' establish the length of the relevant text sections: 2 characters each, in this case
daylen = 2
mthlen = 2
' so that,
daytext = Mid(date_text, daypos, daylen)
mthtext = Mid(date_text, mthpos, mthlen)
remtext = Mid(date_text, rempos, 999) ' the remainder of the text string
' format the output according to 'return_with_month_letters'
' there are 2 options available, each using a different separator
sep_stroke = "/"
sep_hyphen = "-"
If return_with_month_letters = True Then
mthnum = mthtext * 1
mthindex = ((mthnum - 1) * 3) + 1
mthname = Mid("JanFebMarAprMayJunJulAugSepOctNovDec", mthindex, 3)
newtext = daytext & sep_hyphen & mthname & sep_hyphen & LCase(remtext) ' LCase optional
Else
newtext = daytext & sep_stroke & mthtext & sep_stroke & UCase(remtext) ' UCase optional
End If
' finally, return the output through the function name: either as a date, or as the text equivalent
If return_as_date_time_value = True Then
newdate = DateValue(newtext) + TimeValue(newtext)
Date_Text_Convert = newdate
Else
Date_Text_Convert = newtext
End If
End Function
Does anyone know how to get the earliest date on a row in Microsoft Excel. There is not a predictable number of columns on each row, and there are values other than dates which need to be ignored. It could be done with Excel formulas or VBA.
Does anyone have any suggestions?
Right now I am using this quick and dirty VBA function, but when I loaded new input data (approx 200 rows by 100 columns) a message box came up saying that Excel does not have enough resources to process my changes.
' returns smallest date on row
Public Function getSmallestDateFromRow(r As Integer, sheetName As String) As Date
Dim toReturn As Date
Dim rng As Range
Dim scanToColumn As Integer
Dim c As Integer
Dim tmp As Variant
Set rng = Sheets(sheetName).Cells.Find("*", [a1], , , xlByColumns, xlPrevious) 'is this inefficient?
scanToColumn = rng.Column
toReturn = #12/12/2100#
For c = 1 To scanToColumn
tmp = Sheets(sheetName).Cells(r, c).Value
If (IsDate(tmp)) Then
If (toReturn = Null) Then
toReturn = tmp
ElseIf (toReturn > tmp) Then
toReturn = tmp
End If
End If
Next c
If (toReturn = #12/12/2100#) Then
toReturn = 0
End If
getSmallestDateFromRow = toReturn
End Function
You have to remember that Excel (and many other Microsoft products) store dates as floating-point numbers:
The integer part is the count of days elapsed since January 1st, 1900 (e.g.: 1 is equivalent to 1/1/1900)
The decimal part is the 'fraction' of day elapsed (e.g.: 0.5 is equivalent to 12:00 pm)
The question on how to find a minimum or maximum date value is then obfuscated by the fact that you may have many other numbers in your row. So you have to define first a "valid rank" for the dates. After that, a simple "array formula" can do the trick:
Example. Let's say your valid range is between January 1st, 2000 and December 31st, 2100. Then your valid "number rank" is:
1/1/2000 is equivalent to 36526
12/31/2100 is equivalent to 73415
Now you can write the function to track the minimum date value within this range:
function trackMinimum(rowRange as range) as date
on error resume next
dim j as integer, minValue as date
dim t0 as double, t1 as double
dim ans as date
t0 = cdbl(dateserial(2000,1,1))
t1 = cdbl(dateserial(2100,12,31))
ans = 0
for j = 1 to rowRange.columns.count
if ans = 0 then ' You need to store the first valid value
if rowRange.cells(1,j).value >= t0 and rowRange.cells(1,j) <= t1 then
ans = rowRange.cells(1,j).value
end if
else
if (rowRange.cells(1,j).value >= t0 and rowRange.cells(1,j) <= t1) _
and rowRange.cells.value < ans then
ans = rowRange.cells(1,j).value
end if
end if
next j
trackMinimum = ans
end function
Of course I am assuming that the rowRange parameter is a single row range.
Hope this helps you
By the way, the isDate() function is not "fail safe": It must take a valid expression (text) to detect if it is a date or not, and that may depend on formatting. That said, you may prefer to use isDate(.cells(r,c).Text)' instead of.value, since theValue` property may return a double or floating-point value that can be evaluated as "not a date".
I have a column of dates. These will be incorporated into objects, one per row, along with other data in the row.
However, some of the cells in the date column are empty. As I test I create a date variable and set its value as the value of an empty cell. I msgbox'd the new value of the date and found it was "12:00 AM". Is there a constant built into VBA to represent this value, so I can do tests similar to:
If myDate = vbNullDate Then
I run into this problem from time to time. I usually do the below:
dim d as date
if d = cdate(0) then msgbox "Default value"
The default value for a local Date variable seems to be:
Saturday, December 30, 1899 12:00:00 AM
based on this procedure:
Sub testdate()
Dim d As Date
Debug.Print Format$(d, "Long Date") & " " & Format$(d, "Long Time")
End Sub
To check the default value for any variable type, simply declare a variable of that type, then print it's value.
Sub testint()
Dim d As Integer
Debug.Print d
End Sub
As andy holaday said in the comment below the question, the default value of a date is simply 0. Silly me :/