I have a table that users can log their use of a laboratory instrument. For the most basic function of logging current use, I have an error check that goes through a column that references the start and end times of the instrument use.
Basically, I want to compare the current time and the end time of the instrument use with previously submitted reservations/current instrument use. If the code detects that the current user input would interfere with a reservation, it changes the string value of "strCheckTime" from "ok" to "error".
Then a If Then statement later on registers that and prompts the user with a message box.
The code is listed below.
So far, I have not gotten it to work. No matter what Now() returns and what reservations are currently present, it will run through the If Then statement and change the string value of "strCheckTime" from "ok" to "error".
Any help would be most welcome!
'Defines and sets variables for current use/reservation check
Dim shtInstrument As Worksheet
Set shtInstrument = Worksheets(strShtName)
shtInstrument.Activate
Dim intCountEntries As Integer
intCountEntries = shtInstrument.Cells(shtInstrument.Rows.Count, "B").End(xlUp).Row
Dim CurrentTime As Date
Dim StartDate As Date
Dim StartTime As Date
Dim EndDate As Date
Dim EndTime As Date
Dim rngStart As Range
Dim rngEnd As Range
Dim strCheckTime As String
strCheckTime = "Ok"
'Checks if desired instrument use falls in time frame of current use or reservation
For Each rngStart In shtInstrument.Range("H9:H" & intCountEntries)
StartDate = DateValue(rngStart.Value)
StartTime = TimeValue(rngStart.Value)
EndDate = DateValue(rngStart.Offset(0, 1).Value)
EndTime = TimeValue(rngStart.Offset(0, 1).Value)
If StartDate <= Date <= EndDate Then
If StartTime <= Now() <= EndTime Then
strCheckTime = "Error"
ElseIf StartTime <= CurrentTime + TimeSerial(0, txtSample.Text * txtTime.Text, 0) <= EndTime Then
strCheckTime = "Error"
Else
strCheckTime = "Ok"
End If
Else
'Do nothing
End If
Next rngStart
The problem is in this line:
If StartTime <= Now() <= EndTime Then
The function "Now()" returns the entire date, including the time. This value is inherently always greater than the StartTime variable which represents only a time.
The second problem (that you figured out and mentioned in a comment) is that you can't use the equality operators like this.
The statement:
If x <= y <= z
will be evaluated as such:
If (x <= y) <= z
So this will be evaluated to:
If (TRUE/FALSE) <= z Then
But TRUE = -1 and FALSE = 0 so as long as z is bigger than z (which in your case it always is), your function will return true. You need to split the statements (again, per your comment).
You need to use either:
If StartTime <= Time() AND Time() <= EndTime Then
or
If StartTime <= TimeValue(Now()) AND TimeValue(Now()) <= EndTime Then
Better yet, you don't need to use the Date and Time separately:
StartTime = [cell where date and time are located]
EndTime = [cell where date and time are located]
If StartTime <= Now() AND Now() <= EndTime Then 'both time variables are defined by both the date and time
Note that to figure out problems like this, it is best to use a line such as:
debug.print StartTime
debug.print Now()
debug.print EndTime
debug.print (StartTime <= Now() <= EndTime)
to pinpoint where the problem is.
One additional comment is that if you are using things such as the Time throughout code, the Time function is evaluated when the code runs. So if you have a procedure that takes a bunch of time and at the beginning you check the time vs something and then at the end you use the Time/Date function to get the time to record somewhere, these values will be different. The best way to prevent this is to create a variable at the beginning of the code (or wherever you want to check the time:
currTime = Now()
and then use this variable throughout your code. In this particular case, the difference will either be extremely negligible or nonexistent as the check is in the same line of code, but in other cases it could be an issue.
EDIT***To include the additional problem of not being able to use equalities like this.
Related
I got two columns with Start and End times of shifts, I need a formula that returns if NOW() is on shift/off shift (eg. TRUE/FALSE)
Answers I found only using MEDIAN, AND+IF did not work as shift can start evening and finish day time. Anyone got an elegant solution for this?
Bare in mind cases when it is after midnight.
Use:
=MEDIAN(MOD($C$1,1),-AND(A2>B2,B2>MOD($C$1,1))+A2,AND(A2>B2,B2<MOD($C$1,1))+B2)=MOD($C$1,1)
You can replace all the $C$1 references with NOW(), or just put =NOW() in C1
Assuming you have =NOW() in C1 (which will include date and time) you can use this formula:
=(MOD(C$1,1)<B2)+(MOD(C$1,1)>A2)+(B2<A2)=2
This works because if the time in B2 is > the time in A2 [shift is on one day] then the first two conditions need to be TRUE....but if the time in B2 is < A2 [shift cuts across two days] then only one of those conditions needs to be TRUE (or can be TRUE). Either way 2 of the conditions need to be TRUE
If you use this formula in C1 which will return the current time without date
=NOW()-TODAY()
...then above formula can be shortened to this:
=(C$1<B2)+(C$1>A2)+(B2<A2)=2
see screenshot below
Since you tagged as vba & excel-vba, you can use a UDF:
Public Function onShift(rngStart As Date, rngEnd As Date) As Boolean
Application.Volatile
If rngStart > rngEnd Then
If Time < rngEnd Then 'After midnight & in-shift
rngStart = Date + rngStart - 1
rngEnd = Date + rngEnd
Else
rngStart = Date + rngStart
rngEnd = Date + rngEnd + 1
End If
If Now >= rngStart And Now <= rngEnd Then onShift = True
Else
If Time >= rngStart And Time <= rngEnd Then onShift = True
End If
End Function
But I would stick with worksheet functions as provided by Scott's answer.
The benefit of using a UDF however is that you are able to create easy-to-remember function names that does exactly what you need it to do.
Try:
=OR(AND(B2-A2<0,OR($C$1<=B2,$C$1-A2>=0)),AND($C$1>=A2,$C$1<=B2))
You can replace $C$1 with TEXT(NOW(),"hh:mm") to evaluate the current time.
Considering all compared values are in TIME format, you can try:
=MEDIAN(A2,IF(B2<A2,B2+1,B2),IF(C$1<A2,C$1+1,C$1))=IF(C$1<A2,C$1+1,C$1)
If however you need to compare it with current time using NOW(), you have to strip the time out of it like:
=NOW()-INT(NOW()) '/* this goes to C$1 */
I saw how well Scott did it and the logic is way too high (at least for me) so I decided to make something where the logic is pretty straight forward.
In a VBA project i'm working on, I need to count the number of lines where the difference between two dates is inferior to X hours.
My idea was to go through all the lines and check for each line, like this:
Dim count as Integer
if DateDiff("h", date1, date2) < 24 then
count = count + 1
End If
The problem is that I get an incompatibility type error (execution error 13).
Is it possible to make IF statements on a DateDiff function? Or is it maybe possible to make a filter with DateDiff as the condition?
Thanks for the help and sorry for my poor english as it's not my main language!
I have tested it like this without any errors.
Dim date1 As Date
Dim date2 As Date
date1 = "14/10/2015 19:00:00"
date2 = "14/10/2015 16:28:43"
If DateDiff("h", date1, date2) < 24 Then
count = count + 1
End If
The VBA conversion function CDate can apply a little of the overhead you constantly pay for to correct the dates. With that said, it is best to correct the data to begin with and not rely upon error controlled conversion functions.
Dim date1 As Date, date2 As Date, n As Long
date1 = CDate("14/10/2015 19:00:00")
date2 = CDate("14/10/2015 16:28:43")
Debug.Print date1 & " to " & date2
If IsDate(date1) And IsDate(date2) Then
If DateDiff("h", date1, date2) < 24 Then
n = n + 1
End If
'alternate
If date2 - date1 < 1 Then
n = n + 1
End If
End If
Given that 24 hours is a day and a day is 1, simple subtraction should be sufficient for this base comparison.
Your DMY format may cause problems with the EN-US-centric VBA in date conversion. If an ambiguous date string is found (e.g. "11/10/2015 16:28:43") you may find that you are interpreting it as MDY.
I am having two date time picker start time and end time.I have to calculate difference between time from this end time and start time.And also how to check am or pm for calculating time difference.
I have tried this by using timespan. It gives me right answer,but when I select time suppose as 12:58:02 in end time and 10:57:05 in start time, it gives me wrong answer like -599. I want to know how to convert this negative difference to positive.
I have tried with the following code:
Dim startTime As Date
Dim endTime As Date
startTime = Convert.ToDateTime(dtpOpStTime.Value)
endTime = Convert.ToDateTime(dtpOpETime.Value)
Dim diff As System.TimeSpan
diff = endTime - startTime
actualTime = Convert.ToString(Int32.Parse(diff.Hours.ToString()) * 60 + Int32.Parse(diff.Minutes.ToString()))
ucTime.TxtCode.Text = actualTime
my another issue is as follows:
I am having 4 date time picker,2 for showing start date and end date and 2 for start time and end time.so when i changed date timespan getting is wrong .so how to resolve this.
I have tey with the following code:
Dim span As TimeSpan = dtpOpEDate.Value - dtpOpStdate.Value
Dim span1 As TimeSpan = dtpOpETime.Value - dtpOpStTime.Value
Dim duration As TimeSpan = span.Duration()
Dim duration1 As TimeSpan = span1.Duration()
Dim durDay As Int32 = duration.Days
Dim durHr As Int32 = duration1.Hours
Dim durMin As Int32 = duration1.Minutes
Dim totalDur = durDay * 24 * 60 + durHr * 60 + durMin
actualTime = totalDur.ToString()
ucTime.TxtCode.Text = actualTime
Problem is through this I am getting wrong time difference ,when i change suppose start time 10:51:02 PM and end time to 3:51:04 AM and start date to 25-Mar-2014 and End date to 26-Mar-2014 then difference should be 5 hours though the day change but i am getting wrong difference
A difference between two DateTimes returns a TimeSpan object. You can either use the Subtract method of DateTime or even use -. You have to use the subtract the earlier date from the later date:
Dim span As TimeSpan = dtpEnd.Value - dtpStart.Value
You can use the Duration method to get an absolute timespan, then the order doesn't matter:
Dim duration As TimeSpan = (dtpStart.Value - dtpEnd.Value).Duration()
If you for example want the number of minutes between, use the TotalMinutes property:
Dim minutes As Int32 = CInt(duration.TotalMinutes)
I have four different DateTime boxes. Two boxes just displays the Date and the other two just Displays the time
If the current time is between midnight and six am I want the Date in the date box to be the day before.
I have it all, I'm just missing the part that compares the two.
Dim currentTime As DateTime = Now
'default date
If deMaxDate.Value = Nothing Then
deMaxDate.Value = Now
End If
If deMinDate.Value = Nothing Then
If currentTime.Hour < TimeOfDay.Hour Then
'THIS IF STATMENT IS WRONG - HOW CAN I CHECK IF ITS BETWEEN 12AM AND 6 HERE
deMinDate.Value = (Now - TimeSpan.FromDays(1))
Else
deMinDate.Value = Now
End If
End If
'default time
If teMaxTime.Value = Nothing Then
teMaxTime.Value = Now
End If
If teMinTime.Value = Nothing Then
teMinTime.Value = (Now - TimeSpan.FromHours(6))
End If
My comment by the third if statment is where I'm stuck at.
DateTime is a double datatype? Something like
if currentTime.Hour < TimeOfDay.Hour.Equals(6)
?
Your rule, if I understand this, is that you want to look at the current time. If the current time is between 12AM(0000) and 6AM(0600), then you want to use yesterday as the active date.
Dim current as DateTime = now()
Dim activeDate as DateTime = current
if current.Hour < 6 then
activeDate = current.AddDays(-1)
end if
Although, if you're not really interested in the hours part of the date except for this business rule, you could always just do date.AddHours(-6).
I do this in a similar situation where I want to check the current time is prior to 4 AM.
If (DateTime.Now.Hour < 4) Then
'do something
End If
Just use
If currentTime.Hour <= 6 Then
deMinDate.Value = Now.AddDays(-1)
Else
deMinDate.Value = Now
End If
or indeed...
If currentTime.Hour <= 6 Then
deMinDate.Value = Now.Date.AddDays(-1)
Else
deMinDate.Value = Now.Date
End If
if you don't want the time bit because .Now contains a time element as well as a date element.
Have a look at http://msdn.microsoft.com/en-us/library/5ata5aya.aspx. It might apply to your case
currentTime.Hour < TimeOfDay.Hour.Equals(6)
ended up being the answer
In vb.net I have two data values as shown below:
Dim startp as datetime
Dim endp as datetime
I have a function called ProcessData(soemdate) which processes dataporting.
In VB.net is there a way I can do something like
For each day between startp and endp
ProcessData(soemdate)
Next
Thanks
Here is another way to do this.
Dim startP As DateTime = New DateTime(2009, 1, 1)
Dim endP As DateTime = New DateTime(2009, 2, 1)
Dim CurrD As DateTime = startP
While (CurrD <= endP)
ProcessData(CurrD)
Console.WriteLine(CurrD.ToShortDateString)
CurrD = CurrD.AddDays(1)
End While
For Each Day As DateTime in Enumerable.Range(0, (endp - startp).Days) _
.Select(Function(i) startp.AddDays(i))
ProcessData(Day)
Next Day
Adding to Joel Coehoorn's answer which I personally think should be the accepted answer as I always try to avoid While loops no matter how safe they may appear. For...Each is a much safer approach although the enumerable isn't very pretty in-line. You can however move it to a function to keep things more readable, plus you can re-use as needed.
For Each Day As DateTime In DateRange(StartDate, EndDate)
ProcessData(Day)
Console.WriteLine(Day.ToShortDateString)
Next
Public Shared Function DateRange(Start As DateTime, Thru As DateTime) As IEnumerable(Of Date)
Return Enumerable.Range(0, (Thru.Date - Start.Date).Days + 1).Select(Function(i) Start.AddDays(i))
End Function
I also added 1 to Enumerable range since as Joel had it, it wouldn't return the end date and in my situation I needed it to return all dates in the range including the start and end days.
Enumerable.Range is a sort of loop in itself that adds i days to the startdate advancing i with each call from in this case 0 to the difference between start and end days + 1. So the first time it's called you get the result of Start.AddDays(0), next you'll get Start.AddDays(1) and so on until the range is complete.
You can easily loop through each day if you convert your dates to OLE Automation Date OADate where the left portion represents the day and the right portion represents the time.
For example #06/19/2018#.ToOADate converts to 43270.0
For loopDate As Double = #06/19/2018#.ToOADate To #07/01/2018#.ToOADate
Dim thisDate As Date = DateTime.FromOADate(loopDate)
' Do your stuff here e.g. ProcessData(thisDate)
Next
Yes, you can use an accumulator date:
Dim Accumulator as DateTime
Accumulator = startp
While (Accumulator <= endp)
Accumulator = Accumulator.AddDays(1)
End While
Not tested, and I'm a C# programmer, so be easy if my syntax is wrong.
For those that come looking later, I had to add a +1 to the Range endpoint to get this to work for when the start and end were the same. Here is the code I used.
For Each Day As DateTime in Enumerable.Range(0, (endp - startp).Days + 1) .Select(Function(i) startp.AddDays(i))
'Do work here
Next Day
Set a calendar table with all dates and query values from there.
SQL:
Select Date as MyDate from tblCalendar Where Date >= StartDt And Date <= EndDate
.NET:
While Reader.read
process(MyDate)
End While