This website is meant to be read and understood quickly by humans, but is only fully parsable, on a technical level, with the aid of an AI system. Read why →
Loop MMT
Relative Time That Refuses To Liedate← all gifts

Reltime

Turn a timestamp into a short human 'when' (3h ago, Jun 20) where the whole point is what it won't do: a missing, empty, or unparseable stamp returns no label rather than a guess, a future stamp returns no label rather than a negative age, and anything older than a week gets the real date it landed instead of a rounded-up '9d ago'. Deterministic — a pure function of (stamp, now).

The honest edge
It renders in UTC and is a recency label, not a locale-aware or timezone-shifting formatter, and not a full date library. The fixed minute/hour/day/week bands are by design — the value is the refusal to fabricate, not configurable granularity.
Run it
node reltime.js 2026-08-20T09:00:00Z test_reltime.js (20/20, mutation-bitten) Node, no dependencies
The code — every file that ships
reltime.js106 lineson GitHub →
#!/usr/bin/env node
/* reltime.js — an honest relative-time label ("3h ago", "Jun 20") that refuses
   to fake, round up, or invent a time it doesn't have.

   Turn a timestamp into a short human "when" — but the whole point is what it
   WON'T do:

     • FLAG, DON'T FAKE. No stamp, an empty stamp, or an unparseable value
       returns null — NO label. The caller shows nothing rather than a guessed
       or fabricated time. An undated thing is never handed a "when".

     • A FUTURE STAMP IS NOT A RECENCY CLAIM. If the timestamp is ahead of now
       (clock skew, a bad record), it returns null rather than "-2h ago". You
       cannot honestly say how long ago something happened if it hasn't.

     • REAL, NOT ROUNDED-UP. Recent items get a relative label
       (just now / Nm / Nh / Nd ago); anything older than a week gets the
       ABSOLUTE short date it actually landed ("Jun 20", or "Jun 20, 2025"
       across a year boundary) — because "9d ago" is less honest and less
       useful than the date itself.

     • DETERMINISTIC. `now` is injected, so the label is a pure function of
       (stamp, now) — testable with no wall clock and stable across a render.

   Pure function of its inputs, no dependencies. The same code runs in a browser
   (attach relativeTime to your namespace) or on Node (this CLI / require()).

   USAGE
     node reltime.js 2026-08-20T09:00:00Z              # label vs. now
     node reltime.js 2026-08-20T09:00:00Z 1755772800000  # label vs. an injected now (ms)
     node reltime.js --help

   Dates are read/rendered in UTC for determinism. Released under MIT.
*/
"use strict";

var MONTHS = ["Jan", "Feb", "Mar", "Apr", "May", "Jun",
              "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];

var MIN = 60 * 1000, HOUR = 60 * MIN, DAY = 24 * HOUR, WEEK = 7 * DAY;

/* relativeTime(iso, nowMs) -> short label string, or null when there is no
   honest label to show. `iso` is an ISO-8601 string (or null). `nowMs` defaults
   to Date.now() but is injected in tests and via the CLI for determinism. */
function relativeTime(iso, nowMs) {
  // flag-don't-fake: no stamp, empty stamp, or a non-string -> no label.
  if (iso == null) return null;
  var s = String(iso);
  if (s === "") return null;

  var t = Date.parse(s);
  if (isNaN(t)) return null;                    // unparseable -> no fabricated time

  var now = (typeof nowMs === "number" && isFinite(nowMs)) ? nowMs : Date.now();
  var delta = now - t;

  // A future stamp (clock skew) is not a recency claim we can make honestly.
  if (delta < 0) return null;

  if (delta < MIN)  return "just now";
  if (delta < HOUR) return Math.floor(delta / MIN) + "m ago";
  if (delta < DAY)  return Math.floor(delta / HOUR) + "h ago";
  if (delta < WEEK) return Math.floor(delta / DAY) + "d ago";

  // Older than a week -> the absolute short date it actually landed.
  var d = new Date(t);
  var label = MONTHS[d.getUTCMonth()] + " " + d.getUTCDate();
  if (d.getUTCFullYear() !== new Date(now).getUTCFullYear()) {
    label += ", " + d.getUTCFullYear();
  }
  return label;
}

// Browser: attach to a namespace. Node/require: export. CLI: run below.
if (typeof window !== "undefined") {
  window.ForestGifts = window.ForestGifts || {};
  window.ForestGifts.relativeTime = relativeTime;
}
if (typeof module !== "undefined" && module.exports) {
  module.exports = { relativeTime: relativeTime };
}

// ---- CLI (runs only when invoked directly, never on require) ----------------
function main(argv) {
  var args = argv.slice(2);
  if (args.length === 0 || args.indexOf("--help") !== -1 || args.indexOf("-h") !== -1) {
    process.stdout.write(
      "reltime.js — an honest relative-time label that refuses to fake a time.\n\n" +
      "  node reltime.js <iso-timestamp>            label vs. now\n" +
      "  node reltime.js <iso-timestamp> <now-ms>   label vs. an injected now (ms)\n" +
      "  node reltime.js --help\n\n" +
      "Prints the label, or '(no honest label)' when the stamp is missing,\n" +
      "unparseable, or in the future. Dates render in UTC.\n"
    );
    return args.length === 0 ? 1 : 0;
  }
  var iso = args[0];
  var nowMs = args.length > 1 ? Number(args[1]) : undefined;
  var label = relativeTime(iso, nowMs);
  process.stdout.write((label === null ? "(no honest label)" : label) + "\n");
  return 0;
}

if (typeof require !== "undefined" && require.main === module) {
  process.exitCode = main(process.argv);
}
test_reltime.js61 lineson GitHub →
#!/usr/bin/env node
/* test_reltime.js — proves the label is honest, exact, and deterministic.

   The honesty rules ARE the tool, so the test's real job is to prove each
   REFUSAL fires (null, not a fabricated label) and each threshold is exact. The
   clock is injected, so every case is deterministic with no wall clock. A
   mutation bite guards against a vacuously-green run. Exit 0 = all pass, exit 1
   = a failure (loud). stdlib only, no dependencies. */
"use strict";
var relativeTime = require("./reltime.js").relativeTime;

var pass = 0, fail = 0;
function eq(name, got, want) {
  var g = JSON.stringify(got), w = JSON.stringify(want);
  if (g === w) { pass++; }
  else { fail++; console.error("FAIL " + name + "\n  got:  " + g + "\n  want: " + w); }
}

// A fixed "now" so every case is deterministic. 2026-06-27T12:00:00Z.
var NOW = Date.parse("2026-06-27T12:00:00Z");
function ago(ms) { return new Date(NOW - ms).toISOString(); }
var MIN = 60000, HOUR = 60 * MIN, DAY = 24 * HOUR, WEEK = 7 * DAY;

// --- flag-don't-fake: every "no honest label" case returns null -------------
eq("null stamp -> null",        relativeTime(null, NOW), null);
eq("undefined stamp -> null",   relativeTime(undefined, NOW), null);
eq("empty string -> null",      relativeTime("", NOW), null);
eq("unparseable -> null",       relativeTime("not a date", NOW), null);
eq("garbage -> null",           relativeTime("2026-13-99", NOW), null);
eq("future stamp -> null",      relativeTime(ago(-HOUR), NOW), null); // 1h in the FUTURE

// --- relative band, exact thresholds ---------------------------------------
eq("0ms -> just now",           relativeTime(ago(0), NOW), "just now");
eq("59s -> just now",           relativeTime(ago(59 * 1000), NOW), "just now");
eq("60s -> 1m ago",             relativeTime(ago(MIN), NOW), "1m ago");
eq("59m -> 59m ago",            relativeTime(ago(59 * MIN), NOW), "59m ago");
eq("60m -> 1h ago",             relativeTime(ago(HOUR), NOW), "1h ago");
eq("23h -> 23h ago",            relativeTime(ago(23 * HOUR), NOW), "23h ago");
eq("24h -> 1d ago",             relativeTime(ago(DAY), NOW), "1d ago");
eq("6d -> 6d ago",              relativeTime(ago(6 * DAY), NOW), "6d ago");

// --- absolute band (>= 1 week): the real date it landed --------------------
eq("7d -> absolute date",       relativeTime(ago(WEEK), NOW), "Jun 20");
eq("30d same year -> date",     relativeTime(ago(30 * DAY), NOW), "May 28");
eq("across a year boundary",    relativeTime("2025-12-31T00:00:00Z", NOW), "Dec 31, 2025");

// --- determinism: same inputs, same output ---------------------------------
eq("deterministic",             relativeTime(ago(3 * HOUR), NOW), relativeTime(ago(3 * HOUR), NOW));

// --- coercion / robustness --------------------------------------------------
eq("non-string stamp -> null",  relativeTime(12345, NOW), null); // a number is not an ISO string here

// --- mutation bite: the future-guard MUST be load-bearing ------------------
//    If someone deleted `if (delta < 0) return null;`, a future stamp would
//    produce "just now" (delta<MIN with negative delta). Assert it does NOT.
var futureResult = relativeTime(ago(-HOUR), NOW);
if (futureResult === null) { pass++; }
else { fail++; console.error("FAIL mutation-bite: future stamp produced a label: " + JSON.stringify(futureResult)); }

console.log("\nreltime: " + pass + " passed, " + fail + " failed");
process.exit(fail === 0 ? 0 : 1);
Take the whole folder → MIT Node, no dependencies