High Resolution Interval Counter

  Found GetSystemTimePreciseAsFileTime() while poking around at learn.microsoft.com for an improved interval counter procedure in the API. The time won't be any more accurate than what the PC is set to. Maybe ± 25 ms if synchronized over the Internet. Better if set a GPS receiver's time. Better still if you have an atomic clock in your home or office. 😀

  If the time is only accurate to tens of milliseconds, How is resolution to a hundred nanoseconds useful? An advantage is you will get is that two time stamps always will be different on a particular PC (sequence maintenance). If PCs are on a local network (LAN) it should be possible to synchronize them within microseconds. 😎

Demo Source Code

  First is QueryPerformanceFrequency. It returns the current performance-counter frequency in counts per second (Hz). A frequency of 10000000 (10 MHz) has a period of 100 ns, the value of the least significant bit in a FileTime type.

  The improved resolution of GetSystemTimePreciseAsFileTime is shown by comparison with GetSystemTimeAsFileTime. Each is called successively and the time differences displayed. A difference of zero indicates the resolution is greater than the time to immediately repeat the call (bad). The sequence of the time stamps can not be determined by the values contained. Any difference greater than zero means there will be no duplicate time stamps (good).
Note - Instead of variables typed as UDT FileTime, QUAD type is used. This eliminates need for a FileTime/QUAD UNION.

  Getting the entire time stamp to human readable form is the last thing performed in the demo app. Convert the FileTime to a SystemTime. By not using the wMilliseconds and MODing the FileTime by 10000000 an integer that can be concatenated after the decimal point in one step. wDayOfWeek not used here.
For local time use FileTimeToLocalFileTime before FileTimeToSystemTime.

The DECLARE for SUB GetSystemTimePreciseAsFileTime is not in PBWin 10's, nor PBCC 06's, or older, \WinAPI\ include files. Keep for future use! It was new in Windows 8. It probably would have been in the include files for PBWin 11 and PBCC 7 if they had been produced (at the time).

#compile exe
#dim all
#if %pb_cc32 'This ignored by PBWin, stops creation of unneeded console in PBCC.
  #console off
'As with the DECLARES, so you can see it here.
  wYear         as word
  wMonth        as word
  wDayOfWeek    as word
  wDay          as word
  wHour         as word
  wMinute       as word
  wSecond       as word
  wMilliseconds as word
end type
'DECLARES here so you don't have to look in the include files to "follow the 
'action". Microsoft defined parameters as BYVAL pointers, that is why the 
'variable names have "lp" even though the PB includes use BYREF meaning you
'use the base variable names and PB supplies the pointer.
declare function QueryPerformanceFrequency lib "Kernel32.dll" _
   alias "QueryPerformanceFrequency" (lpFrequency as quad) as long
declare sub GetSystemTimeAsFileTime lib "Kernel32.dll" _
   alias "GetSystemTimeAsFileTime" (lpFileTime as quad) 'was "AS FILETIME"
declare function FileTimeToSystemTime lib "Kernel32.dll" _
   alias "FileTimeToSystemTime" (lpFileTime as quad), _  'was "AS FILETIME"
   lpSystemTime as SYSTEMTIME) as long
'Reminder - This DECLARE is not in the PB \WinAPI\ include files.
declare sub GetSystemTimePreciseAsFileTime lib "Kernel32.dll" _
   alias "GetSystemTimePreciseAsFileTime" (FTime as quad)
function pbmain () as long
  local Freq, UTC_FTime1, UTC_FTime2 as quad
  local hTxt as dword
  local UTC_STime as systemtime
  txt.window("Demo HR Timestamp", 100, 100, 25, 80) to hTxt
  txt.print "Curious about frequency my PC uses for ";
  txt.print "high res time stamp and interval."
  QueryPerformanceFrequency Freq
  txt.print dec$(Freq);
  txt.print " 10 Mhz on my PC. That is 100 ns per cycle. (hint, hint)."
  txt.print string$$(80, &h2550)
  txt.print "Difference between two consecutive calls."
  sleep 0 'do test in fresh time slice
  GetSystemTimePreciseAsFileTime UTC_FTime1
  GetSystemTimePreciseAsFileTime UTC_FTime2
  txt.print dec$(UTC_FTime2 - UTC_FTime1) + " Precise. I get 2 to 4 here."
  sleep 0 'do test in fresh time slice
  GetSystemTimeAsFileTime UTC_FTime1
  GetSystemTimeAsFileTime UTC_FTime2
  txt.print dec$(UTC_FTime2 - UTC_FTime1) + _
     " Old. Always zero due to coarse resolution."
  txt.print "GetSystemTimeAsFileTime() could return the same for ~15+ ms."
  txt.print string$$(80, &h2550)
  txt.print "Two forms of human readable time."
  GetSystemTimePreciseAsFileTime UTC_FTime1
  txt.print dec$(UTC_FTime1) + _
     " Current UTC time in decimal of FileTime format."
  FileTimeToSystemTime UTC_FTime1, UTC_STime
  txt.print dec$(UTC_STime.wYear, 4) + "-" + dec$(UTC_STime.wMonth, 2) + "-";
  txt.print dec$(UTC_STime.wDay, 2) + "T";
  txt.print dec$(UTC_STime.wHour, 2) + ":" + dec$(UTC_STime.wMinute, 2) + ":";
  txt.print dec$(UTC_STime.wSecond, 2) + ".";
  txt.print dec$(UTC_FTime1 mod 10000000, 7) + "Z";
  txt.print " Current UTC in ISO 8601 format."
  txt.print string$$(80, &h2550)
  txt.print "Any key to exit."
end function

Created on 14 March 2023, last edit 15 March 2023.

To Domain Home.

To Dale's Notebook index.
To Programs index.