Yesterday I wrote a somewhat cryptic comment on a Strava activity headlined 40.87 miles ahead of pace, as of this millisecond:

If you want a fun math problem, you can find out when I changed the title of this post, to an accuracy range of 5 minutes and 15 seconds. The only data points you need to derive this number are (1) my precise distance up to this point (2) my yearly goal. The latter is publicly available on my profile, but I suppose to get the former you’d have to hack my Strava account, since OAuth tokens are per-athlete. So never mind. 🙃

What in the world am I talking about? Well, I have a one thousand mile running goal this year. When we do the math in a 365-day year, that comes out to about 2.73973 miles that we must average every day.

1000 / 365 = 2.7397260274

In my headline, I gave away the fact that I was 40.87 miles ahead of pace at the time that I modified the headline. That’s rounded to the hundredth place. How many times per day would this number change? We can easily find out. We need to run 2.73973 miles per day, and since we’re keeping track of this in hundredths, we multiply that by 100:

2.7397260274 * 100 = 273.9726

This resulting 273.9726 is how many times per day that 40.87 number will regress by a hundredth of a mile. To answer how many times per day this would change in seconds, we find out how many seconds are in a day:

60 * 60 * 24 = 86400

And then we divide that by the number of times our pace will regress:

86400 / 273.9726 = 315.3600

In other words, that number will regress every 315.36 seconds, or every 5 minutes and fifteen seconds. This is why I said in the Strava activity that we could get within “an accuracy range of 5 minutes and 15 seconds.”

With that explanation in the backdrop, we can get to the fun math problem of which I spoke. We need my precise distance year-to-date in order to be able to solve that problem, and I have that precise distance. It’s 262824.7 meters. Let’s figure out how many miles that is:

262824.7 * 0.000621371 = 163.3116466637

Next we need to find out what my distance for the year up to this point should be in order for me to be on pace. To do this, we take 163 miles and subtract from it the 40 miles:

163.3116466637 - 40.87 = 122.44

Now all we have to do is find out how many 315.3600-second intervals occur from January 1 to the time that we’d need to have run 122.44 miles, get the total number of seconds that occurred in those intervals, and we’ll have our answer.

First, we need to establish the pattern. For the first (315.3600 / 2) seconds of the year, we need to have run .004978437341451041 miles. Since we’re rounding to the hundredths, that comes out to be zero miles. For the 315.3600 seconds following, we need to have run .01 miles. For the next 315.3600 seconds, we need to have run .02 miles.

Here’s some JavaScript to demonstrate. First some core functions, which are the backbone of my distance web app:

const amountOfYearExpired = (time = new Date().getTime()) => {
  const startOfYear = new Date(`Jan 1, ${new Date(time).getFullYear()}`).getTime();
  const startOfNextYear = new Date(`Jan 1, ${new Date(time).getFullYear() + 1}`).getTime();
  const percentagePassed = (time - startOfYear) / (startOfNextYear - startOfYear);
  return percentagePassed;
}

const metersToMiles = (meters) => {
  return (meters * 0.000621371);
};

paceDelta = (currentDistance, goal, timestamp = new Date().getTime()) => {
  if (!goal) {
    return 0;
  }
  const percentagePassed = amountOfYearExpired(timestamp);
  const currentTarget = goal * percentagePassed;
  // if this number is negative then we're behind pace
  const output = metersToMiles(currentDistance) - currentTarget;
  return output;
};

Next we use them like so to prove the established pattern above.

// Subtract 1 to get the value at the very last millisecond before
// it switches to the second half of the first interval. Multiply
// by 1000 to get the value in milliseconds.
const firstHalfOfFirstInterval = (315.3600 / 2) * 1000 - 1;
const date = new Date(new Date('January 1, 2018').getTime() + firstHalfOfFirstInterval);
const delta = Math.abs(paceDelta(0, 1000, date));

The above delta variable has a value of 0.004999968290208016. Rounded to hundredths, that’s 0. If we don’t subtract 1 from firstHalfOfFirstInterval, we get exactly .005. Rounded to hundredths, that’s .01. It stays at .01 for the next 315.3600 seconds:

Math.abs(paceDelta(0, 1000, new Date(new Date('January 1, 2018').getTime() + ((315.3600 / 2) * 1000 - 1) + 315.3600 * 1000)))
// outputs 0.014999968290208016

And then once we re-supply that one final millisecond, we arrive at .02 when rounding to the hundredths:

Math.abs(paceDelta(0, 1000, new Date(new Date('January 1, 2018').getTime() + ((315.3600 / 2) * 1000) + 315.3600 * 1000)))
// outputs 0.015000000000000001

With this established pattern, all we have to do is multiply 12,243 (122.44 miles times 100, since our interval duration is per one hundredth of a mile increments, and then subtract 1, since the full 12,244 will round up and yield our upper limit) by our interval duration, and then add a half an interval to that (because of the aforementioned rounding pattern), and we’ll have the start time of our range.

12,243.5 * 315.3600 = 3,861,110.16

To get the end time of our range, we add an interval of 315.3600 from the previous formula:

12,244.5 * 315.3600 = 3,861,425.52

Let’s multiply those by 1000 to change them to milliseconds, and make dates out of them. Notice we subtract 1 millisecond from the finish since otherwise it’s technically in the next range:

const jan1InUnix = new Date('January 1, 2018').getTime();
// Wed Feb 14 2018 16:31:50 GMT-0600 (CST)
const start = new Date(jan1InUnix + 3861110160);
// Wed Feb 14 2018 16:37:05 GMT-0600 (CST)
const finish = new Date(jan1InUnix + 3861425519);

We can check these numbers to make sure they’re in the right range and representative of that range’s extreme lower and upper limits, which indeed they are:

// outputs 122.435 which rounds to 122.44, the lower limit
Math.abs(paceDelta(0, 1000, jan1InUnix + 3861110160));
// outputs 122.4449999682902 which also rounds to 122.44, the upper limit
Math.abs(paceDelta(0, 1000, jan1InUnix + 3861425519));

When we look at the activity, the start time was around 3:25 PM CST with a moving time of 53:27 minutes. That puts the finish time in the 4:18 PM CST range. We can know with confidence that somewhere between 4:31:50 PM CST and 4:37:05 PM CST, about 14 to 19 minutes after I completed the run, I modified the headline. That’s an accuracy range of five minutes and fifteen seconds.