How to get a DateDiff-Value in milliseconds in VBA (Excel)? - vba

I need to calculate the difference between two timestamps in milliseconds.
Unfortunately, the DateDiff-function of VBA does not offer this precision.
Are there any workarounds?

You could use the method described here as follows:-
Create a new class module called StopWatch
Put the following code in the StopWatch class module:
Private mlngStart As Long
Private Declare Function GetTickCount Lib "kernel32" () As Long
Public Sub StartTimer()
mlngStart = GetTickCount
End Sub
Public Function EndTimer() As Long
EndTimer = (GetTickCount - mlngStart)
End Function
You use the code as follows:
Dim sw as StopWatch
Set sw = New StopWatch
sw.StartTimer
' Do whatever you want to time here
Debug.Print "That took: " & sw.EndTimer & "milliseconds"
Other methods describe use of the VBA Timer function but this is only accurate to one hundredth of a second (centisecond).

If you just need time elapsed in Centiseconds then you don't need the TickCount API. You can just use the VBA.Timer Method which is present in all Office products.
Public Sub TestHarness()
Dim fTimeStart As Single
Dim fTimeEnd As Single
fTimeStart = Timer
SomeProcedure
fTimeEnd = Timer
Debug.Print Format$((fTimeEnd - fTimeStart) * 100!, "0.00 "" Centiseconds Elapsed""")
End Sub
Public Sub SomeProcedure()
Dim i As Long, r As Double
For i = 0& To 10000000
r = Rnd
Next
End Sub

GetTickCount and Performance Counter are required if you want to go for micro seconds..
For millisenconds you can just use some thing like this..
'at the bigining of the module
Private Type SYSTEMTIME
wYear As Integer
wMonth As Integer
wDayOfWeek As Integer
wDay As Integer
wHour As Integer
wMinute As Integer
wSecond As Integer
wMilliseconds As Integer
End Type
Private Declare Sub GetLocalTime Lib "kernel32" (lpSystemTime As SYSTEMTIME)
'In the Function where you need find diff
Dim sSysTime As SYSTEMTIME
Dim iStartSec As Long, iCurrentSec As Long
GetLocalTime sSysTime
iStartSec = CLng(sSysTime.wSecond) * 1000 + sSysTime.wMilliseconds
'do your stuff spending few milliseconds
GetLocalTime sSysTime ' get the new time
iCurrentSec=CLng(sSysTime.wSecond) * 1000 + sSysTime.wMilliseconds
'Different between iStartSec and iCurrentSec will give you diff in MilliSecs

If Timer() precision is enough then you can just create timestamp by combining date and time with milliseconds:
Function Now2() As Date
Now2 = Date + CDate(Timer / 86400)
End Function
To calculate the difference between two timestamps in milliseconds you may subtract them:
Sub test()
Dim start As Date
Dim finish As Date
Dim i As Long
start = Now2
For i = 0 To 100000000
Next
finish = Now2
Debug.Print (finish - start) & " days"
Debug.Print (finish - start) * 86400 & " sec"
Debug.Print (finish - start) * 86400 * 1000 & " msec"
End Sub
Actual precision of that method is about 8 msec (BTW GetTickCount is even worse - 16 msec) for me.

You can also use =NOW() formula calcilated in cell:
Dim ws As Worksheet
Set ws = Sheet1
ws.Range("a1").formula = "=now()"
ws.Range("a1").numberFormat = "dd/mm/yyyy h:mm:ss.000"
Application.Wait Now() + TimeSerial(0, 0, 1)
ws.Range("a2").formula = "=now()"
ws.Range("a2").numberFormat = "dd/mm/yyyy h:mm:ss.000"
ws.Range("a3").formula = "=a2-a1"
ws.Range("a3").numberFormat = "h:mm:ss.000"
var diff as double
diff = ws.Range("a3")

Apologies to wake up this old post, but I got an answer:
Write a function for Millisecond like this:
Public Function TimeInMS() As String
TimeInMS = Strings.Format(Now, "HH:nn:ss") & "." & Strings.Right(Strings.Format(Timer, "#0.00"), 2)
End Function
Use this function in your sub:
Sub DisplayMS()
On Error Resume Next
Cancel = True
Cells(Rows.Count, 2).End(xlUp).Offset(1) = TimeInMS()
End Sub

Besides the Method described by AdamRalph (GetTickCount()), you can do this:
Using the QueryPerformanceCounter() and QueryPerformanceFrequency() API Functions
How do you test running time of VBA code?
or, for environments without access to the Win32 API (like VBScript), this:
http://ccrp.mvps.org/ (check the download section for the "High-Performance Timer" installable COM objects. They're free.)

Related

Every 5 seconds record the value in 2 cells in another worksheet VBA

Problem:
I have searched extensively for this and cannot seem to get it to work. I have a timer running when the "StartBtn" is pressed:
Dim StopTimer As Boolean
Dim SchdTime As Date
Dim Etime As Date
Dim currentcost As Integer
Const OneSec As Date = 1 / 86400#
Private Sub ResetBtn_Click()
StopTimer = True
Etime = 0
[TextBox21].Value = "00:00:00"
End Sub
Private Sub StartBtn_Click()
StopTimer = False
SchdTime = Now()
[TextBox21].Value = Format(Etime, "hh:mm:ss")
Application.OnTime SchdTime + OneSec, "Sheet1.NextTick"
End Sub
Private Sub StopBtn_Click()
StopTimer = True
Beep
End Sub
Sub NextTick()
If StopTimer Then
'Don't reschedule update
Else
[TextBox21].Value = Format(Etime, "hh:mm:ss")
SchdTime = SchdTime + OneSec
Application.OnTime SchdTime, "Sheet1.NextTick"
Etime = Etime + OneSec
End If
End Sub
Then in another cell (say, C16) I have a manually entered value which is the hourly cost rate. I have a third cell that is calculating total cost by C16*current timer value.
What I want to do is record every 5 seconds after the "StartBtn" is clicked the current time and current calculated cost in another sheet. This is what I have started:
Sub increment()
Dim x As String
Dim n As Integer
Dim Recordnext As Date
n = 0
Record = [TextBox21].Value
Recordnext = [TextBox21].Value + OneSec
Range("B13").Value = Recordnext
Do Until IsEmpty(B4)
If [TextBox21].Value = Recordnext Then ActiveCell.Copy
Application.Goto(ActiveWorkbook.Sheets("Sheet2").Range("A1").Offset(1, 0))
ActiveSheet.Paste
Application.CutCopyMode = False
n = n + 1
Recordnext = [TextBox21].Value + 5 * (OneSec)
Exit Do
End If
ActiveCell.Offset(1, 0).Select
Loop
End Sub
But it doesnt work. Any help would be appreciated.
I have tried to simplify your timer method down to what is actually needed.
Sheet1 code sheet
Option Explicit
Private Sub ResetBtn_Click()
bRun_Timer = False
'use the following if you want to remove the last time cycle
TextBox21.Value = Format(0, "hh:mm:ss")
End Sub
Private Sub StartBtn_Click()
bRun_Timer = True
dTime_Start = Now
TextBox21.Value = Format(Now - dTime_Start, "hh:mm:ss")
Range("D16").ClearContents
Call next_Increment
End Sub
Module1 code sheet
Option Explicit
Public bRun_Timer As Boolean
Public Const iSecs As Integer = 3 'three seconds
Public dTime_Start As Date
Sub next_Increment()
With Worksheets("Sheet1")
.TextBox21.Value = Format(Now - dTime_Start, "hh:mm:ss")
.Range("D16") = Sheet1.Range("C16") / 3600 * _
Second(TimeValue(Sheet1.TextBox21.Value)) '# of secs × rate/sec
Worksheets("Sheet2").Cells(Rows.Count, 1).End(xlUp).Resize(1, 2).Offset(1, 0) = _
Array(.TextBox21.Value, .Range("D16").Value)
End With
If bRun_Timer Then _
Application.OnTime Now + TimeSerial(0, 0, iSecs), "next_Increment"
End Sub
Note that the operation of transferring the data to Sheet2 is a direct value transfer¹ with no .GoTo, ActiveCell or Select.
It was not entirely clear to me what you were trying to do with the value transfer. I have stacked them one after another on Sheet1.
        
You would benefit by adding Option Explicit² to the top of all your code sheets. This requires variable declaration and if you misplace a public variable, you will quickly know.
¹ See How to avoid using Select in Excel VBA macros for more methods on getting away from relying on select and activate to accomplish your goals.
² Setting Require Variable Declaration within the VBE's Tools ► Options ► Editor property page will put the Option Explicit statement at the top of each newly created code sheet. This will avoid silly coding mistakes like misspellings as well as influencing you to use the correct variable type in the variable declaration. Variables created on-the-fly without declaration are all of the variant/object type. Using Option Explicit is widely considered 'best practice'.

Global variables are empty even though they've been set within a sub vba

Tried to create a timer in VBA. My intetion is to be able to call TimerStart, TimerLaps and TimerStop within other funcitions to estimate how long it takes a code block to finish. But I'm having problems with my global variable being empty.
Private starttime As Double
Sub TimerStart()
starttime = Timer
End Sub
Function TimerLaps() As Double
TimerLaps = Timer - starttime
End Function
Function TimerStop() As Double
i = starttime
starttime = 0
TimerStop = Round(Timer - i, 2)
End Function
Sub test()
Call TimerStart
MsgBox TimerStop()
End Sub
Thanks guys!
You can use in two way:
Global starttime As Double
And
Public starttime As Double
Use one of them instead of Private. The main different is:
Global can only be used in standard modules, whereas Public can be used in all contexts (modules, classes, controls, forms etc.) Global comes from older versions of VB and was likely kept for backwards compatibility, but has been wholly superseded by Public.
Try:
Global starttime As Double
At the top of your module
(instead of Private starttime As Double)
EDIT
Global starttime As Double
Sub test()
Call TimerStart
Application.Wait (Now + #12:00:02 AM#)
MsgBox TimerStop()
End Sub
Sub TimerStart()
starttime = Timer
End Sub
Function TimerStop() As Double
Dim i As Double
i = starttime
starttime = 0
TimerStop = Round(Timer - i, 2)
End Function

VBA QueryPerformanceCounter Not Working

I am trying to test the execution time differences between data types after looping through 1 million random numbers per data type (integer, double, decimal, and variant). I took this code from the Microsoft Developer website. I am using Excel 2010.
Here is the code:
Option Explicit
Sub Function1()
Module Module1
Declare Function QueryPerformanceCounter Lib "Kernel32" (ByRef X As Long) As Short
Declare Function QueryPerformanceFrequency Lib "Kernel32" (ByRef X As Long) As Short
Dim Ctr1, Ctr2, Freq As Long
Dim Acc, I As Integer
' Times 100 increment operations by using QueryPerformanceCounter.
If QueryPerformanceCounter(Ctr1) Then ' Begin timing.
For I = 1 To 100 ' Code is being timed.
Acc += 1
Next
QueryPerformanceCounter (Ctr2) ' Finish timing.
Console.WriteLine ("Start Value: " & Ctr1)
Console.WriteLine ("End Value: " & Ctr2)
QueryPerformanceFrequency (Freq)
Console.WriteLine ("QueryPerformanceCounter minimum resolution: 1/" & Freq & " seconds.")
Console.WriteLine ("100 Increment time: " & (Ctr2 - Ctr1) / Freq & " seconds.")
Else
Console.WriteLine ("High-resolution counter not supported.")
End If
'
' Keep console window open.
'
Console.WriteLine()
Console.Write ("Press ENTER to finish ... ")
Console.Read()
End Module
End Sub
Sub Function1_Int_RandNumCounter()
Dim Int_RandNum_X As Integer
Dim Int_RandNum_Y As Integer
Dim Count As Integer
For Count = 1 To Count = 1000000
Int_RandNum_X = Rnd(Now) ' Get rnd vals based on Now, built-in VBA property
Int_RandNum_Y = Rnd(Now)
Next Count
' Call Function1_Dbl_RandNumCounter
End Sub
Sub Function1_Dbl_RandNumCounter()
Dim Dbl_RandNum_X As Double, Dbl_RandNum_Y As Double, Count As Double
For Count = 1 To Count = 1000000
Dbl_RandNum_X = Rnd(Now) ' Get rnd vals based on Now, built-in VBA property
Dbl_RandNum_Y = Rnd(Now)
Next Count
Call Function1_Var_RandNumCounter
End Sub
Sub Function1_Var_RandNumCounter()
Dim Var_RandNum_X, Var_RandNum_Y, Count As Variant
For Count = 1 To Count = 1000000
Var_RandNum_X = Rnd(Now) ' Get rnd vals based on Now, built-in VBA property
Var_RandNum_Y = Rnd(Now)
Next Count
Call Function1_Dec_RandNumCounter
End Sub
Sub Function1_Dec_RandNumCounter()
Dim Count, Var_RandNum_X, dec_RandNum_X, Var_RandNum_Y, dec_RandNum_Y
dec_RandNum_X = CDec(Var_RandNum_X)
dec_RandNum_Y = CDec(Var_RandNum_Y) ' convert these vals to decimals
For Count = 1 To Count = 1000000
dec_RandNum_X = Rnd(Now) ' Get rnd vals based on Now, built-in VBA property
dec_RandNum_Y = Rnd(Now)
Next Count
Call Function2_BarGraph
End Sub
Sub Function2_BarGraph()
' Put all of these vals in a 2D bar graph
End Sub
This code gives me errors such as:
Compile error:
Only comments may appear after End Sub, End Function, or End Property
EDIT: Here is the improved version of the code, which has no compile errors, but I'm not sure how to integrate the timer into my functions.
Option Explicit
Private Type LARGE_INTEGER
lowpart As Long
highpart As Long
End Type
Private Declare Function QueryPerformanceCounter Lib "kernel32" (lpPerformanceCount As LARGE_INTEGER) As Long
Private Declare Function QueryPerformanceFrequency Lib "kernel32" (lpFrequency As LARGE_INTEGER) As Long
Private m_CounterStart As LARGE_INTEGER
Private m_CounterEnd As LARGE_INTEGER
Private m_crFrequency As Double
Private Const TWO_32 = 4294967296# ' = 256# * 256# * 256# * 256#
Private Function LI2Double(LI As LARGE_INTEGER) As Double
Dim Low As Double
Low = LI.lowpart
If Low < 0 Then
Low = Low + TWO_32
End If
LI2Double = LI.highpart * TWO_32 + Low
End Function
Private Sub Class_Initialize()
Dim PerfFrequency As LARGE_INTEGER
QueryPerformanceFrequency PerfFrequency
m_crFrequency = LI2Double(PerfFrequency)
End Sub
Public Sub StartCounter()
QueryPerformanceCounter m_CounterStart
End Sub
Property Get TimeElapsed() As Double
Dim crStart As Double
Dim crStop As Double
QueryPerformanceCounter m_CounterEnd
crStart = LI2Double(m_CounterStart)
crStop = LI2Double(m_CounterEnd)
TimeElapsed = 1000# * (crStop - crStart) / m_crFrequency
End Property
Sub Function1_Int_RandNumCounter()
Dim Int_RandNum_X As Integer
Dim Int_RandNum_Y As Integer
Dim Count As Integer
For Count = 1 To Count = 1000000
Int_RandNum_X = Rnd(Now) ' Get rnd vals based on Now, built-in VBA property
Int_RandNum_Y = Rnd(Now)
Next Count
' Call Function1_Dbl_RandNumCounter
End Sub
Sub Function1_Dbl_RandNumCounter()
Dim Dbl_RandNum_X As Double, Dbl_RandNum_Y As Double, Count As Double
For Count = 1 To Count = 1000000
Dbl_RandNum_X = Rnd(Now) ' Get rnd vals based on Now, built-in VBA property
Dbl_RandNum_Y = Rnd(Now)
Next Count
Call Function1_Var_RandNumCounter
End Sub
Sub Function1_Var_RandNumCounter()
Dim Var_RandNum_X, Var_RandNum_Y, Count As Variant
For Count = 1 To Count = 1000000
Var_RandNum_X = Rnd(Now) ' Get rnd vals based on Now, built-in VBA property
Var_RandNum_Y = Rnd(Now)
Next Count
Call Function1_Dec_RandNumCounter
End Sub
Sub Function1_Dec_RandNumCounter()
Dim Count, Var_RandNum_X, dec_RandNum_X, Var_RandNum_Y, dec_RandNum_Y
dec_RandNum_X = CDec(Var_RandNum_X)
dec_RandNum_Y = CDec(Var_RandNum_Y) ' convert these vals to decimals
For Count = 1 To Count = 1000000
dec_RandNum_X = Rnd(Now) ' Get rnd vals based on Now, built-in VBA property
dec_RandNum_Y = Rnd(Now)
Next Count
Call Function2_BarGraph
End Sub
Sub Function2_BarGraph()
' Put all of these vals in a 2D bar graph
End Sub
EDIT: New VBA code (did I set up this function properly?)
Sub Function1_Int_RandNumCounter()
Dim Int_RandNum_X As Integer
Dim Int_RandNum_Y As Integer
Dim Count As Integer
Dim oPM As PerformanceMonitor
Dim Time_Int As Variant
Time_Int = CDec(Time_Int)
Set oPM = New PerformanceMonitor
oPM.StartCounter
For Count = 1 To Count = 1000000
Int_RandNum_X = Rnd(Now) ' Get rnd vals based on Now, built-in VBA property
Int_RandNum_Y = Rnd(Now)
Next
Time_Int = oPM.TimeElapsed
' Call Function1_Dbl_RandNumCounter
End Sub
Add a new class module to your project, call it PerformanceMonitor and paste this code from the thread I linked to in my comment into the class:
Option Explicit
Private Type LARGE_INTEGER
lowpart As Long
highpart As Long
End Type
Private Declare Function QueryPerformanceCounter Lib "kernel32" (lpPerformanceCount As LARGE_INTEGER) As Long
Private Declare Function QueryPerformanceFrequency Lib "kernel32" (lpFrequency As LARGE_INTEGER) As Long
Private m_CounterStart As LARGE_INTEGER
Private m_CounterEnd As LARGE_INTEGER
Private m_crFrequency As Double
Private Const TWO_32 = 4294967296# ' = 256# * 256# * 256# * 256#
Private Function LI2Double(LI As LARGE_INTEGER) As Double
Dim Low As Double
Low = LI.lowpart
If Low < 0 Then
Low = Low + TWO_32
End If
LI2Double = LI.highpart * TWO_32 + Low
End Function
Private Sub Class_Initialize()
Dim PerfFrequency As LARGE_INTEGER
QueryPerformanceFrequency PerfFrequency
m_crFrequency = LI2Double(PerfFrequency)
End Sub
Public Sub StartCounter()
QueryPerformanceCounter m_CounterStart
End Sub
Property Get TimeElapsed() As Double
Dim crStart As Double
Dim crStop As Double
QueryPerformanceCounter m_CounterEnd
crStart = LI2Double(m_CounterStart)
crStop = LI2Double(m_CounterEnd)
TimeElapsed = 1000# * (crStop - crStart) / m_crFrequency
End Property
Now as an example of how to use it, you need to declare and create an instance of the PerformanceMonitor class, then call its StartCounter method at the start of the code you want to time, then at the end call its TimeElapsed property to see how long it took (in milliseconds). For example:
Sub foo()
Dim n As Long
Dim oPM As PerformanceMonitor
Set oPM = New PerformanceMonitor
oPM.StartCounter
For n = 1 To 100000
Debug.Print n
Next
MsgBox oPM.TimeElapsed
Set oPM = Nothing
End Sub

Get Unix time milliseconds

In Java to get system time in milliseconds I use:
new date().gettime()
It is possible to get the same result in milliseconds using Excel VBA?
SUMMARY: For best results, use GetSystemTime.
The Excel worksheet function Now() has relatively good precision, roughly down to 10 ms. But to call it you have to use a worksheet formula.
To correctly get the milliseconds value, you should avoid the VBA Now() function. Its precision is roughly 1 second.
The VBA Timer() function returns a single with a precision of roughly 5 milliseconds. But you have to use Now() to get the date part. This might cause a slight problem if Now() is called before midnight and Timer() is called after midnight (this is probably a rare situation and not an issue for most people).
The Windows API function GetSystemTime has true millisecond precision. You can use the values in the SYSTEMTIME structure to create an Excel double that has the correct millisecond precision. GetSystemTime returns the UTC time so if you want the date in POSIX format, you can subtract the UNIX epoch (1 January 1970 UTC), which is 25569 in Excel date format (disregarding leap seconds).
The code below compares the precision of each method:
Option Explicit
Private Type SYSTEMTIME
wYear As Integer
wMonth As Integer
wDayOfWeek As Integer
wDay As Integer
wHour As Integer
wMinute As Integer
wSecond As Integer
wMilliseconds As Integer
End Type
Private Declare Sub GetSystemTime Lib "kernel32" (lpSystemTime As SYSTEMTIME)
Function Now_System() As Double
Dim st As SYSTEMTIME
GetSystemTime st
Now_System = DateSerial(st.wYear, st.wMonth, st.wDay) + _
TimeSerial(st.wHour, st.wMinute, st.wSecond) + _
st.wMilliseconds / 86400000#
End Function
Function Now_Timer() As Double
Now_Timer = CDbl(Int(Now)) + CDbl(Timer() / 86400#)
End Function
Sub CompareCurrentTimeFunctions()
' Compare precision of different methods to get current time.
Me.Range("A1:D1000").NumberFormat = "yyyy/mm/dd h:mm:ss.000"
Dim d As Double
Dim i As Long
For i = 2 To 1000
' 1) Excel NOW() formula returns same value until delay of ~10 milliseconds. (local time)
Me.Cells(1, 1).Formula = "=Now()"
d = Me.Cells(1, 1)
Me.Cells(i, 1) = d
' 2) VBA Now() returns same value until delay of ~1 second. (local time)
d = Now
Me.Cells(i, 2) = d
' 3) VBA Timer returns same value until delay of ~5 milliseconds. (local time)
Me.Cells(i, 3) = Now_Timer
' 4) System time is precise down to 1 millisecond. (UTC)
Me.Cells(i, 4) = Now_System
Next i
End Sub
Different interpretation, based on Excel posix time and with an hour adjustment for summer time:
Sub Pose()
ut = ((Now - 25569) * 86400000) - 3600000
End Sub
If not sufficiently precise, http://vbadud.blogspot.co.uk/2008/10/excel-vba-timestamp-milliseconds-using.html may be of interest.
Here is a short extension on the answer by #bouvierr as I needed the equivalent of the java.lang.System.currentTimeMillis() method in VBA:
Private Type SYSTEMTIME
wYear As Integer
wMonth As Integer
wDayOfWeek As Integer
wDay As Integer
wHour As Integer
wMinute As Integer
wSecond As Integer
wMilliseconds As Integer
End Type
Private Declare Sub GetSystemTime Lib "kernel32" (lpSystemTime As SYSTEMTIME)
Function CurrentTimeMillis() As Double
' Returns the milliseconds from 1970/01/01 00:00:00.0 to system UTC
Dim st As SYSTEMTIME
GetSystemTime st
Dim t_Start, t_Now
t_Start = DateSerial(1970, 1, 1) ' Starting time for Linux
t_Now = DateSerial(st.wYear, st.wMonth, st.wDay) + _
TimeSerial(st.wHour, st.wMinute, st.wSecond)
CurrentTimeMillis = DateDiff("s", t_Start, t_Now) * 1000 + st.wMilliseconds
End Function
This produces a timestamp in format yyyy mm dd hh:mm:ss.fff where fff are the milliseconds.
Dim dateToday As Date
Dim datetimeNow As Date
Dim secondsElapsedSinceMidnight As Double
Dim h As Long
Dim m As Long
Dim s As Long
dateToday = Now
secondsElapsedSinceMidnight = Timer
h = Int(secondsElapsedSinceMidnight / 3600)
m = Int(secondsElapsedSinceMidnight / 60) - h * 60
s = Int(secondsElapsedSinceMidnight) - m * 60 - h * 3600
datetimeNow = DateSerial(Year(dateToday), Month(dateToday), Day(dateToday)) _
+ TimeSerial(h, m, s)
Debug.Print Format(datetimeNow, "yyyy mm dd hh:nn:ss.") _
& Format((secondsElapsedSinceMidnight _
- Int(secondsElapsedSinceMidnight)) * 1000, "000")
As I submit this answer, the output is:
2015 04 21 16:24:22.852
I found only one possible variant
Private Type SYSTEMTIME
wYear As Integer
wMonth As Integer
wDayOfWeek As Integer
wDay As Integer
wHour As Integer
wMinute As Integer
wSecond As Integer
wMilliseconds As Integer
End Type
Private Declare Sub GetLocalTime Lib "kernel32" (lpSystemTime As SYSTEMTIME)
Sub test()
Dim sSysTime As SYSTEMTIME
GetLocalTime sSysTime
MsgBox = ((Now - 25569) * 86400000) - 3600000 + sSysTime.wMilliseconds
End Sub

VBA - show clock time with accuracy of less than a second

Is there a way to use VBA (excel) to generate a clock time with accuracy to a tenth of a second or less?
eg:
Sub test()
MsgBox Format(Time, "hh:mm:ss???") 'not sure what this format should be...
End Sub
I think that Time doesn't give that information.
You can use Timer for extra accuracy.
In Microsoft Windows the Timer
function returns fractional portions
of a second. On the Macintosh, timer
resolution is one second.
Here is an example:
MsgBox Format(Time, "hh:mm:ss:" & Right(Format(Timer, "#0.00"), 2))
Here is a much simpler way:
t = Evaluate("Now()")
This evaluates the current time as a worksheet function in milliseconds, rather than as a VBA function in seconds.
The following VBA code returns the current local time as a String, including milliseconds. If you need system time, simply replace GetLocalTime by GetSystemTime.
Private Type SYSTEMTIME
wYear As Integer
wMonth As Integer
wDayOfWeek As Integer
wDay As Integer
wHour As Integer
wMinute As Integer
wSecond As Integer
wMilliseconds As Integer
End Type
Private Declare Sub GetLocalTime Lib "kernel32" (ByRef lpLocalTime As SYSTEMTIME)
Public Function NowMilli() As String
Dim tTime As SYSTEMTIME
Dim sTwo As String, sThree As String
Dim sOut As String
sOut = "yyyy-mm-dd hh:mm:ss.mmm"
sTwo = "00": sThree = "000"
Call GetLocalTime(tTime)
Mid(sOut, 1, 4) = tTime.wYear
Mid(sOut, 6, 2) = Format(tTime.wMonth, sTwo)
Mid(sOut, 9, 2) = Format(tTime.wDay, sTwo)
Mid(sOut, 12, 2) = Format(tTime.wHour, sTwo)
Mid(sOut, 15, 2) = Format(tTime.wMinute, sTwo)
Mid(sOut, 18, 2) = Format(tTime.wSecond, sTwo)
Mid(sOut, 21, 3) = Format(tTime.wMilliseconds, sThree)
NowMilli = sOut
End Function
You can use the Windows API to get a more accurate time (including milliseconds) as follows.
Private Type SYSTEMTIME
Year As Integer
Month As Integer
DayOfWeek As Integer
Day As Integer
Hour As Integer
Minute As Integer
Second As Integer
Milliseconds As Integer
End Type
Public Declare Sub GetSystemTime Lib "kernel32" (lpSystemTime As SYSTEMTIME)
Public Function GetMilliseconds()
'' This function returns an accurate version of the milliseconds elememt of the current date/time
Dim tSystem As SYSTEMTIME
GetSystemTime tSystem
GetMilliseconds = tSystem.Milliseconds
End Function
Credit goes to http://custom-designed-databases.com/wordpress/2011/get-milliseconds-or-seconds-from-system-time-with-vba/ where there is also more detailed information on getting the milliseconds from the system time in VBA.
I have noticed through some trial and error that current time is shown atleast upto 10 milliseconds if you assign a formula to a cell opposed to using the function directly in vba.
I generally use the NOW() function for current time.
If my code is as follows:
sub test()
cells(1,1)=now()
end sub
then cell A1 shows time upto seconds, not milliseconds (time would be shown as 10:38:25.000)
if I use this code:
sub test()
cells(2,1).formula "=now()"
cells(1,1)=cells(2,1)
end sub
Then time is shown upto milliseconds in A1 (time would be shown as 10:38:25.851)