{"version":3,"file":"time.mjs","names":[],"sources":["../../src/common/time.ts"],"sourcesContent":["import { base32ToNumber, numberToBase32 } from './data/math'\n\nlet testModeTime: number | undefined\n\n/**\n * Set a fixed timestamp (ms since epoch) used by `getTimestamp` for tests.\n *\n * Pass a millisecond timestamp (for example created with Date.UTC) to force\n * time-based helpers to return deterministic values during tests.\n *\n * @param ts - timestamp in milliseconds (defaults to 2000-01-01T00:00:00.000Z)\n */\nexport function setTimestampTest(ts = Date.UTC(2000, 0, 1, 0, 0, 0, 0)) {\n  testModeTime = ts\n}\n\n/**\n * Return the current timestamp in milliseconds.\n *\n * In test mode (when `setTimestampTest` was called) the forced value is\n * returned instead of the real current time.\n *\n * @returns timestamp in milliseconds\n */\nexport function getTimestamp(): number {\n  return testModeTime ?? Date.now()\n}\n\n/**\n * Return the current timestamp in seconds.\n *\n * Uses `getTimestamp()` which can be overridden for tests with\n * `setTimestampTest`.\n *\n * @returns timestamp in whole seconds (integer)\n */\nexport function getTimestampInSeconds(): number {\n  return Math.floor(getTimestamp() / 1000)\n}\n\n/**\n * Convert a UNIX timestamp in seconds to a JavaScript Date.\n *\n * @param ts - seconds since epoch\n * @returns Date instance corresponding to the provided seconds\n */\nexport function dateFromSeconds(ts: number): Date {\n  return new Date(ts * 1000)\n}\n\n// typeof performance !== \"undefined\" ? performance.now() : new Date().getTime()\n\n/**\n * Format a millisecond duration into a human readable string.\n *\n * - Values >= 1000ms are shown in seconds with one decimal (e.g. \"1.2 s\").\n * - Smaller values are shown in milliseconds with two decimals (e.g. \"123.45 ms\").\n *\n * @param ms - duration in milliseconds\n * @returns formatted duration string\n */\nexport function formatMilliseconds(ms: number): string {\n  return ms > 999 ? `${(ms / 1000).toFixed(1)} s` : `${ms.toFixed(2)} ms`\n}\n\n/**\n * Parses the given date candidates and returns the first valid Date object found.\n *\n * @param dateCandidates - The date candidates to parse, which can be either strings or Date objects.\n * @returns The parsed Date object, or undefined if no valid date is found.\n */\nexport function parseDate(\n  ...dateCandidates: (string | Date)[]\n): Date | undefined {\n  for (const dateCandidate of dateCandidates) {\n    if (dateCandidate instanceof Date)\n      return dateCandidate\n\n    if (typeof dateCandidate === 'string') {\n      let date = null\n      if (dateCandidate.includes(':')) {\n        try {\n          date = new Date(dateCandidate)\n        }\n        catch (err) {}\n      }\n      if (!(date instanceof Date)) {\n        const m = /(\\d\\d\\d\\d)-(\\d\\d)-(\\d\\d)/.exec(dateCandidate)\n        if (m)\n          date = new Date(+m[1], +m[2] - 1, +m[3], 12, 0)\n      }\n      if (date instanceof Date)\n        return date\n    }\n  }\n}\n\n/**\n * Return a high-resolution timestamp in milliseconds.\n *\n * Uses the `performance.now()` clock when available (monotonic, high\n * resolution), otherwise falls back to `Date.now()`.\n *\n * @returns timestamp in milliseconds (relative for performance.now, epoch for Date.now)\n */\nexport function getPerformanceTimestamp(): number {\n  return typeof performance !== 'undefined' ? performance.now() : Date.now()\n}\n\n/**\n * Create a simple duration reporter.\n *\n * Returns a function that, when called, reports the elapsed time since\n * `duration()` was called. The returned string is formatted via\n * `formatMilliseconds`.\n *\n * Example: const t = duration(); // do work...; console.log(t());\n *\n * @returns a zero-argument function that returns the elapsed time string\n */\nexport function duration(): () => string {\n  const t0 = getPerformanceTimestamp()\n\n  return function (): string {\n    const duration = getPerformanceTimestamp() - t0\n    return formatMilliseconds(duration)\n\n    // if (duration > 500)\n    //   return `${(duration / 1000).toFixed(4)}s`\n\n    // // https://elijahmanor.com/format-js-numbers\n    // // https://tc39.es/proposal-unified-intl-numberformat/section6/locales-currencies-tz_proposed_out.html#sec-issanctionedsimpleunitidentifier\n    // return duration.toLocaleString('en-US', {\n    //   style: 'unit',\n    //   unit: 'millisecond',\n    //   notation: 'compact',\n    //   compactDisplay: 'long',\n    // })\n  }\n}\n\n/**\n * If you parsed a date string that didn't include a time zone, adjust the\n * naive UTC components into a local Date with the same Y/M/D/H/M/S values.\n *\n * This effectively treats the input as if it already represented local time\n * and builds a corresponding Date using the local timezone.\n */\nexport function datetimeToLocal(fromDate: Date): Date {\n  return new Date(\n    fromDate.getUTCFullYear(),\n    fromDate.getUTCMonth(),\n    fromDate.getUTCDate(),\n    fromDate.getUTCHours(),\n    fromDate.getUTCMinutes(),\n    fromDate.getUTCSeconds(),\n    fromDate.getUTCMilliseconds(),\n  )\n}\n\n/**\n * If you parsed a date string that didn't include a time zone, adjust the\n * local date components into a UTC Date with the same Y/M/D/H/M/S values.\n */\nexport function datetimeToUTC(fromDate: Date): Date {\n  return new Date(Date.UTC(\n    fromDate.getFullYear(),\n    fromDate.getMonth(),\n    fromDate.getDate(),\n    fromDate.getHours(),\n    fromDate.getMinutes(),\n    fromDate.getSeconds(),\n    fromDate.getMilliseconds(),\n  ))\n}\n\n// const tsMs2000 = 946684800000   // same as Jan 11 1970 in ms\n// const tsMs2500 = 16725225600000 // same as Jul 13 1970 in ms\n//               1000000000000\n\n/**\n * Convert a timestamp in milliseconds to seconds.\n *\n * When `smart` is true the function will try to detect already-second\n * timestamps and return them unchanged (heuristic threshold). For example,\n * small numbers (< 1e12) are likely seconds and are returned as-is.\n *\n * @param ts - timestamp in milliseconds or seconds\n * @param smart - enable heuristic detection (default: true)\n * @returns timestamp in seconds\n */\nexport function timestampMillisecondsToSeconds(ts: number, smart = true): number {\n  if (ts <= 0)\n    return 0\n  if (smart && ts < 1000000000000) { // TODO find a better threshold and add tests\n    return ts\n    // log.warn('Timestamp might already be in seconds?', ts)\n  }\n  return Math.floor(ts / 1000)\n}\n\n/**\n * Convert a timestamp in seconds to milliseconds.\n *\n * When `smart` is true the function tries to detect already-millisecond\n * values and returns them unchanged (heuristic threshold). Very large\n * numbers (> 1e12) are assumed to already be milliseconds.\n *\n * @param ts - timestamp in seconds or milliseconds\n * @param smart - enable heuristic detection (default: true)\n * @returns timestamp in milliseconds\n */\nexport function timestampSecondsToMilliseconds(ts: number, smart = true): number {\n  if (ts <= 0)\n    return 0\n  if (smart && ts > 1000000000000) { // TODO find a better threshold and add tests\n    return ts\n    // log.warn('Timestamp might already be in milliseconds?', ts)\n  }\n  return Math.floor(ts * 1000)\n}\n\n/** Number of milliseconds in (approx.) one year (365 days). */\nexport const TIME_YEAR_MS = 31536000000 // 365 * 24 * 60 * 60 * 1000\n/** Number of seconds in (approx.) one year (365 days). */\nexport const TIME_YEAR_S = 31536000 // 365 * 24 * 60 * 60\n/** Number of milliseconds in (approx.) one month (30 days). */\nexport const TIME_MONTH_MS = 2592000000 // 30 * 24 * 60 * 60 * 1000\n/** Number of seconds in (approx.) one month (30 days). */\nexport const TIME_MONTH_S = 2592000 // 30 * 24 * 60 * 60\n/** Number of milliseconds in one week (7 days). */\nexport const TIME_WEEK_MS = 604800000 // 7 * 24 * 60 * 60 * 1000\n/** Number of seconds in one week (7 days). */\nexport const TIME_WEEK_S = 604800 // 7 * 24 * 60 * 60\n/** Number of milliseconds in one day (24 hours). */\nexport const TIME_DAY_MS = 86400000 // 24 * 60 * 60 * 1000\n/** Number of seconds in one day (24 hours). */\nexport const TIME_DAY_S = 86400 // 24 * 60 * 60\n/** Number of milliseconds in one hour (60 minutes). */\nexport const TIME_HOUR_MS = 3600000 // 60 * 60 * 1000\n/** Number of seconds in one hour (60 minutes). */\nexport const TIME_HOUR_S = 3600 // 60 * 60\n/** Number of milliseconds in one minute (60 seconds). */\nexport const TIME_MINUTE_MS = 60000 // 60 * 1000\n/** Number of seconds in one minute (60 seconds). */\nexport const TIME_MINUTE_S = 60 // 60\n\n// BUILD NUMBER\n\nconst buildNumberSeconds = 5\nconst buildNumberPadding = 6\nconst buildStartSeconds = 1735686000 // 2025-01-01\n\n/** Build number is minutes since 2025-01-01 in base32 \"agnoster\" encoded format */\nexport function getBuildNumber(): string {\n  const buildNumber = Math.floor((getTimestampInSeconds() - buildStartSeconds) / buildNumberSeconds)\n  return numberToBase32(buildNumber, buildNumberPadding)\n}\n\nexport function getSecondsFromBuildNumber(buildNumber: string): number {\n  return buildStartSeconds + (base32ToNumber(buildNumber) * buildNumberSeconds)\n}\n"],"mappings":"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"}