3

In our code, we were previously calculating the difference between to events like so:

var beginTime = new Date();
// Do stuff
var endTime = new Date();
var duration = endTime.getTime() - beginTime.getTime();

console.log("Job began at " + beginTime.toUTCString()
            + " and took " + duration + " milliseconds.");

This results in a human-readable string:

Job began at Thu Sep 28 2017 11:17:33 GMT-0500 (Central Daylight Time) and took 7000 milliseconds.

We've decided to switch to High Resolution Time by using the more reliable performance.now(). However, we still want to be able have a human-readable UTC time string included.

Initially, we tried this:

var beginTime = performance.now();
// Do stuff
var endTime = performance.now();
var duration = endTime - beginTime;

console.log("Job began at " + new Date(beginTime).toUTCString() 
            + " and took " + duration + " seconds.");

We are finding that the duration is accurate, but new Date(performance.now()) results in an inaccurate UTC value (at the time of this writing, it provides a date nearly 50 years in the past).

Job began at Wed Dec 31 1969 20:10:46 GMT-0600 (Central Standard Time) and took 7000 milliseconds.

Is there a better way to convert the output of performance.now() to an accurate UTC string? It doesn't have to be the exact same format as new Date().toUTCString(), but it should be human-readable.

sideshowbarker
  • 81,827
  • 26
  • 193
  • 197
Thunderforge
  • 19,637
  • 18
  • 83
  • 130
  • 2
    performance.now() gives the time in ms since the navigation start. (and not since 1970) – Roland Starke Sep 28 '17 at 16:55
  • 2
    They do different things. https://stackoverflow.com/questions/30795525/performance-now-vs-date-now – Thusitha Sep 28 '17 at 16:57
  • What are you doing that needs 5 microseconds of precision. Or did you just read a click bait article. – Darkrum Sep 28 '17 at 17:18
  • @Darkrum We actually needed surviving system clock manipulations, rather than the microsecond precision. Our actual jobs take longer than the example, and we had a few situations where a Windows Time Update ran during the job and spewed out bad results. – Thunderforge Sep 28 '17 at 18:45
  • @Thunderforge I see. But that's a fault of not having the server configured correctly and IMO not worth the effort. Just programmatically disable windows time updates during tests. – Darkrum Sep 28 '17 at 19:20
  • @Darkrum In this case it's client-side code rather than server-side code, and we can't force end users to disable Windows time updates. But yeah, anybody who is thinking about switching to High Resolution Time should consider if it's really what they need. – Thunderforge Sep 28 '17 at 21:29

4 Answers4

4

I would do it like this:

// mark the start time
performance.mark("start");

// ... 
//  later...
// ...
// mark the end time
performance.mark("end");

// create a measure called 'm' based on the two marks above
performance.measure("m", "start", "end");

// get the start mark, calculate its real-world timestamp using the time origin
var started = performance.getEntriesByName("start")[0];
var startedDt = new Date(performance.timing.navigationStart + started.startTime);

// get the measure we created above
var duration = performance.getEntriesByName("m")[0];
console.log(`job began on ${startedDt} and took ${duration.duration/1000.0} seconds`);

performance.clearMarks();

First, mark the start and end times for your duration measurement. Second, create a duration measurement.

Later on, you get your start and end marks, calculate the started date by combining the time origin with the start mark's timestamp. Lastly, clear the marks.

It's not strictly necessary to get the start mark... you can also calculate the real-world start timestamp from the measure m, which also has a startTime. I used the start mark, but either is valid.

In other words, you can also do this:

// get the measure we created above
var duration = performance.getEntriesByName("m")[0];
var startedDt = new Date(performance.timing.navigationStart + duration.startTime);

console.log(`job began on ${startedDt} and took ${duration.duration/1000.0} seconds`);
3

It can be done like this:

const t0 = performance.now();

// measured job here

const t1     = performance.now(),
      t0Date = new Date(performance.timing.navigationStart + t0).toUTCString();

console.log(`Job began at ${t0Date} and took ${t1 - t0} milliseconds.`);
/* Console formatting only */
.as-console-wrapper { top: 0; }

Note however that after performance.now() MDN page (emphasis mine):

(…) performance.timing.navigationStart + performance.now() will be approximately equal to Date.now().

For me, it's within one second of actual time start.

Przemek
  • 3,855
  • 2
  • 25
  • 33
1

From the MDN,

unlike Date.now(), the values returned by Performance.now() always increase at a constant rate, independent of the system clock (which might be adjusted manually or skewed by software like NTP). Otherwise, performance.timing.navigationStart + performance.now() will be approximately equal to Date.now()

As the name implies performance.now() is to measure performance up to an accuracy of 5 microseconds between two tasks and not to keep the UNIX timeStamp.

performance.timing.navigationStart + performance.now() will be approximately equal to Date.now()

Read more about here.
https://developer.mozilla.org/en-US/docs/Web/API/Performance/now

Thusitha
  • 3,393
  • 3
  • 21
  • 33
0

This is not possible. The return values of performance.now do have the page load as their origin (0) value, and cannot be transformed into a date. According to the spec, you should be able to sum it with performance.timeOrigin to obtain a unix timestamp, however there doesn't seem to be any browser support for this.

If you want to know when your measuring started in wall clock time, I recommend to store a conventional new Date/Date.now() timestamp as well.

Bergi
  • 630,263
  • 148
  • 957
  • 1,375
  • 1
    It looks like `performance.timeOrigin` does not exist in [High Resolution Time Level 1](https://www.w3.org/TR/hr-time-1/) (submitted December 2012), but does in [High Resolution Time Level 2](https://www.w3.org/TR/hr-time/) (submitted August 2017). As you said, browsers don't seem to support this yet. – Thunderforge Sep 28 '17 at 17:09
  • @Thunderforge As Thusita wrote, you might use `performance.timing.navigationStart` as a stand-in, although it's not totally accurate. Or you just calculate it yourself once, using `Date.now() - performance.now()`. – Bergi Sep 28 '17 at 17:23