Say I have a local Pacific Time (UTC-8) Excel value. For example 41656.67297 is 17 January 2014 4:09 PM. I do not have control of this value.
I want to adjust the given value relative to the users local time. Say for example someone in Dublin is using the Excel workbook. I want to adjust the given number and display it in his local time, 18 January 2014 12:09 AM. Is there any built in Excel functionality to make this smooth?
VBA doesn't provide an option for this natively, but there's a Windows API that will allow you to do what you're looking for. This article describes how to do so and here is the MSDN page on GetTimeZoneInformation.
From the CPearson page, make these declarations first:
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
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' NOTE: If you are using the Windows WinAPI Viewer Add-In to get
' function declarations, not that there is an error in the
' TIME_ZONE_INFORMATION structure. It defines StandardName and
' DaylightName As 32. This is fine if you have an Option Base
' directive to set the lower bound of arrays to 1. However, if
' your Option Base directive is set to 0 or you have no
' Option Base diretive, the code won't work. Instead,
' change the (32) to (0 To 31).
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Private Type TIME_ZONE_INFORMATION
Bias As Long
StandardName(0 To 31) As Integer
StandardDate As SYSTEMTIME
StandardBias As Long
DaylightName(0 TO 31) As Integer
DaylightDate As SYSTEMTIME
DaylightBias As Long
End Type
''''''''''''''''''''''''''''''''''''''''''''''
' These give symbolic names to the time zone
' values returned by GetTimeZoneInformation .
''''''''''''''''''''''''''''''''''''''''''''''
Private Enum TIME_ZONE
TIME_ZONE_ID_INVALID = 0 ' Cannot determine DST
TIME_ZONE_STANDARD = 1 ' Standard Time, not Daylight
TIME_ZONE_DAYLIGHT = 2 ' Daylight Time, not Standard
End Enum
Private Declare Function GetTimeZoneInformation Lib "kernel32" _
(lpTimeZoneInformation As TIME_ZONE_INFORMATION) As Long
Private Declare Sub GetSystemTime Lib "kernel32" _
(lpSystemTime As SYSTEMTIME)
Then this function will find the user's local time and offset from GMT:
Function LocalOffsetFromGMT(Optional AsHours As Boolean = False, _
Optional AdjustForDST As Boolean = False) As Double
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' LocalOffsetFromGMT
' This returns the amount of time in minutes (if AsHours is omitted or
' false) or hours (if AsHours is True) that should be *added* to the
' local time to get GMT. If AdjustForDST is missing or false,
' the unmodified difference is returned. (e.g., Kansas City to London
' is 6 hours normally, 5 hours during DST. If AdjustForDST is False,
' the resultif 6 hours. If AdjustForDST is True, the result is 5 hours
' if DST is in effect.)
' Note that the return type of the function is a Double not a Long. This
' is to accomodate those few places in the world where the GMT offset
' is not an even hour, such as Newfoundland, Canada, where the offset is
' on a half-hour displacement.
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Dim TBias As Long
Dim TZI As TIME_ZONE_INFORMATION
Dim DST As TIME_ZONE
DST = GetTimeZoneInformation(TZI)
If DST = TIME_ZONE_DAYLIGHT Then
If AdjustForDST = True Then
TBias = TZI.Bias + TZI.DaylightBias
Else
TBias = TZI.Bias
End If
Else
TBias = TZI.Bias
End If
If AsHours = True Then
TBias = TBias / 60
End If
LocalOffsetFromGMT = TBias
End Function
You can test the code with:
MsgBox (LocalOffsetFromGMT())
Then to add this offset to the original time you can do the following:
OriginalTime + TIME(LocalOffsetFromGMT(True, True), 0, 0)
Related
In Access, I'm trying to create a table Field called "Signed Year" which has to be from 1970 to present (currently 1970-2018) using Lookup Wizard. However, I realised it's not optimal since I have to manually add one more year from time to time.
Is there a code of some sort to automatically generate such ranges?
Thanks a lot!
You can use a callback list for this. Here is code for one listing ultimo dates 15 years back:
Public Function ListUltimoYears( _
ctl As Control, _
lngId As Long, _
lngRow As Long, _
lngCol As Long, _
intCode As Integer) _
As Variant
' Period for listing dates.
Const cintYears As Integer = 15
' 2014-09-24. Cactus Data ApS, CPH.
Static datFirstDate As Date
Static strFormat As String
Static intRows As Integer
Dim datDate As Date
Dim varValue As Variant
Select Case intCode
Case acLBInitialize
datDate = Date
datFirstDate = DateSerial(Year(datDate), 12, 31)
intRows = 1 + cintYears
strFormat = ctl.Format
varValue = True ' True to initialize.
Case acLBOpen
varValue = Timer ' Autogenerated unique ID.
Case acLBGetRowCount ' Get rows.
varValue = intRows ' Set number of rows.
Case acLBGetColumnCount ' Get columns.
varValue = 1 ' Set number of columns.
Case acLBGetColumnWidth ' Get column width.
varValue = -1 ' Use default width.
Case acLBGetValue ' Get the data for each row.
varValue = DateAdd("yyyy", lngRow, datFirstDate)
Case acLBGetFormat ' Format the data.
varValue = strFormat ' Use format of control.
Case acLBEnd
' Do something when form with listbox closes or
' listbox is requeried.
End Select
' Return Value.
ListUltimoYears = varValue
End Function
Modify it from using:
Const cintYears As Integer = 15
to use a variable:
Dim intYears = DateDiff("yyyy", #1/1/1970#, Date)
To use it in a form, set the combobox' property RowSourceType: ListUltimoYears
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
I am trying to implement millisecond timestamping in Access 2010/13 using this method;
MS Access Can Handle Millisecond Time Values--Really - See more at: http://www.devx.com/dbzone/Article/39046#sthash.xEIruMyE.dpuf
The function Msec(2) is supposed to return the system time in milliseconds but it seems to be about 10 hours out.
Public Function Msec( _
Optional ByVal intTimePart As Integer) _
As Date
' This is the core function.
' It generates the current time with millisecond resolution.
'
' Returns current (local) date/time including millisecond.
' Parameter intTimePart determines level of returned value:
' 0: Millisecond value only.
' 1: Time value only including milliseconds.
' 2: Full Date/time value including milliseconds.
' None or any other value: Millisecond value only.
Const cintMsecOnly As Integer = 0
Const cintMsecTime As Integer = 1
Const cintMsecDate As Integer = 2
Static typTime As SYSTEMTIME
Static lngMsecInit As Long
Dim datMsec As Date
Dim datDate As Date
Dim intMilliseconds As Integer
Dim lngTimeZoneBias As Long
Dim lngMsec As Long
Dim lngMsecCurrent As Long
Dim lngMsecOffset As Long
' Set resolution of timer to 1 ms.
timeBeginPeriod 1
lngMsecCurrent = timeGetTime()
If lngMsecInit = 0 Or lngMsecCurrent < lngMsecInit Then
' Initialize.
' Get bias for local time zone respecting
' current setting for daylight savings.
lngTimeZoneBias = GetLocalTimeZoneBias(False)
' Get current UTC system time.
Call GetSystemTime(typTime)
intMilliseconds = typTime.wMilliseconds
' Repeat until GetSystemTime retrieves next count of milliseconds.
' Then retrieve and store count of milliseconds from launch.
Do
Call GetSystemTime(typTime)
Loop Until typTime.wMilliseconds <> intMilliseconds
lngMsecInit = timeGetTime()
' Adjust UTC to local system time by correcting for time zone bias.
typTime.wMinute = typTime.wMinute - lngTimeZoneBias
' Note: typTime may now contain an invalid (zero or negative) minute count.
' However, the minute count is acceptable by TimeSerial().
Else
' Retrieve offset from initial time to current time.
lngMsecOffset = lngMsecCurrent - lngMsecInit
End If
With typTime
' Now, current system time is initial system time corrected for
' time zone bias.
lngMsec = (.wMilliseconds + lngMsecOffset)
Select Case intTimePart
Case cintMsecTime, cintMsecDate
' Calculate the time to add as a date/time value with millisecond resolution.
datMsec = lngMsec / 1000 / clngSecondsPerDay
' Add to this the current system time.
datDate = datMsec + TimeSerial(.wHour, .wMinute, .wSecond)
If intTimePart = cintMsecDate Then
' Add to this the current system date.
datDate = datDate + DateSerial(.wYear, .wMonth, .wDay)
End If
Case Else
' Calculate millisecond part as a date/time value with millisecond resolution.
datMsec = (lngMsec Mod 1000) / 1000 / clngSecondsPerDay
' Return millisecond part only.
datDate = datMsec
End Select
End With
Msec = datDate
End Function
As Jack hardcastle says; Probably Timezone related.
It never runs this code;
If lngMsecInit = 0 Or lngMsecCurrent < lngMsecInit Then
' Initialize.
' Get bias for local time zone respecting
' current setting for daylight savings.
lngTimeZoneBias = GetLocalTimeZoneBias(False)
' Get current UTC system time.
Call GetSystemTime(typTime)
intMilliseconds = typTime.wMilliseconds
' Repeat until GetSystemTime retrieves next count of milliseconds.
' Then retrieve and store count of milliseconds from launch.
Do
Call GetSystemTime(typTime)
Loop Until typTime.wMilliseconds <> intMilliseconds
lngMsecInit = timeGetTime()
' Adjust UTC to local system time by correcting for time zone bias.
typTime.wMinute = typTime.wMinute - lngTimeZoneBias
' Note: typTime may now contain an invalid (zero or negative) minute count.
' However, the minute count is acceptable by TimeSerial().
But goes to;
Else
' Retrieve offset from initial time to current time.
lngMsecOffset = lngMsecCurrent - lngMsecInit
End If
Answer! From #pathDongle
Time is stored as Millisecond UTC;
!DateTimeMS = GetTimeUTC()
And restored by;
Public Function UTCtoTimeLocal(dSysUTC As Date) As Date
'Dim sysTime As SYSTEMTIME
Dim DST As Long
Dim tzi As TIME_ZONE_INFORMATION
DST = GetTimeZoneInformation(tzi)
UTCtoTimeLocal = dSysUTC - TimeSerial(0, tzi.Bias, 0) + IIf(DST = 2, TimeSerial(1, 0, 0), 0)
End Function
Query;
SELECT tblzzAuditTrail.DateTimeMS, FormatDate(UTCtoTimeLocal([DateTimeMS])) AS DateTimeLocal
Which can be filtered on as a String.
Private Sub BuildFilter()
Dim strFilter As String
Dim ctl As Control
strFilter = ""
'add selected values to string
For Each ctl In Me.FormHeader.Controls
With ctl
If .ControlType = acTextBox Or .ControlType = acComboBox Then
If Nz(.Value) <> "" Then
If InStr(.Name, "Date") <> 0 Then
If Nz(StartDate) <> "" And Nz(EndDate) <> "" And InStr(strFilter, "DateTimeLocal") = 0 Then
strFilter = strFilter & "[DateTimeLocal] BETWEEN '" & FormatDate(Me.StartDate.Value) & "' AND '" & FormatDate(Me.EndDate.Value) & "' AND "
ElseIf Nz(StartDate) <> "" And InStr(strFilter, "DateTimeLocal") = 0 Then
strFilter = strFilter & "[DateTimeLocal] > '" & FormatDate(Me.StartDate.Value) & "' AND "
ElseIf Nz(EndDate) <> "" And InStr(strFilter, "DateTimeLocal") = 0 Then
strFilter = strFilter & "[DateTimeLocal] <= '" & FormatDate(Me.EndDate.Value) & "' AND "
End If
ElseIf InStr(.Name, "ID") <> 0 Then
strFilter = strFilter & "[" & .Name & "] = " & .Value & " AND "
Else
strFilter = strFilter & "[" & .Name & "] = '" & .Value & "' AND "
End If
End If
End If
End With
Next ctl
'trim trailing And
strFilter = TrimR(strFilter, 5)
Debug.Print strFilter
With Me.subfrmzzAuditTrailDisplay
.Form.Filter = strFilter
.Form.FilterOn = True
End With
End Sub
Resulting Filter String;
[UserID] = 2 AND [DateTimeLocal] BETWEEN '06/01/2015 00:00:00.000' AND '07/01/2015 00:00:00.000'
As per my other question;
Millisecond time: Filter form by date
Most of those functions can be simplified down to the following:
Function GetTimeLocal will return the users local system datetime with daylight saving adjustment
Function GetTimeUTC will return UTC time
Function FormatDate will format a Date as a string with the correct millisecond component.
Usually it's better to store all times as UTC and convert if needed.
Option Explicit
#If Win64 Then
Public Declare PtrSafe Sub GetSystemTime Lib "kernel32" (ByRef lpSystemTime As SYSTEMTIME)
Private Declare PtrSafe Function GetTimeZoneInformation Lib "kernel32" (lpTimeZoneInformation As TIME_ZONE_INFORMATION) As Long
#Else
Public Declare Sub GetSystemTime Lib "kernel32" (ByRef lpSystemTime As SYSTEMTIME)
Private Declare Function GetTimeZoneInformation Lib "kernel32" (lpTimeZoneInformation As TIME_ZONE_INFORMATION) As Long
#End If
Public 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 Type TIME_ZONE_INFORMATION
Bias As Long
StandardName(31) As Integer
StandardDate As SYSTEMTIME
StandardBias As Long
DaylightName(31) As Integer
DaylightDate As SYSTEMTIME
DaylightBias As Long
End Type
Sub test()
Dim dtLcl As Date
Dim dtUTC As Date
dtLcl = GetTimeLocal 'Gets local time including adjustement for daylight saving time
dtUTC = GetTimeUTC 'Gets UTC time
Debug.Print FormatDate(dtLcl)
Debug.Print FormatDate(dtUTC)
End Sub
Function FormatDate(ByVal dt As Date) As String
Dim sysTime As SYSTEMTIME
Dim sec As Double
Dim x As Double
With sysTime
.wYear = Year(dt)
.wMonth = Month(dt)
.wDay = Day(dt)
.wHour = Hour(dt)
.wMinute = Minute(dt)
'Second() function rounds to nearest second so calc floor second
'Eg 12:15:09.678 will give second component as 10 instead of 09
x = (dt - Int(dt)) * 86400#
sec = x - Fix(x / 60#) * 60#
.wSecond = Int(sec)
.wMilliseconds = Int(Round(sec - .wSecond, 3) * 1000)
FormatDate = Format(dt, "dd/mm/yyyy hh:mm:ss.") & Format(sysTime.wMilliseconds, "000")
End With
End Function
Public Function GetTimeLocal() As Date
Dim dSysUTC As Date, sysTime As SYSTEMTIME
Dim DST As Long, IsDST As Boolean
Dim tzi As TIME_ZONE_INFORMATION
Dim ms As Double
GetSystemTime sysTime
With sysTime
'Debug.Print "ms=" & .wMilliseconds
ms = CDbl(.wMilliseconds) / (86400# * 1000#)
dSysUTC = DateSerial(.wYear, .wMonth, .wDay) + TimeSerial(.wHour, .wMinute, .wSecond) + ms
End With
DST = GetTimeZoneInformation(tzi)
GetTimeLocal = dSysUTC - TimeSerial(0, tzi.Bias, 0) + IIf(DST = 2, TimeSerial(1, 0, 0), 0)
End Function
Public Function GetTimeUTC() As Date
Dim dSysUTC As Date
Dim sysTime As SYSTEMTIME
Dim ms As Double
GetSystemTime sysTime
With sysTime
'Debug.Print "ms=" & .wMilliseconds
ms = CDbl(.wMilliseconds) / (86400# * 1000#)
GetTimeUTC = DateSerial(.wYear, .wMonth, .wDay) + TimeSerial(.wHour, .wMinute, .wSecond) + ms
End With
End Function
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)
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.)