Monotonically increasing time in JavaScript?
Asked Answered
R

4

31

What’s the best way to get monotonically increasing time in JavaScript? I’m hoping for something like Java’s System.nanoTime().

Date() obviously won’t work, as it’s affected by system time changes.

In other words, what I would like is for a <= b, always:

a = myIncreasingTime.getMilliseconds();
...
// some time later, maybe seconds, maybe days
b = myIncreasingTime.getMilliseconds();

At best, even when using the UTC functions in Date(), it will return what it believes is the correct time, but if someone sets the time backward, the next call to Date() can return a lesser value. System.nanoTime() does not suffer from this limitation (at least not until the system is rebooted).

Modification: [2012-02-26: not intended to affect the original question, which has a bounty]

I am not interested knowing the “wall time”, I’m interested in knowing elapsed time with some accuracy, which Date() cannot possibly provide.

Riotous answered 1/9, 2011 at 15:12 Comment(6)
W3Schools show something similar.Ana
What do you mean by session? Where do you run this code (browser, server)?Proximo
Sorry, I had worded that poorly. Hopefully the change I just added is clearer.Riotous
@xyu: w3schools is not really a good site - w3fools.com - the fact that they pass a string to setTimeout clearly proves that.Sturrock
@Sturrock Yes I know. But I meant the idea of increasing a variable using setTimeout.Ana
I have added the requirement of accuracy to my original query. As there was a bounty introduced by someone else, I note that my requirement is not intended to affect the bounty.Riotous
S
27

You could use window.performance.now() - since Firefox 15, and window.performance.webkitNow() - Chrome 20]

var a = window.performance.now();
//...
var delay = window.performance.now() - a;
Saharan answered 3/5, 2012 at 3:25 Comment(4)
This function is also available in IE10.Riotous
Web worker uses different time origin. developer.mozilla.org/en-US/docs/Web/API/…Uraemia
Bit of warning, this doesn't actually appear to be monotonic in the sense that I was hoping for (the POSIX monotonic clock). Specifically, it does not account for time spent in sleep mode.Cheltenham
It should account for time spent in sleep mode, but there are bugs in browsers. See this note.Warmblooded
L
7

You could wrap Date() or Date.now() so as to force it to be monotonic (but inaccurate). Sketch, untested:

var offset = 0;
var seen = 0;
function time() {
  var t = Date.now();
  if (t < seen) {
    offset += (seen - t);
  }
  seen = t;
  return t + offset;
}

If the system clock is set back at a given moment, then it will appear that no time has passed (and an elapsed time containing that interval will be incorrect), but you will at least not have negative deltas. If there are no set-backs then this returns the same value as Date.now().

This might be a suitable solution if you're writing a game simulation loop, for example, where time() is called extremely frequently — the maximum error is the number of set-backs times the interval between calls. If your application doesn't naturally do that, you could explicitly call it on a setInterval, say (assuming that isn't hosed by the system clock), to keep your accuracy at the cost of some CPU time.


It is also possible that the clock will be set forward, which does not prevent monotonicity but might have equally undesirable effects (e.g. a game spending too long trying to catch up its simulation at once). However, this is not especially distinguishable from the machine having been asleep for some time. If such a protection is desired, it just means changing the condition next to the existing one, with a constant threshold for acceptable progress:

if (t > seen + leapForwardMaximum) {
  offset += (seen - t) + leapForwardMaximum;
}

I would suggest that leapForwardMaximum should be set to more than 1000 ms because, for example, Chrome (if I recall correctly) throttles timers in background tabs to fire not more than once per second.

Liss answered 26/2, 2012 at 14:7 Comment(3)
Yes, good point and answered as I originally asked. I have modified my original question to add the accuracy requirement.Riotous
A kind-of obvious comment that isn't an issue for the original question in the way it is asked, but it might be important for some none-the-less: This solution does not handle the case where the user sets the clock forward, i.e. it does not prevent large positive jumps in the returned time.Forewent
@MarkusA. It seemed worth discussing, so I added a section on that.Liss
F
4

Javascript itself does not have any functionality to access the nanoTime. You might load a java-applet to aqcuire that information, like benchmark.js has done. Maybe @mathias can shed some light on what they did there…

Fluency answered 26/2, 2012 at 11:6 Comment(0)
S
1

Firefox provides "delay" argument for setTimeout... this is the one of ways to implement monotonically increased time counter.

var time = 0;

setTimeout(function x(actualLateness) {
  setTimeout(x, 0);
  time += actualLateness;
}, 0);
Saharan answered 24/2, 2012 at 11:54 Comment(2)
setTimeout has no guarantee of the maximum delay. The parameter specifies a minimum, only. There is no way to conclusively determine the actual amount of time passed using setTimeout.Riotous
see developer.mozilla.org/en/DOM/window.setTimeout, >> Note: Prior to Gecko 13 (Firefox 13.0 / Thunderbird 13.0) , Gecko passed an extra parameter to the callback routine, indicating the "actual lateness" of the timeout in milliseconds. This non-standard parameter is no longer passed, i have updated my answerSaharan

© 2022 - 2024 — McMap. All rights reserved.