Unexpected results using os.time() and os.difftime()

I’m getting unexpected behavior from os.time() and os.difftime() functions and wonder how come…

For example doing this:

function setup()
    start=os.time()
end

function draw()
    text(os.difftime(os.time(),start),WIDTH/2,HEIGHT/2)
end

results in the text displaying 0 for a while, then 128 for a while, then 256 etc. rather than correctly displaying the difference in seconds.

Is this a bug in Codea? (Equivalent code works as expected in iLuaBox on the iPad and in the terminal on the Mac.)

(Of course I’m aware of Codea’s ElapsedTime() function but I’m not sure it is suitable for the application I envision.)

Unfortunately Codea’s version of Lua uses 32 bit floats for numbers. So the unix epoch, which is a 32 bit integer, is being converted into a 32 bit float. There’s not enough bits to display a float representation of an integer that large. I’m not too sure how to fix this. What application did you have in mind?

I’ve noticed some strange behavior as well using Lua’s os.time and os.difftime functions. For instance, to get the timing to work correctly, I had to actively print the time to the consol, if I didn’t, it wouldn’t work. Was pretty weird, if I get some time I’ll try to reproduce it.

I ended up using Codea’s EllapsedTime function, it works well. Ended up using it to modify the default frame rate of the draw function.

Oh please - show us how. Please!

@jlslate If your message was directed at my comment on modifying the frame rate, I’ll describe the process I used. The idea was pretty simple, after a touch was registered, I wanted the draw() function to wait about 0.1-0.2 seconds before updating the object I was moving. If I didn’t wait, because the draw() function implicitly redraws the scene at about 60 fps, the object would move extremely fast for very short touch input periods. So all I had to do was wrap the code I wanted to “slow down” in an if statement that compared a change in some time to my target (approximately 0.1 seconds). The idea is this:

function setup()
     t = 0.1
     t1 = 0
end

function draw()
     t2 = ElapsedTime
     dt = t2 - t1

     if dt > t then
          code you want to "slow down"
          t1 = ElapsedTime
     end
end

@traviscarrigan - yes, that was directed at you. Thanks for the code. I’m still trying to figure out the best way to implement the ability to emulate a wait statement in Codea. I have something currently, but I am always looking for something better.

function draw() 
    background(0,0,0,255)
    text(os.date(),100,100)
    --number of seconds since midnight
    dsec = os.date("*t").sec + (os.date("*t").min * 60) + (os.date("*t").hour * 60 * 60)
    text("day " .. os.date("*t").day,100,120)
    text("month " .. os.date("*t").month,100,140)
    text("yday " .. os.date("*t").yday,100,160)
    text("sec " .. os.date("*t").sec,100,180)
    text("dsec " .. dsec,100,200)
    text("year " .. os.date("*t").year,100,220)
end

The string of os.date() doesn’t loose anything. This is also true for when it is parsed by “*t”.

This isn’t a complete solution but it can give you the seconds since midnight.

If you compare the seconds since midnight, day of the year, and the year you can do most comparisons. Daylight savings and leap years get a bit strange which is why epoch was created.

Side note, my birthday is a negative number in epoch. Yes, I older than time itself, at least according to UNIX and variants.

@Simeon thank you for the explanation! Thanks too to @traviscarrigan and @Ipda41001 for the code examples.

A while back I coded a “days alive calculator” in Codea subtracting os.time() from os.time() with a parameter table containing a birth date and then dividing the result by 86400 (number of secs in a day) and had no trouble – but for this I was only concerned with having the correct number of days…

What application did you have in mind?

Timers! Hundreds of them! That are saved and recalled for the periods when the app isn’t running.

Actually it is only a vague idea which I first caught a glimpse of while learning about closures and finding I could store os.time() in the non-local variable (at least in iLuaBox which I often use as a sort of scratch pad)…

Daylight savings and leap years get a bit strange which is why epoch was created.

Ah ha. Good to know. So hopefully one day @Simeon will think of a fix for this but in the meantime I’ll try to do a work-around using os.date().

Yes, I older than time itself...

Me too! By a good 14 years.

@simeon
have you thought about maybe using something like this:

http://luaforge.net/projects/lnum/

Also, this might open the door for binary operators like shifts, and, or not, …

lnum looks interesting, however it was last updated in 2009, so is likely not compatible with the current version of Lua.

Lua 5.2 has a bit operation library. Though we haven’t managed to upgrade to using Lua 5.2 just yet.

lnum is compatible with lua 5.1.4, but not with 5.2.

5.2 is not quite backwards compatible with 5.1, btw., so stuff written for the current version of codea might not work with a version using 5.2. 5.1 will be around for quite a while longer, considering that luajit is 5.1 only and Mike has not stated any intention to change that, so apart from goto and yieldable pcalls, which nobody here uses anyway :wink: there is probably no real reason to upgrade to 5.2.

There is also http://www.tecgraf.puc-rio.br/~lhf/ftp/lua/#lbn

@jlslate, I have a timer class which I used in the pacman game, implemented using DeltaTime. In pacman various behaviors where triggered after a fixed amount of time (For example, whether ghosts scatter or chase pacman). I’ll see if I can extract the timer class out of that code and post here.

That sounds very useful. Thanks.

I have one as well in my user interface class, amd also on playlist class. If you look at my codea videos on youtube (won’t give me a link to cut and paste - search for codea and pendulum) then those are done with playlists comtrolled by timers.

Sample timer code


function setup()
    clock = Clock()
    clock:add(2,soundTest,SOUND_BLIT,15)
    clock:add(4,soundTest,SOUND_EXPLODE,20)
    clock:addPeriodic(.5,1,soundTest,SOUND_HIT,50)
end

function soundTest(type,seed)
    sound(type,seed)
end

function draw()
    clock:run()
end

-- Clock class definition

Clock = class()

function Clock:init()
    self.time = 0
    self.callbacks = {}
end

function Clock:add(t,func,...)
    table.insert(self.callbacks,{func=func,t=t,args=arg})
end

function Clock:addPeriodic(t,period,func,...)
    table.insert(self.callbacks,{func=func,t=t,period=period,args=arg})
end

function Clock:run()
    local finalTime = self.time + DeltaTime
    while true do
        -- find the least timer
        local minT = finalTime
        local minIdx = nil
        for idx,timer in ipairs(self.callbacks) do
            if timer.t < minT then
                minT = timer.t
                minIdx = idx
            end
        end
        
        if not minIdx then
            self.time = finalTime
            break 
        end
        
        self.time = minT
        
        -- execute
        local t = self.callbacks[minIdx]
        t.func(unpack(t.args))
              
        if t.period then t.t = t.t + t.period
        else table.remove(self.callbacks,minIdx) end
    end
end

@ruilov Thanks! A very useful class!

@Ipda41001 I believe there is a small error in your os.date() code above. (You may want to edit it so that others can use it.)

Should not:

dsec = os.date("*t").sec + (os.date("*t").min * 60) + (os.date("*t").hour * 24 * 60)

be:

dsec = os.date("*t").sec + (os.date("*t").min * 60) + (os.date("*t").hour * 60 * 60)

I spent a few hours last night trying to kludge together a ‘closed’ timer function using os.date() instead of os.time() but the affair became rather messy… Eventually I gave up (for the time being…) when I discovered I couldn’t successfully serialize/store the up-value (which was the point of the exercise) using string.dump.

@Simeon should I file a request for a fix for os.time() on the wiki?

Put it on the issue tracker, but I’m not sure how much we can do about it.

It comes down to this: better arithmetic performance, or os.time() working properly.

I would take better performance. From what I’ve read, the iPad’s Cortex-A8 CPU performs best with single-precision floating point numbers, as it can use the NEON unit instead of the VFP.

How about a separate timer that only works in the 24-bit range? From what I saw, DeltaTime and ElapsedTime are only updated whenever draw is called, I also have wanted a timer, mainly for benchmarking purposes… make it a simple thing, just a userdata with 3 methods, start(), stop() and current(), and maybe a way to determine wether it wrapped. Resolution in 1/100th of a second range would be ok for me.

Thanks @Simeon. I understand the trade-off (though the specifics are way over my head…) and totally agree concerning the choice for performance.

May I suggest at least changing the documentation to reflect the fact that the results of os.time() and os.difftime() are somewhat limited in the current Codea implementation? And perhaps what those limits are?

In my own limited testing I find I can use os.time() to return the number of hours since the epoch by dividing by 3600 but minutes and, obviously, seconds, fail.