subs-free.openapps.xyz

Source

This app’s code and revision history are public. Download a copy or explore the files below.

Public
The app’s code and revision history are public.
Owned app · rev 1ae4feb2bebf
Run it yourself
Take the whole tree and run it on your own machine. The download needs no account. Cloning needs a sign-in.
Download .zip
bounded clone 5af1a249bbbef1db1b4c5ec8
A copy you download is not bounded. The guarantees this app runs under - declared egress, proven invariants, writes that fail closed - live in the runtime serving this page, not in the files. What you get is auditable source you can read and run, with no runtime guarantees attached.
← All files
src/lib/dates.ts
// Dates are the person's local calendar days, kept as "YYYY-MM-DD". For arithmetic they become day
// numbers (days since 1970-01-01), which never drift with time zones or daylight saving.
const DAY_MS = 86400000;

/** Today as YYYY-MM-DD, in the person's time zone. */
export function dayKey(d = new Date()): string {
  return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, "0")}-${String(d.getDate()).padStart(2, "0")}`;
}

export function dayNum(key: string): number {
  const [y, m, d] = key.split("-").map(Number);
  return Math.round(Date.UTC(y, m - 1, d) / DAY_MS);
}

export function keyOf(n: number): string {
  return new Date(n * DAY_MS).toISOString().slice(0, 10);
}

export const isDayKey = (s: string) => /^\d{4}-\d{2}-\d{2}$/.test(s) && keyOf(dayNum(s)) === s;

export const addDays = (key: string, n: number) => keyOf(dayNum(key) + n);

/** Adds months, keeping the given day of the month where the month has it (Jan 31 + 1 month = Feb 28). */
export function addMonths(key: string, n: number, day = Number(key.slice(8, 10))): string {
  const y = Number(key.slice(0, 4));
  const m = Number(key.slice(5, 7)) - 1 + n;
  const yy = y + Math.floor(m / 12);
  const mm = ((m % 12) + 12) % 12;
  const last = new Date(Date.UTC(yy, mm + 1, 0)).getUTCDate();
  return `${yy}-${String(mm + 1).padStart(2, "0")}-${String(Math.min(day, last)).padStart(2, "0")}`;
}

/** Whole months from a to b, counting by calendar month only. */
export const monthsBetween = (a: string, b: string) =>
  (Number(b.slice(0, 4)) - Number(a.slice(0, 4))) * 12 + Number(b.slice(5, 7)) - Number(a.slice(5, 7));

/** A day in the person's language: "Mon, Nov 3", with the year when it is not this year. */
export function fmtDay(key: string, today = dayKey(), opts?: Intl.DateTimeFormatOptions): string {
  const sameYear = key.slice(0, 4) === today.slice(0, 4);
  return new Date(dayNum(key) * DAY_MS).toLocaleDateString(undefined, {
    timeZone: "UTC",
    ...(opts ?? (sameYear ? { weekday: "short", month: "short", day: "numeric" } : { month: "short", day: "numeric", year: "numeric" })),
  });
}

/** "today", "tomorrow", "in 5 days", "3 days ago". */
export function relDay(key: string, today = dayKey()): string {
  const d = dayNum(key) - dayNum(today);
  if (d === 0) return "today";
  if (d === 1) return "tomorrow";
  if (d === -1) return "yesterday";
  return d > 0 ? `in ${d} days` : `${-d} days ago`;
}

Change history

Point store links to openapps.free
1ae4feb2be · 18 hours ago
Bind agent address in policy
99bbab3f0c · 19 hours ago
Use OpenApps service URLs and add contribution strip
781c3c23e5 · 19 hours ago
Bounded source sync
048e3257a5 · 1 day ago
Bounded source sync
7a358a3be6 · 1 day ago
Bounded source sync
0c833fcb0e · 1 day ago