How to simulate milliseconds in a timestamp using a counter

This example code in this article demonstrates how to use a persistent counter to simulate a count of milliseconds.

The Lua time function os.time() only returns a time value to the nearest second. There are many occasions where milliseconds would be useful to distinguish between timed events. The code in this article solves this problem by simulating milliseconds, and concatenating them to the current time.

Warning! If real timed milliseconds required then please see suggestion at the bottom of this page.The “millisecond values” in example on this page are not timed milliseconds, they are just a sequential count from 1-999 within a second. But this example demonstrates how one could build a restartable counter to memorize given state.

How it works

The code uses a counter as a surrogate for milliseconds. The counter value is appended to the time returned by the os.time() command, and returned as a string. Should processing yield more than 999 newly generated timestamps within less than one second, the code will sleep until current second rolls over to next second and the milliseconds counter resets itself.

The magic is all done in the msec module, all that is needed to do is to call the msec.timestamp() function, as shown:

The “milliseconds”, in this case “023”, have been appended to the time.

Sample Code

Code for main():

-- Example how to simulate milliseconds in timestamp
require 'msec'

function trace(a,b,c,d) return end

function main()
   trace(msec.timestamp())
end

Code for the msec module:

msec ={}

SQLITE_DB ='test.sqlite' 
conn = db.connect{
   api=db.SQLITE,
   name=SQLITE_DB
}

local function zfill(s,N)
   if s:len() < 3 then
      repeat
         s = '0' .. s
      until s:len() == 3
   end 
   return s
end

local function Msec() 

   -- the only configurable parameter is name of table 't'
   local t = 'milliseconds'

   local function createTable()  
      local Sql='drop table if exists '..t
      conn:execute{sql=Sql,live=true}
      Sql = "CREATE TABLE IF NOT EXISTS "..t.." 
      ('msec' INT(255) NULL, 'sec' INT(255) NULL);"
      conn:execute{sql=Sql,live=true}
   end

   local function tableExists()

      local rs=conn:query('SELECT name FROM sqlite_master') 

      local function foo(i)
         if rs[i].name:nodeValue()
            == t  then 
            return true  
         end
      end

      for i=1,#rs do
         if foo(i) then return true end         
      end
   end

   local function currentSec()
      local rs=conn:query('SELECT * FROM '..t) 
      if rs[1].sec:nodeValue() 
         == os.date("%S",os.time()) then         
         return true
      end
   end

   local function currentMsec()
      local rs=conn:query('SELECT * FROM '..t) 
      return rs[1].msec:nodeValue() 
   end

   local function writeMsec(msec)
      conn:execute{
         sql="INSERT INTO "..t.." 
         (msec,sec) VALUES ("..msec..",
         "..os.date("%S",os.time())..");",
         live=true
      }
   end

   local function updateMsec(msec)
      conn:execute{
         sql="UPDATE "..t.." 
         set msec="..msec..",
         sec="..os.date("%S",os.time())..";",
         live=true
      }    
   end

   if tableExists() then
      local ms=currentMsec()
      local msnext=ms+1
      if ms=='999' then
         repeat
            util.sleep(1)
         until os.date("%S",os.time())~=currentSec()
         updateMsec(1)
         return 1
      else
         updateMsec(msnext)
         return msnext
      end
   else
      createTable()
      writeMsec(1)
      return 1
   end

end  

function msec.timestamp()
   local v = Msec()
   v=zfill(tostring(v),3)
   local t = os.date("%Y%m%d%H%M%S",os.time())
   return t..v   
end

What’s Next?

The above example shows how to use a counter to simulate milliseconds. Just remember these are not timed milliseconds, just a count from 1-999 within a second.

If timed milliseconds needed, then solution is different. Please use OS independent solution, a call to os.clock(), as explained in this example.

Please contact support at support@interfaceware.com if you need more help.