Excel VBA Storing time specific value into cell from userform - vba

I have a userform that requires the user to input a specific date and time through two separate comboboxes, cboStartDate, cboStartTime. The user will also have to input the duration in a text field, txtDuration.
Upon saving, the start date and time will be stored in a formatted cell [DD/MM/YYYY HH:MM AM/PM]. The end date and time will be calculated from the duration field and stored in another cell with the same formatting. Something like this:
+-----------------------+-----------------------+
| startTime | endTime |
+-----------------------+-----------------------+
| 2/4/2012 11:30:00 AM | 2/4/2012 2:00:00 PM |
+-----------------------+-----------------------+
However, after running the userform through, the start time is not stored, and the end time is not calculated. Something like this:
+-----------------------+-----------------------+
| startTime | endTime |
+-----------------------+-----------------------+
| 2/4/2012 12:00:00 AM | 2/4/2012 12:00:00 AM |
+-----------------------+-----------------------+
Below is my part of my VBA code:
Dim iRow As Long
Dim ws As Worksheet
Dim startDate As Date
Dim unFmtStartDuration() As String
Dim startDuration As Double
Dim minTest As Integer
Dim endDate As Date
Dim endDuration As Double
Set ws = Worksheets("EVENTS")
'Search for the last row in the worksheet
iRow = ws.Cells(Rows.Count, 1).End(xlUp).Offset(1, 0).Row
'Date manipulation and set start and end timings
unFmtStartDuration() = Split(cboStartTime.Text, ":")
startDuration = unFmtStartDuration(0)
If unFmtStartDuration(1) = "00" Then
minTest = 0
Else
minTest = unFmtStartDuration(1)
If minTest = 30 Then
startDuration = startDuration + 0.5
End If
End If
startDate = DateValue(DateAdd("h", startDuration, cboDate.Text & " 12:00AM"))
ws.Cells(iRow, 4).Value = startDate
endDuration = txtDuration.Value
endDate = DateValue(DateAdd("h", endDuration, startDate))
ws.Cells(iRow, 5).Value = endDate
So how can I get this part sorted out? Would appreciate any help here. Thanks.
P.S. Would like to post screenshots here, but my reputation here is too low for it. Sorry.

It looks like you are only adding the time when minTest = 30, but this value probably varies quite a bit. Also, in one instance, you are comparing a string, and another a number when referencing unFmtStartDuration, which may work, but is confusing when reading your code.
To follow your current method, use
startDuration = Val(unFmtStartDuration(0) + Round(Val(unFmtStartDuration(1)) / 60, 2)
to replace this
startDuration = unFmtStartDuration(0)
If unFmtStartDuration(1) = "00" Then
minTest = 0
Else
minTest = unFmtStartDuration(1)
If minTest = 30 Then
startDuration = startDuration + 0.5
End If
End If
This will take whatever the time is and convert it to the decimal form you are using, instead of relying on the 30 match. (Unless you need that specifically. If so, say so, as I think this can still be arranged with rounding tricks.)
However, I think a better option would be to use
startDuration = TimeValue(cboStartTime.Text) * 24
So no other math or checks are involved.
Also, unless cboStartTime.Text (and subsequently startDuration) is greater than 24 hours, this
startDate = DateValue(DateAdd("h", startDuration, cboDate.Text & " 12:00AM"))
will always return the date specified in cboDate.Text with an implied 12:00:00 AM. To correct this, you will want to change to
startDate = DateAdd("h", startDuration, cboDate.Text & " 12:00AM")
I think there is some more to fix, but hopefully this gets you going in the right direction...

Related

VB.NET : Populating Gridview with Dates (30 days interval)

I am trying to populate a gridview with dates.
For example the date today is 2/18/2019 and the next date within 30 days is 3/20/2019. so the desired output shoul'd look like this
No. Date
1 3/20/2019
2 4/19/2019
3 5/19/2019
etc.
but the result is
No. Date
1 3/20/2019
2 3/20/2019
3 3/20/2019
etc.
Here is my code so far.
Dim termCounter As Integer = 36
Dim today As DateTime = DateTime.Today
Dim dueDate As DateTime = today.AddDays(30)
Dim dtable As DataTable = New DataTable()
dtable.Columns.Add("No.")
dtable.Columns.Add("Payment Date")
Dim RowValues As Object() = {"", ""}
Dim dRow As DataRow
Dim tmpDate As Date
For i As Integer = 1 To termCounter
If GridAmortSched.RowCount <= 0 Then
RowValues(0) = i
RowValues(1) = dueDate.ToShortDateString
dRow = dtable.Rows.Add(RowValues)
Else
tmpDate = GridAmortSched.Rows(GridAmortSched.RowCount - 1).Cells(1).Value.ToString()
RowValues(0) = i
RowValues(1) = tmpDate.AddDays(30).ToShortDateString
dRow = dtable.Rows.Add(RowValues)
End If
Next
dtable.AcceptChanges()
GridAmortSched.DataSource = dtable
As is often the case, the code is behaving as you have told it to do, not at you want it to do :-).
Every time you go through the loop, you set tmpDate from the same source and then two lines later, you put the value into RowValues. :
tmpDate = GridAmortSched.Rows(GridAmortSched.RowCount - 1).Cells(1).Value.ToString()
You do not adjust your source by i, nor do you increment it. When you AddDays, you do not adjust the original value either (which would not work because of how and when you assign it).
Two easy fixes (two options)
One. The first is to index the days to be added against the loop value - the simple modification to the code below will achieve this. It is a quick fix and may be harder to maintain in the future - more to the point, is harder to spot if you change something such as the loop starting point.
RowValues(1) = tmpDate.AddDays(30*i).ToShortDateString
Two. The other option is to address the code logic. Set your baseline value and then increment it within the loop.
Dim tmpDate As Date
tmpDate = GridAmortSched.Rows(GridAmortSched.RowCount - 1).Cells(1).Value.ToString() '*** Moved outside of loop
For i As Integer = 1 To termCounter
If GridAmortSched.RowCount <= 0 Then
RowValues(0) = i
RowValues(1) = dueDate.ToShortDateString
dRow = dtable.Rows.Add(RowValues)
Else
RowValues(0) = i
tmpDate = tmpDate.AddDays(30) '*** Increment date every time it passes here
RowValues(1) = tmpDate.ToShortDateString '*** Assign the new value
dRow = dtable.Rows.Add(RowValues)
End If
Next
dtable.AcceptChanges()
GridAmortSched.DataSource = dtable

Excel VBA / Formula . Fill a range of cells with each Friday of the current month

So the titles a bit of a mess but im trying to do the following:
I have a range of cells from Q8:Q12, T8:T12, Q16:Q20 and T16:T20
Im trying to have these cells populate with the date for each friday of this current month. Essentially, using July (The current month) it would look something like this:
Q8/T8 = 06/07
Q9/T9 = 13/07
Q10/T10 = 20/07
Q11/T11 = 27/07
Q12/T12 = -
The reason Q/T12 would be blank is to handle months of the year that have 5 fridays in them rather than 4. Its kind of a way of error handling.
I have a cell that currently tracks the month within the Cell A9 and the formula looks like this:
=TEXT(NOW(),"mmmm")
Im not quite sure how to handle this logically really. Either VBA or a Formula would do in my eyes.
Ideally, because i have a different sheet for every month July, August etc. The formula above changes depending on what month it is currently. I would need to convert the Formula/VBA script from cells Q8:Q12 OR A9 into a value AFTER populating the date range cells Q8:Q12.
Anyone have any ideas. Im sorry its a bit of a messy question
excel-formula
Put this in the first cell and copy down 5
=IFERROR(AGGREGATE(15,6,ROW(INDEX(A:A,EOMONTH(TODAY(),-1)+1):INDEX(A:A,EOMONTH(TODAY(),0)))/(WEEKDAY(ROW(INDEX(A:A,EOMONTH(TODAY(),-1)+1):INDEX(A:A,EOMONTH(TODAY(),0))),1)=6),ROW(1:1)),"-")
Then format to your specifications.
There are probably more elegant formulas but this is what came to mind.
Here is a macro version without needing a date value in Range("A9")...
Dim SoM As Date
Dim EoM As Date
Dim rw As Long
SoM = DateSerial(Year(Now), Month(Now) + 0, 1)
EoM = DateSerial(Year(Now), Month(Now) + 1, 0)
rw = 8
While SoM < EoM
If Weekday(SoM) = vbFriday Then
Cells(rw, 17).Value = SoM
Cells(rw, 17).NumberFormat = "m/d/yyyy"
rw = rw + 1
End If
SoM = SoM + 1
Wend
I made a user defined function that works with any date range, then show how it could be applied to this example with a few formulas. This would account for year to year transitions.
Function DAYOFWEEKFREQUENCY(ByVal dayOfWeekType As String, ByVal startDate As String, ByVal endDate As String) As Long
Dim myStartDate As Date
myStartDate = CDate(startDate)
Dim myEndDate As Date
myEndDate = CDate(endDate)
Dim includeStartDate As Long
includeStartDate = 1
Dim daysBetweenDatesInclusive As Long
daysBetweenDatesInclusive = Application.WorksheetFunction.Days(endDate, startDate) + includeStartDate
Dim vbStartDay As Long
vbStartDay = Weekday(startDate)
Dim dateCheckedIncremented As Date
dateCheckedIncremented = myStartDate
For dayCounter = 1 To daysBetweenDatesInclusive
If Weekday(dateIncrementedChecked) = dayOfWeekType Then
DAYOFWEEKFREQUENCY = DAYOFWEEKFREQUENCY + 1
End If
dateIncrementedChecked = DateAdd("d", 1, dateIncrementedChecked)
Next
End Function

Compare dates to fill a cell using VBA Excel

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

VBA - Checking date string validity

I have a string in the following format in Excel.
07/12/2015 08:00 - 08/12/2015 09:00
I want to check if the current date fits between the two (these dates are validity dates, meaning I have to check if the current date is bigger than the first date and smaller than the second date).
I sometimes also have this string without hours, so like:
07/12/2015 - 08/12/2015
so I have to check that as well (just without the hours).
I split the dates using the Split function to split by the "-" character. However, I'm not sure how to do the check because I've never worked with dates.
Can anyone show me how to do this? It seems that it'd be complicated with the check for the hours.
You can try this :
Dim mydate as String, splitdate as Variant
mydate = "07/12/2015 08:00 - 08/12/2015 09:00"
splitdate = Split(mydate, "-")
If Date < splitdate(0) And Date > splitdate(1) Then MsgBox "Is Between"
In case there will be some issues with understanding date formats, you can still use Cdate function -
If Date < CDate(splitdate(0)) ...
Assuming, that you String is located in A1, and the Date you want to check for is in B1, then put this formula in C1:
=IF(AND(B1>=DATEVALUE(LEFT(A1,10)),B1<=DATEVALUE(MID(A1,FIND("-",A1)+2,10))),TRUE,FALSE)
This works for both cases, if you are only interested if the date lies between the dates (thus excluding the time).
Use cdate function. You wrote you already used the split function, so all you need to do is put the separate date strings into date variables using cdate ('c' stands for cast).
So
Dim d1 as Date
Dim d2 as Date
d1 = CDate(splitstring(0))
d2 = CDate(splitstring(1))
Then you can check the given date.
With the CDate() conversion function and Trim (to get rid of useless spaces), here is a boolean function that you can easily use to test if you are in the time lapse described by your string.
Here is how to use it :
Sub test_Gilbert_Williams()
Dim TpStr As String
TpStr = "08/12/2015 08:00 - 08/12/2015 09:00"
'TpStr = "07/12/2015 - 08/12/2015"
MsgBox Test_Now_Date_Validity(TpStr)
End Sub
And the function :
Public Function Test_Now_Date_Validity(Date_Lapse As String) As Boolean
Dim A() As String, _
Date1 As Date, _
Date2 As Date
If InStr(1, Date_Lapse, "-") Then
A = Split(Date_Lapse, "-")
Debug.Print Trim(A(0)) & " " & CDate(Trim(A(0)))
Debug.Print Trim(A(1)) & " " & CDate(Trim(A(1)))
If CDate(A(0)) > CDate(A(1)) Then
Date1 = CDate(A(1))
Date2 = CDate(A(0))
Else
Date1 = CDate(A(0))
Date2 = CDate(A(1))
End If
If Now > Date1 And Now < Date2 Then
Test_Now_Date_Validity = True
Else
Test_Now_Date_Validity = False
End If
Else
Exit Function
End If
End Function

I am getting Run-Time Error '13': Type Mismatch

I am having problems with my syntax below. I keep on getting Run Time error.
Please advise.
Dim a As Date Dim b As Date Dim c As Double
If Val(txtStart.Text) = Null Or Val(txtEnd.Text) = Null Then
Cells(eRow, 6).Value = txtEffort.Value
Else
a = Format(txtStart.Text, "hh:mm AMPM")
b = Format(txtEnd.Text, "hh:mm AMPM")
c = Abs(b - a) * 24
Cells(eRow, 6).Value = c
End If`
Your prompt response is much appreciated! Thanks!
You cannot throw text that looks like a date, time or datetime into the Format function without converting them to real dates.
a = Format(CDate(txtStart.Text), "hh:mm AMPM")
b = Format(CDate(txtEnd.Text), "hh:mm AMPM")
You could also use TimeValue for the time. The DateValue function will only see the date portion of the text string. CDate sees both the date and time.
Addendum:
You will have the same problem with c = Abs(b - a) * 24. The output of the Format function is text so both a & b will now be text representations of time. You need to convert them back to doubles representing time with c = Abs(TimeValue(b) - TimeValue(a)) * 24.
You haven't provided much in the way of detail. There is probably a better way to do this.
Judging by the code posted, time is being compared.
The problem arises because Abs expects a number, but in fact, a string is subtracted from a string, which causes an exception to throw.
If a and b are string variables holding values like "16:00" that are later formatted into "4:00", I suggest implementing a function to calculate the time in minutes, and then their difference.
Here is the function:
Function ConvertToMinutes(ByVal time_str As String) As Integer
ConvertToMinutes = 0
On Error GoTo Ende
splts = Split(time_str, ":")
ConvertToMinutes = CInt(splts(0)) * 60 + CInt(splts(1))
Ende:
On Error GoTo 0
End Function
Then, the code will look like
a = Trim(Format(txtStart.Text, "hh:mm AMPM"))
b = Trim(Format(txtEnd.Text, "hh:mm AMPM"))
C = Abs(ConvertToMinutes(b) - ConvertToMinutes(a)) * 24
Please adjust the calculations so that you get the required result.