Howdy, Stranger!

It looks like you're new here. If you want to get involved, click one of these buttons!

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

edited February 2012 in Bugs Posts: 273

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.)

Comments

  • SimeonSimeon Admin Mod
    edited February 2012 Posts: 4,932

    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.

  • Posts: 176

    Oh please - show us how. Please!

  • edited February 2012 Posts: 2

    @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
    
  • Posts: 176

    @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.

  • edited February 2012 Posts: 622
    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.

  • edited February 2012 Posts: 273

    @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.

  • Posts: 146

    @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, ...

  • SimeonSimeon Admin Mod
    Posts: 4,932

    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.

  • Posts: 146

    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 ;) there is probably no real reason to upgrade to 5.2.

  • Posts: 447

    @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.

  • Posts: 176

    That sounds very useful. Thanks.

  • Posts: 2,161

    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.

  • Posts: 447

    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
  • edited February 2012 Posts: 273

    @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?

  • SimeonSimeon Admin Mod
    edited February 2012 Posts: 4,932

    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.

  • Posts: 146

    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.

  • edited February 2012 Posts: 273

    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.

  • Posts: 50

    I just came across this thread when I realized that a calculation I was trying to make might be having precision problems. I'm trying to evaluate a time series, one that can cover a pretty broad range. It involves julian days over the course of more than a century, with intermediate results in the sub-second range. 32 bit floats just can't do it. If being able to switch to 64 floats (as a switch, I suppose) isn't feasible, has anyone written a higher precision arithmetic package?

  • Posts: 1,254

    Probably missing something simple, but...

    If I do...

    d = difftime(os.time(), os.time())
    Print(os.date("%M:%S", d)
    

    I get "00:00" which is just what I would expect. However, if I do

    d = difftime(os.time(), os.time())
    Print(os.date("%H:%M:%S", d)
    

    I get "18:00:00" which is definitely not what I expected. If I use %I instead of %H to do hours on a 12 hour clock, I get 06:00:00. So where is the 18 hour difference in hours coming from?

    Sorry for reviving an old thread, but it seemed like the appropriate place.

  • dave1707dave1707 Mod
    Posts: 7,554

    When you use the d in os.date() in you're example above, your asking os.date to convert the value of d to some date or time. d is the number of seconds from some arbitrary point in time long ago. Use the print statement below to see that point in time.

        print(os.date("%c", 0))
    
  • Posts: 1,254

    I don't see how that could work so that minutes and seconds are always 0, as I would expect,but hours is 18.

  • IgnatzIgnatz Mod
    edited December 2014 Posts: 5,396

    @Mark - I get 8 hours rather than 16, and my time is GMT+8 hours, so maybe it is treating the time parameter as being GMT, and reformatting it in local time.

    If you prefix %H with ! , ie "!%H.." (which the docs tell me formats in Universal Coordinated Time, aka GMT) then you get zero as expected.

  • dave1707dave1707 Mod
    Posts: 7,554

    @Mark You set d = difftime(os.time(),os.time()), which set d to 0. Passing 0 to os.date() means that os.date() will give you the result of Wed Dec 31 18:00:00 1969 . As you can see, the hours and minutes are 0. Apparently you don't understand what os.date is doing when you pass it a value. The value you give to d is the number of seconds since Wed Dec 31 18:00:00 1969. So depending on that value, os.date() will give you the date and time that many seconds since then.

  • Posts: 1,254

    Thanks, @Ignatz. That's just what I needed.

Sign In or Register to comment.