Time Representations and Decimal Years

Titles are cool.

I wanted to represent a timespan in terms of decimal years, for no apparent reason. This isn’t the most interesting thing I have to talk about, but in this “era”* of political correctness, one can’t even be politically correct without someone calling you out on it. Yeah, I’m taking the easy way out.

Anyway, I noticed that there’s a variety of ways to calculate this timespan, specifically the non-integer portion. As always, there’s a naïve way to compute the entire number, namely calculate the difference in milliseconds (or some similarly small unit of time), and divide by the number of milliseconds in a year. How many milliseconds are in a year? That’s a silly question, because years don’t have a constant length. We could use the average, but we’d generally be off.

So we know the root issue is that years have a non-constant length. What does a decimal year represent, then? Just like everything else about our measurement of time, it’s an arbitrary representation. I think it makes the most sense to make the representation continuous - if my decimal year clock says “3.5” and I wait half of this year, it should say “4.0,” and every moment of time should be a continuous and linear representation of those two points in time.

So I’ve established something meaningless here, because that doesn’t get us any closer to an answer. Because the decision is meaningless, here are a few possibilities for calculating the decimal: using average length of a year, using the length of the current year, using the length of the destination year, using a combination of the current and destination years, or using the average/combination lengths of the years between the current and destination years (inclusively or exclusively).

I ended up deciding on the fourth option - using a combination of the current and destination years. First, convert the dates into their absolute decimal years, then take the difference of those decimals.

// date must be a valid date object
function getDecimalYear(date) {
  var year = date.getFullYear(), start = +new Date(year, 0);
  return year + (date - start) / (new Date(year + 1, 0) - start);

function getDecimalAge(birth) {
  var birthDate = new Date(birth);
  if (isNaN(birthDate)) return null;
  return getDecimalYear(new Date()) - getDecimalYear(birthDate);

*because there’s an era for everything

Edit #

I’ve actually changed my mind, and am opting to use the following:

// precondition: start and end are Date objects
function getDecimalDiff(start, end) {
  var mostRecent = new Date(start), endYear = end.getFullYear();
  if (mostRecent > end) mostRecent.setFullYear(endYear -= 1);
  var subsequent = new Date(mostRecent);
  subsequent.setFullYear(endYear + 1);
  return (endYear - start.getFullYear()) + (end - mostRecent) / (subsequent - mostRecent);

Now read this


Our databases are inflexible. Our databases are designed for specific tasks, even if they’re intended to be general purpose. They make decisions for us, choosing how consistency, availability, and partition tolerance fit together. By... Continue →