Filenames that Alphanumerically Sort by Descending Timestamp

January 2nd, 2015 Permalink

The professional life of a programmer consists of an endless series of small trials that in a sane world would never exist. For example, finding oneself needing to generate filenames that sort in descending order by file modification timestamp. Why, oh why, would anyone ever be stuck with a file listing technology that doesn't allow the user to simply specify a sort by that timestamp? This does actually happen, however, I'm sad to say. It is trivial to create a filename format that sorts by ascending timestamp: anything like YYYY-MM-DD-HH-SS will work just fine. Then your file listing technology should have some simple way to reverse that list. What if it doesn't, however? Woe is you.

For the sake of never again repeating the short segment of my life in which I had to think about filename formats that sort in descending timestamp order, here I'll set down one crude way of generating unwieldy but informative filenames that do just that. I'll use Node.js-style Javascript and the dateformat package, but obviously the same approach will work in any language:

var util = require('util');
var dateFormat = require('dateformat');

/**
 * Generate a filename of a form that will sort alphanumerically by
 * descending timestamp.
 *
 * @param {Date|number} date A date instance or timestamp.
 * @param {String} postfix The final section of the filename, e.g. "machine.log"
 * @return {String} "[sort-section]-ts-yyyy-mm-dd-HH-MM-ss-l-machine.log".
 */
function generateFilename (date, postfix) {
  // Convert the date to a universal timestamp if necessary.
  if (typeof date !== 'number') {
    date = date.getTime();
  }

  // Pick a future timestamp that is more distant than any possible continued use
  // of this software.
  var futureDate = Date.UTC(2100, 0);

  // Now create a sortable number string by subtracting one timestamp from the
  // other. The method below ensures padding with leading zeros to keep sorting
  // consistent if the leading digits fall off in the course of time. Not likely to
  // happen here, but you could certainly pick a future date that did cause this to
  // be an issue.
  var paddingLength = ('' + futureDate).length
  var padding = new Array(paddingLength + 1).join('0');
  var sorter = (padding + (futureDate - date)).slice(0 - paddingLength);

  // The human-readable timestamp portion of the filename, including milliseconds.
  // Note the use of UTC for consistence.
  var humanTimestamp = dateFormat(date, 'UTC:yyyy-mm-dd-HH-MM-ss-l');

  return util.format(
    '%s-ts-%s-%s',
    sorter,
    humanTimestamp,
    postfix
  );
}

If you run a quick test of the output for a few recent timestamps, you'll see something like the set of filenames below. Note that the leading numeric string sorts in reverse order to the human readable timestamp, as expected.

2682304975198-ts-2015-01-01-19-17-04-802-machine.log
2682304995093-ts-2015-01-01-19-16-44-907-machine.log
2682305044764-ts-2015-01-01-19-15-55-236-machine.log
2682305077715-ts-2015-01-01-19-15-22-285-machine.log
2682305111674-ts-2015-01-01-19-14-48-326-machine.log
2682305143345-ts-2015-01-01-19-14-16-655-machine.log
2682305179602-ts-2015-01-01-19-13-40-398-machine.log
2682305201789-ts-2015-01-01-19-13-18-211-machine.log
2682305221780-ts-2015-01-01-19-12-58-220-machine.log
2682305315096-ts-2015-01-01-19-11-24-904-machine.log