I'm running into all sorts of problems with large time values (like more
than 100 days) because of overflow problems. I've tried typing the variables
differently, and using casting conversions on the results.
More importantly, the hours don't seem right if I enter a certain number of
minutes more than 60. What I have so far is shown below.
I can't seem to find this on google for classic VB. Any ideas?
TIA as always!
Calan
****************
Public Function MillisecondsToTime(ms As Single, Optional IncludeHundreths
As Boolean = False, Optional IncludeLabels As Boolean = False) As String
Dim hours As Single
Dim mins As Single
Dim secs As Single
Dim hundreths As Single
hours = CLng(ms / 3600000)
mins = CLng(ms / 60000 Mod 60)
secs = CLng(ms / 1000 Mod 60)
hundreths = CInt((ms Mod 1000) / 10)
If hours > 999 Then
MillisecondsToTime = "***"
Exit Function
End If
If IncludeLabels Then
MillisecondsToTime = "H:" & Format(Hours, "0#") & " M:" & Format(mins,
"0#") & " S:" & Format(secs, "0#")
Else
MillisecondsToTime = Format(Hours, "0#") & ":" & Format(mins, "0#") &
":" & Format(secs, "0#")
End If
If IncludeHundreths Then
If IncludeLabels Then
MillisecondsToTime = MillisecondsToTime & " H:" & Format(hundreths,
"0#")
Else
MillisecondsToTime = MillisecondsToTime & ":" & Format(hundreths,
"0#")
End If
End If
End Function
****************
Here is the function I have come up with.
Function MillisecondsToTime(MS As Long, _
Optional IncludeHundreths As Boolean = False, _
Optional IncludeLabels As Boolean = False) As String
Dim Hours As Long
Dim Minutes As Long
Dim Seconds As Currency
Dim Hundreds As Long
Const MSperHour As Long = 360000
Const MSperMinute As Long = 6000
Const MSperSecond As Long = 100
If MS / MSperHour > 999 Then
MillisecondsToTime = "***"
Else
Hours = Int(MS / MSperHour)
Minutes = Int((MS - (Hours * MSperHour)) / MSperMinute)
Seconds = (MS - (Hours * MSperHour) - _
(Minutes * MSperMinute)) / MSperSecond
Hundreds = MSperSecond * (Seconds - Int(Seconds))
Seconds = Int(Seconds)
If IncludeLabels Then
MillisecondsToTime = "H:" & Format(Hours, "0# ") & _
"M:" & Format(Minutes, "0# ") & _
"S:" & Format(Seconds, "0#")
Else
MillisecondsToTime = Format(Hours, "0#") & _
":" & Format(Minutes, "0#") & _
":" & Format(Seconds, "0#")
End If
If IncludeHundreths Then
If IncludeLabels Then
MillisecondsToTime = MillisecondsToTime & " H:" & _
Format(Hundreds, "0#")
Else
MillisecondsToTime = MillisecondsToTime & ":" & _
Format(Hundreds, "0#")
End If
End If
End If
End Function
Rick - MVP
Try to convert 61742 milliSeconds.
From head it gives: H:00 M:01 S:01 H:74
The Function reveals
H:00 M:10 S:17 H:42
"Rick Rothstein" <rickNOS...@NOSPAMcomcast.net> skrev i en meddelelse
news:vPSdnScv364...@comcast.com...
Quite right, ako, a millisecond is 1/1000 of a second. Also, once you fix that,
the function should also round off the seconds if you choose not to display
milliseconds, e.g. your example of 61742 should show M:01 S:02.
Const MSperHour As Long = 3600000
Const MSperMinute As Long = 60000
Const MSperSecond As Long = 1000
Function MillisecondsToTime(MS As Long, _
Optional IncludeMilliSecs As Boolean = False, _
Optional IncludeLabels As Boolean = False) As String
Dim Hours As Long
Dim Minutes As Long
Dim Seconds As Currency
Dim MilliSecs As Long
Hours = Int(MS / MSperHour)
Minutes = Int((MS - (Hours * MSperHour)) / MSperMinute)
Seconds = (MS - (Hours * MSperHour) - _
(Minutes * MSperMinute)) / MSperSecond
MilliSecs = MSperSecond * (Seconds - Int(Seconds))
Seconds = Int(Seconds)
If Not IncludeMilliSecs Then
If MilliSecs >= MSperSecond / 2 Then
Seconds = Seconds + 1
If Seconds > 59 Then
Seconds = 0
Minutes = Minutes + 1
If Minutes > 59 Then
Minutes = 0
Hours = Hours + 1
End If
End If
End If
End If
If IncludeLabels Then
MillisecondsToTime = "H:" & Format(Hours, "0# ") & _
"M:" & Format(Minutes, "0# ") & _
"S:" & Format(Seconds, "0#")
Else
MillisecondsToTime = Format(Hours, "0#") & _
":" & Format(Minutes, "0#") & _
":" & Format(Seconds, "0#")
End If
If IncludeMilliSecs Then
MillisecondsToTime = MillisecondsToTime & "." & _
Format(MilliSecs, "0#")
End If
End Function
Private Sub Command1_Click()
Debug.Print Me.MillisecondsToTime(61742, True)
Debug.Print Me.MillisecondsToTime(61742, False)
Debug.Print Me.MillisecondsToTime(MSperHour - 1, True)
Debug.Print Me.MillisecondsToTime(MSperHour - 1, False)
End Sub
'The OP doesn't want to append the decimalpart to the
'seconds. He want it to stand explicitly. Also, he want
'it expressed with 2 digits.
'To full fill this, I changed several among which;
'you can 't have MilliSecs As Long, as to round off
'seconds to two digits (up for third => 5).
I also changed the Dim'ing to Double, enthough
Currency would work. -But in certain cases,
there could be a long ripple (99...9) on the
decimalparts of seconds, where Currency wouldn't
suffice with its limit of 4 digit decimal.
Also, when making divisions with 2 Longs, the
result is rounded up on odd integer part left of
the decimalpart. Otherwise, it is rounded down.
This fact would really mess op the final result.
' Rick did it on purpose to call all Lurkes playing put!
ako
Function MillisecondsToTime(MS As Long, _
Optional IncludeHundreths As Boolean = False, _
Optional IncludeLabels As Boolean = False) As String
Dim Hours As Double
Dim Minutes As Double
Dim Seconds As Double
Dim Hundreds As Double
Dim MilliSecs As Double
Const MSperHour As Long = 3600000
Const MSperMinute As Long = 60000
Const MSperSecond As Long = 1000
If MS / MSperHour > 999 Then
MillisecondsToTime = "***"
Else
Hours = Int(MS / MSperHour)
Minutes = Int((MS - Hours * MSperHour) / MSperMinute)
Seconds = (MS - (Hours * MSperHour) - _
(Minutes * MSperMinute)) / MSperSecond
MilliSecs = Seconds - Int(Seconds)
Seconds = Int(Seconds)
If Not IncludeHundreths Then
If MilliSecs >= 0.5 Then
Seconds = Seconds + 1
If Seconds > 59 Then
Seconds = 0
Minutes = Minutes + 1
If Minutes > 59 Then
Minutes = 0
Hours = Hours + 1
End If
End If
End If
End If
If IncludeLabels Then
MillisecondsToTime = "H:" & Format(Hours, "0# ") & _
"M:" & Format(Minutes, "0# ") & _
"S:" & Format(Seconds, "0#")
Else
MillisecondsToTime = Format(Hours, "0#") & _
":" & Format(Minutes, "0#") & _
":" & Format(Seconds, "0#")
End If
If IncludeHundreths Then
If IncludeLabels Then
MillisecondsToTime = MillisecondsToTime & _
" H:" & Right(Format(MilliSecs, "0.00"), 2)
Else
MillisecondsToTime = MillisecondsToTime & _
":" & Right(Format(MilliSecs, "0.00"), 2)
End If
End If
End If
End Function
It is the CDbl that does the above.
The exact from help:
When the fractional part is exactly 0.5, CInt and
CLng always round it to the nearest even number.
For example, 0.5 rounds to 0, and 1.5 rounds to 2.
CInt and CLng differ from the Fix and Int functions,
which truncate, rather than round, the fractional
part of a number. Also, Fix and Int always return
a value of the same type as is passed in.
When Hours, MS and MSperHour is Long
Hours = MS / MSperHour
would be rounded up to 2
if decimalpart of division are = 1.5
if 2.5 it would be rounded down to 2
So it would produce mess in the calculation,
But I discovered the above rules is overRided
when wrapped with Int() as:
Hours = Int(MS / MSperHour)
Whether this is considered to be logical, is
a matter of taste in my opinion.
Conclusion: Dim'ing Hours, Minutes as Long
(as done originally) would also work.
To eliminate the needs to memorize such
remarkable things and the consequence of
humen memory-failure, I would make the
Dim'ing as Double.
ako
When Hours, MS and MSperHour is Long
Hours = MS / MSperHour
would be rounded up to 2
if decimalpart of division are => 1.5
if < 1.5 it would be rounded down to 2
So it would produce mess in the calculation,
But I discovered the above rules is overRided
when wrapped with Int() as:
Hours = Int(MS / MSperHour)
Whether this is considered to be logical, is
a matter of taste in my opinion.
ako
You are correct... there is definitely a bug in my routine. Thank you
for noticing it and bringing it to my attention. Below is a corrected
routine which I think works correctly now. Thanks again.
Rick - MVP
Function MillisecondsToTime(MS As Long, _
Optional IncludeHundreths As Boolean = False, _
Optional IncludeLabels As Boolean = False) As String
Dim Hours As Long
Dim Minutes As Long
Dim Seconds As Currency
Dim RawSeconds As Currency
Dim Hundreds As Long
Const SecondsPerHour As Long = 3600
Const SecondsPerMinute As Long = 60
Const MSperSecond As Long = 1000
RawSeconds = MS / MSperSecond
Hours = Int(RawSeconds / SecondsPerHour)
If Hours > 999 Then
MillisecondsToTime = "***"
Else
Minutes = Int((RawSeconds - (Hours * _
SecondsPerHour)) / SecondsPerMinute)
Seconds = (RawSeconds - (Hours * SecondsPerHour) - _
(Minutes * SecondsPerMinute))
If IncludeHundreths Then
Hundreds = 100 * (Seconds - Int(Seconds))
Seconds = Int(Seconds)
Else
Seconds = Format$(Seconds, "00")
End If
If IncludeLabels Then
MillisecondsToTime = "H:" & Format(Hours, "00 ") & _
"M:" & Format(Minutes, "00 ") & _
"S:" & Format(Seconds, "00")
Else
MillisecondsToTime = Format(Hours, "0#") & _
":" & Format(Minutes, "00") & _
":" & Format(Seconds, "00")
End If
If IncludeHundreths Then
If IncludeLabels Then
MillisecondsToTime = MillisecondsToTime & " H:" & _
Format(Hundreds, "00")
Else
MillisecondsToTime = MillisecondsToTime & ":" & _
Format(Hundreds, "00")
I think you are being tricked into thinking that by the numbers you are
using. The Int function always truncates the fractional part of any
floating point number. For example
Dim A As Long
Dim B As Long
A = 99
B = 98
Debug.Print Int(A / B)
No rounding is taking place. And you are right that simply dividing two
Long values and then putting that result into a Long variable will
perform Banker's Rounding (all rounding of numbers ending in 5 to the
previous decimal position is toward the nearest even number in that
position). This Banker's Rounding takes place in all rounding situations
except one... when you let the Format function perform the rounding. In
this case, the rounding method most people consider "normal" takes place
(5 and higher round up, less than 5 round down).
Debug.Print Format(3 / 2, "0")
Debug.Print Format(1 / 8, "0.00")
Rick - MVP
No, I'm not being tricked, Rick. What I'm saying above is
not what you say:
I know how Int(...) works, both on positive and negative
numbers, and the Fix(..)
I se you changed the original
Hours = Int(MS / MSperHour)
to
RawSeconds = MS / MSperSecond
and droped the Int(..) around the right side.
Because RawSeconds is Dim'ed as Currency, you can
safe do this. As opposed, dropping Int(..) around the original;
Hours = Int(MS / MSperHour)
would lead to mess in the calculation, that is:
As Hours, MS and MSperHour is Dim'ed as Long,
Hours in the equation
Hours = MS / MSperHour
would be rounded up to 2 if the equations right side
division had a value of say =>1.5 but less than 2.
This situation would make a wrong calculation in the
conversion procedure.
Now, what I tried to point out at the top is:
Having MS / MSperHour wraped in Int(..) as
Hours = Int(MS / MSperHour) prevent divisions
of values between 1.5 and 1.99.. to round *up* to 2
That's what I were saying !.
I wonder how you could mis-understand this.
Never mind. I also se that you like Longs and
Currency. The later I too !.
ako
My mistake... I misread what you had written. Sorry.
> Never mind. I also se that you like Longs and
> Currency. The later I too !.
Currency is nice to work with (as long as you can work within its 4
decimal place limit) because it is really an integer value (kind of
number, not the data type) scaled to look like a floating point number.
Because of this, it does not suffer from the inaccuracies calculating
with floating point numbers can produce (again, as long as you can work
within its 4 decimal place limitation).
Rick