This is the follow up post of this question and this question
I have created the following VBA function to calculate the seconds(also count the milliseconds) from two datetime.
Function:
Public Function ConvertDate(D1 As String, D2 As String) As Date
Dim StrD1 As Date
Dim StrD2 As Date
StrD1 = CDate(Left(D1, 10) & " " & Replace(Mid(D1, 12, 8), ".", ":"))
StrD2 = CDate(Left(D2, 10) & " " & Replace(Mid(D2, 12, 8), ".", ":"))
ConvertDate = DateDiff("s", StrD2, StrD1)
End Function
Scenario 1:
Given Dates:
2011-05-13-04.36.14.366004
2011-05-13-04.36.14.366005
Getting Result:
0
Expected Result:
0.000001
Scenario 2:
Given Dates:
2011-05-13-04.36.14.366004
2011-05-13-04.36.15.366005
Getting Result:
1
Expected Result:
1.000001
Scenario 3:
Given Dates:
2011-05-13-04.36.14.366004
2011-05-13-04.37.14.366005
Getting Result:
60
Expected Result:
60.000001
A day is 1. A date is 1 for every day past 31-Dec-1899. Today happens to be 42,556. Time is a decimal portion os a day. Today at noon will be 42,556.5 and today at 06:00 pm will be 42,556.75.
There are 24 hours in a day, 60 minutes in an hour and 60 seconds in a minute. That means that there are 86,400 seconds in a day (24 × 60 × 60) and a second is ¹⁄₈₆₄₀₀ (0.0000115740740740741) of a day. Excel's 15 digit floating point calculation sometimes fouls up (loses small amounts) time calculations due to the base-24 and base-60 numbering system.
Dim tm1 As String, tm2 As String
Dim dbl1 As Double, dbl2 As Double
Dim i As Long
With Worksheets("Sheet9")
For i = 1 To .Cells(.Rows.Count, "A").End(xlUp).Row Step 2
tm1 = .Cells(i, "A").Text
tm2 = .Cells(i + 1, "A").Text
dbl1 = CLng(CDate(Left(tm1, 10))) + _
TimeValue(Replace(Mid(tm1, 12, 8), Chr(46), Chr(58))) + _
(CDbl(Mid(tm1, 20)) / 86400)
dbl2 = CLng(CDate(Left(tm2, 10))) + _
TimeValue(Replace(Mid(tm2, 12, 8), Chr(46), Chr(58))) + _
(CDbl(Mid(tm2, 20)) / 86400)
.Cells(i + 1, "B") = (dbl2 - dbl1) * 86400
.Cells(i + 1, "B").NumberFormat = "0.000000"
Next i
End With
The above takes your time-and-date-as-text and calculates a pseudo-DateDiff to an accuracy of a millionth of a second. The results are displayed in seconds as an integer with fractions of a second as a decimal.
Related
I want to calculate the difference in hh:mm:ss between Now and column E. They both appear in format dd/mm/yyyy hh:mm. With the code I have written below, it only takes into consideration the hh:mm and not the days. So, if they have 2 days difference in wont add to the hours +48. The code is below:
With ws1.Range("N2:N" & lastrow1)
.Formula = "=TIME(HOUR(NOW()),MINUTE(NOW()),SECOND(NOW()))-TIME(HOUR(E2),MINUTE(E2),SECOND(32))"
End With
Just use =(NOW()-E2) and apply a custom format [hh]:mm:ss. The brackets around hh will do the trick.
If you rather need a number of hours, multiply by 24 as #Kerry Jackson suggested.
The logic behing date/time values is that 1 day = 1, so
1 hour = 1/24
1 min = 1/1440 '(that is 24*60 )
etc...
Put this in a module
Public Function DateTimeDiff(d1 As Date, d2 As Date)
Dim diff As Double
diff = ABS(d2 - d1)
DateTimeDiff = Fix(diff) & Format(diff, " hh:mm")
End Function
Then use
=DateTimeDiff( NOW(), E2 )
as the formula in the worksheet.
You might want to add some validation on the dates and return an error message if they are not valid.
Are you looking for a number which is the number of hours, or are you looking for text?
If you want the number of hours, try just subtracting the dates, and multiplying by 24, so the formula would be =(NOW()-E2)*24.
Public Function timeElapsed(ByVal target_cell As Range) As String
Dim hours As Long, minutes As Long, days As Long
If target_cell.Value = 0 Then Exit Function
x = DateDiff("n", target_cell, Now())
days = Format(Application.WorksheetFunction.RoundDown(x / 1440, 0), "00")
hours = Format(Application.WorksheetFunction.RoundDown((x - (days * 1440)) / 60, 0), "00")
minutes = Format(Application.WorksheetFunction.RoundDown((x - ((days * 1440) + (hours * 60))), 0), "00")
timeElapsed = CStr(days) & " " & "days" & " " & CStr(hours) & ":" & CStr(minutes)
End Function
And use as function with the result as below:
So you code becomes:
With ws1
.Range("N2:N" & lastrow1).FormulaR1C1 = "=timeElapsed(RC[-9])"
End With
At the begging I want tell you that I just read a lot of stack's about it and I didnt find solution so...
I'm substracting two dates
1 - 2017/05/05 17:00
2 - 2017/05/09 18:00
I want to receive "4 Days 1 Hour"
How I should do it? I tried to use DateFormat/Format/DateDiff and a lot of things.
It doesn't always have to be a ready-made VBA function. Especially with dates, I rarely find what I want exactly as VBA-ready. Try this custom function:
Function myDateDiff(ByVal d1 As Date, ByVal d2 As Date) As String
Dim days As Long, hours As Long
days = DateDiff("d", d1, d2) ' diff in days
hours = DateDiff("h", d1, d2) - days * 24 ' diff in hours
myDateDiff = days & " Day" & IIf(days > 1, "s ", " ") & hours & " Hour" & IIf(hours > 1, "s", "")
End Function
Sub Test()
Dim d1 As Date, d2 As Date
d1 = CDate("2017/05/05 17:00")
d2 = CDate("2017/05/09 18:00")
Debug.Print myDateDiff(d1, d2) ' 4 Days 1 Hour
d1 = CDate("2017/05/05 17:00")
d2 = CDate("2017/05/06 19:00")
Debug.Print myDateDiff(d1, d2) ' 1 Day 2 Hours
End Sub
I am trying to keep the date of some event occurrence. I am having trouble with the way my data has been defined. Instead of showing the actual date the output is showing in 1900 format (like 10/1/1900)
Here is my code:
Dim Arr_rate As Date
Dim D(528) As Date
for i = 3 to 500
Arr_rate = -3.7 * Log(Rnd) '<~~ Arrival interval
D(2) = CDate(1 / 1 / 2006)
D(i) = D(i - 1) + Arr_rate
Worksheets("A").Cells(i, "E").Value = Cdate(D(i))
next i
I tried to have the sdate format in my code but it will not give me the numbers in a proper format (1/1/2006). How can I print in the proper format? I have read some post in stackover flow but was not helpful!
Without quotes 1\1\12006 is just a mathematical expression and not a date.
Sub test()
Dim Arr_rate As Date
Dim D(528) As Date
For i = 3 To 500
Arr_rate = -3.7 * Log(Rnd) '<~~ Arrival interval
'<~~ Using Cdate, then use string. Otherwise 1 / 1 / 2006 evaluates to 4.98504486540379E-04
D(2) = CDate("1 / 1 / 2006")
D(i) = D(i - 1) + Arr_rate
Worksheets("Sheet1").Cells(i, "E").Value = CDate(D(i))
Next i
End Sub
I need to define the shift length for 3 different types of shifts: an 8, 10, or 12 hour shift.
8 hour shift:
1st shift: 6am-2pm
2nd shift: 2pm-10pm
3rd shift: 10pm-6am
10 hour shift:
1st shift: 5am-3pm
2nd shift: 3pm-1am
12 hour shift:
1st shift: 3am-3pm
2nd shift: 3pm-3am
The problem is that I'm not sure what is the best way to deal with the midnight changeover. When you do Datediff("n", #10:00:00:pm#, #06:00:00#), it gives a negative value because it thinks you want the time difference from 6am going to 10pm (but with a negative value). This is not what I want.
My code below keeps giving me negative values for the times where it changes over the midnight line.
The extremely tricky part here is that users can submit a start_time of, let's say, 2:30:00 AM, which is past the midnight line. This further complicates things.
How can I make it so that not only does my code return the correct shift length, but also accounts for the possibility of users submitting start times after midnight?
(Switch
(Max([dbo_job.Uf_Shift_Standard])="8",
(Switch(
(tbl_OEE.shift = "SH1"),
Datediff("n", Max([start_time]), [end_time]),
(tbl_OEE.shift = "SH2"),
Datediff("n", Max([start_time]), [end_time]),
True,
Datediff("n", Max([start_time]), #23:59:59#) + Datediff("n", #00:00:00#, #06:00:00#))),
Max([dbo_job.Uf_Shift_Standard])="10",
(Switch(
(tbl_OEE.shift = "SH1"),
Datediff("n", Max([start_time]), [end_time]),
True,
Datediff("n", Max([start_time]), #23:59:59#) + Datediff("n", #00:00:00#, #01:00:00#))),
Max([dbo_job.Uf_Shift_Standard])="12",
(Switch(
(tbl_OEE.shift = "SH1"),
Datediff("n", Max([start_time]), #15:00:00#),
True,
Datediff("n", Max([start_time]), #23:59:59#) + Datediff("n", #00:00:00#, #03:00:00#)))
)
) AS actual_shift_length_minutes
You can add 1 (= one day) to the time difference, then calculate it directly using TimeValue to remove the date part:
ShiftDuration = TimeValue(CDate(#06:00:00 am# - #10:00:00 pm# + 1))
ShiftDuration = TimeValue(CDate(#10:00:00 pm# - #02:00:00 pm# + 1))
ShiftDuration = TimeValue(CDate(#02:00:00 pm# - #06:00:00 am# + 1))
All of these will return a date/time value (a timespan) of 08:00:00 which can format for display using Format as you like or convert to hours and/or minutes.
If you need to display durations of more than 24 hours, use a function like this:
Public Function FormatHourMinute( _
ByVal datTime As Date, _
Optional ByVal strSeparator As String = ":") _
As String
' Returns count of days, hours and minutes of datTime
' converted to hours and minutes as a formatted string
' with an optional choice of time separator.
'
' Example:
' datTime: #10:03# + #20:01#
' returns: 30:04
'
' 2005-02-05. Cactus Data ApS, CPH.
Dim strHour As String
Dim strMinute As String
Dim strHourMinute As String
strHour = CStr(Fix(datTime) * 24 + Hour(datTime))
' Add leading zero to minute count when needed.
strMinute = Right("0" & CStr(Minute(datTime)), 2)
strHourMinute = strHour & strSeparator & strMinute
FormatHourMinute = strHourMinute
End Function
I am getting time as 23300000 i.e. hhMMssmm format as string
and I want to calculate difference of such two values.
Here hh is hours, MM is minutes, ss is seconds, and mm is 60th of second.
Using VBA for Excel 2003
This UDF will return the absolute value of the difference in seconds
Public Function tDiff(s1 As String, s2 As String) As Double
'
' calculates the absolute value of the differences
' returns the answer in seconds
'
Dim hrs As Double, mins As Double, secs As Double, sixt As Double
Dim tVal1 As Double, tVal2 As Double
hrs = CDbl(Mid(s1, 1, 2)) * 60 * 60
mins = CDbl(Mid(s1, 3, 2)) * 60
secs = CDbl(Mid(s1, 5, 2))
sixt = CDbl(Mid(s1, 7, 2)) / 60
tVal1 = hrs + mins + secs + sixt
hrs = CDbl(Mid(s2, 1, 2)) * 60 * 60
mins = CDbl(Mid(s2, 3, 2)) * 60
secs = CDbl(Mid(s2, 5, 2))
sixt = CDbl(Mid(s2, 7, 2)) / 60
tVal2 = hrs + mins + secs + sixt
If tVal1 > tVal2 Then
tDiff = tVal1 - tVal2
Else
tDiff = tVal2 - tVal1
End If
End Function
How about something like this:
Public Sub test()
Dim ms1 As Double
Dim ms2 As Double
ms1 = ToSeconds(23142700)
ms2 = ToSeconds(23311500)
Debug.Print "Difference between dates in seconds: " & ms2 - ms1
End Sub
Public Function ToSeconds(number As Long) As Double
Dim hh As Long
Dim mm As Long
Dim ss As Long
Dim ms As Long
ms = (number Mod (100 ^ 1)) / (100 ^ 0)
ss = (number Mod (100 ^ 2) - ms) / (100 ^ 1)
mm = (number Mod (100 ^ 3) - ss * (100 ^ 1) - ms) / (100 ^ 2)
hh = (number Mod (100 ^ 4) - mm * (100 ^ 2) - ss * (100 ^ 1) - ms) / (100 ^ 3)
ToSeconds = ms * 1 / 60 + ss + mm * 60 + hh * 60 * 60
End Function
The ToSeconds() function converts your number to seconds, and you can do your calculations based on that.
While this solution may not be as short as the others, I believe it is very easy to understand. Not everything here may be necessary, but you may find some of it useful in the future.
The run sub routine allows you to run the test function with your specified values.
The test function tests the timeDiff & timeSum logic.
The timeDiff function finds the time-difference between t1 and t0.
The timeSum function finds the time-sum of t1 and t0.
The asDuration function removes the AM/PM suffix from a time value.
The asMilitary function converts 12-hour format to 24 hour-format.
The concat function I created to more easily concatenate strings.
Sub Main() 'Run Test
MsgBox Test("0:29:0", "23:30:0")
End Sub
Function Test(startT As Date, endT As Date) 'Test timeDiff & timeSum logic
Dim nextShift As Date, prevShift As Date, hours As Date
hours = timeDiff(endT, startT)
prevShift = timeDiff(startT, "0:30:0")
nextShift = timeSum("0:30:0", endT)
Test = concat("Start -", startT, "", "End - ", endT, "", "Duration -", asDuration(hours), "", "Next Shift: ", nextShift, "", "Prev Shift: ", prevShift)
End Function
Function timeDiff(t1 As Date, t0 As Date) As Date 'Return Time1 minus Time0
Dim units(0 To 2) As String
units(0) = Hour(t1) - Hour(t0)
units(1) = Minute(t1) - Minute(t0)
units(2) = Second(t1) - Second(t0)
If units(2) < 0 Then
units(2) = units(2) + 60
units(1) = units(1) - 1
End If
If units(1) < 0 Then
units(1) = units(1) + 60
units(0) = units(0) - 1
End If
units(0) = IIf(units(0) < 0, units(0) + 24, units(0))
timeDiff = Join(units, ":")
End Function
Function timeSum(t1 As Date, t0 As Date) As Date 'Return Time1 plus Time0
Dim units(0 To 2) As String
units(0) = Hour(t1) + Hour(t0)
units(1) = Minute(t1) + Minute(t0)
units(2) = Second(t1) + Second(t0)
If units(2) >= 60 Then
units(2) = units(2) Mod 60
units(1) = units(1) + 1
End If
If units(1) >= 60 Then
units(1) = units(1) Mod 60
units(0) = units(0) + 1
End If
units(0) = IIf(units(0) >= 24, units(0) Mod 24, units(0))
timeSum = Join(units, ":")
End Function
Function asDuration(time As Date) As String 'Display as duration; Remove AM/PM suffix from time
time = asMilitary(time)
asDuration = Left(time, Len(time))
End Function
Function asMilitary(time As Date) As String 'Convert 12-hour format to 24-hour-format
asMilitary = Hour(time) & ":" & Minute(time) & ":" & Second(time)
End Function
Function concat(ParamArray var() As Variant) As String 'Return arguments of function call concatenated as a single string
For Each elem In var()
concat = IIf(elem <> "", concat & elem & " ", concat & vbNewLine)
Next
End Function