{"id":50901708,"url":"https://github.com/didrod205/tempora","last_synced_at":"2026-06-16T03:04:43.699Z","repository":{"id":361100985,"uuid":"1253089746","full_name":"didrod205/tempora","owner":"didrod205","description":"Human-friendly time in ~1KB — relative time ('3 minutes ago'), duration format \u0026 parse. Zero-dependency, localized via Intl.","archived":false,"fork":false,"pushed_at":"2026-05-29T07:13:59.000Z","size":38,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-05-29T08:26:35.310Z","etag":null,"topics":["duration","format-duration","humanize","i18n","intl","parse-duration","relative-time","time","timeago","typescript"],"latest_commit_sha":null,"homepage":"https://www.npmjs.com/package/tempora-js","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/didrod205.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null},"funding":{"custom":["https://elab-studio.lemonsqueezy.com/checkout/buy/5d059b89-51d0-456b-b33a-ed56994f7010"]}},"created_at":"2026-05-29T06:29:18.000Z","updated_at":"2026-05-29T07:13:41.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/didrod205/tempora","commit_stats":null,"previous_names":["didrod205/tempora"],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/didrod205/tempora","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/didrod205%2Ftempora","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/didrod205%2Ftempora/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/didrod205%2Ftempora/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/didrod205%2Ftempora/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/didrod205","download_url":"https://codeload.github.com/didrod205/tempora/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/didrod205%2Ftempora/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34388683,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-16T02:00:06.860Z","response_time":126,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["duration","format-duration","humanize","i18n","intl","parse-duration","relative-time","time","timeago","typescript"],"created_at":"2026-06-16T03:04:43.631Z","updated_at":"2026-06-16T03:04:43.688Z","avatar_url":"https://github.com/didrod205.png","language":"TypeScript","funding_links":["https://elab-studio.lemonsqueezy.com/checkout/buy/5d059b89-51d0-456b-b33a-ed56994f7010"],"categories":[],"sub_categories":[],"readme":"\u003cdiv align=\"center\"\u003e\n\n# tempora\n\n**Human-friendly time, in ~1 KB. Relative time, duration formatting \u0026 parsing — zero dependencies, localized via `Intl`.**\n\n[![npm version](https://img.shields.io/npm/v/tempora-js.svg?color=success)](https://www.npmjs.com/package/tempora-js)\n[![bundle size](https://img.shields.io/bundlephobia/minzip/tempora-js?label=gzip)](https://bundlephobia.com/package/tempora-js)\n[![CI](https://github.com/didrod205/tempora/actions/workflows/ci.yml/badge.svg)](https://github.com/didrod205/tempora/actions/workflows/ci.yml)\n[![types](https://img.shields.io/npm/types/tempora-js.svg)](https://www.npmjs.com/package/tempora-js)\n[![license](https://img.shields.io/npm/l/tempora-js.svg)](./LICENSE)\n\n\u003c/div\u003e\n\nThree tiny functions for the time formatting every app ends up needing — without\npulling in a date library or shipping locale data.\n\n```ts\nimport { timeAgo, formatDuration, parseDuration } from \"tempora-js\";\n\ntimeAgo(Date.now() - 3 * 60_000);   // \"3 minutes ago\"\ntimeAgo(then, { locale: \"ko\" });    // \"3분 전\"\n\nformatDuration(90_061_000);         // \"1d 1h 1m 1s\"\nparseDuration(\"1h30m\");             // 5_400_000\n```\n\n---\n\n## Why tempora?\n\n- 🪶 **Zero dependencies.** ~1 KB gzipped. No locale files to ship.\n- 🌍 **Localized for free.** `timeAgo` uses the built-in `Intl.RelativeTimeFormat`, so every language the runtime knows just works.\n- 🔁 **Round-trips.** `parseDuration` and `formatDuration` are two ends of the same pipe.\n- 🧩 **Three focused functions.** No giant API surface, no moment-style mutable objects.\n- ⚙️ **Runs everywhere.** Node 18+, Deno, Bun, Cloudflare Workers and the browser.\n- 🛡️ **Type-safe.** Written in TypeScript, ships full declarations.\n\n## Install\n\n```bash\nnpm install tempora-js\n# or: pnpm add tempora-js  /  yarn add tempora-js  /  bun add tempora-js\n```\n\n\u003e The npm package is published as **`tempora-js`** (the name `tempora` was already reserved on npm). Everything you import stays the same.\n\nShips ESM **and** CommonJS:\n\n```ts\nimport { timeAgo } from \"tempora-js\";        // ESM / TypeScript\nconst { timeAgo } = require(\"tempora-js\");   // CommonJS\n```\n\n## CLI\n\n```bash\nnpx tempora-js ago 2024-01-01        # \"2 years ago\"\nnpx tempora-js duration 5400000      # \"1h 30m\"  (--long → \"1 hour, 30 minutes\")\nnpx tempora-js parse 1h30m           # 5400000   (--human → \"1m 30s\")\nnpx tempora-js ago \"2030-01-01\" --locale ko   # \"5년 후\"\n```\n\nThe bundled command is **`tempora`** (run `npx tempora-js \u003ccmd\u003e`). Commands:\n`ago`, `duration`, `parse`. Options: `--locale`, `--long`, `--human`.\n\n## Usage\n\n### `timeAgo` — relative time\n\n```ts\ntimeAgo(Date.now() - 3 * 60_000);              // \"3 minutes ago\"\ntimeAgo(Date.now() + 2 * 86_400_000);          // \"in 2 days\"\ntimeAgo(\"2026-01-01T00:00:00Z\");               // \"last year\" (relative to now)\n\n// Localized — any BCP-47 locale the runtime supports\ntimeAgo(then, { locale: \"ko\" });               // \"3분 전\"\ntimeAgo(then, { locale: \"es\" });               // \"hace 3 minutos\"\n\n// Tweak the wording\ntimeAgo(yesterday, { numeric: \"always\" });     // \"1 day ago\" (instead of \"yesterday\")\ntimeAgo(then, { style: \"short\" });             // \"3 min. ago\"\n\n// Test deterministically by pinning \"now\"\ntimeAgo(t, { now: fixedTimestamp });\n```\n\nAccepts a `Date`, a millisecond timestamp, or any string `Date.parse` understands.\n\n### `formatDuration` — milliseconds → readable\n\n```ts\nformatDuration(90_061_000);                          // \"1d 1h 1m 1s\"\nformatDuration(5_400_000);                           // \"1h 30m\"\nformatDuration(90_061_000, { largest: 2 });          // \"1d 1h\"\nformatDuration(5_400_000, { long: true });           // \"1 hour, 30 minutes\"\nformatDuration(elapsed, { units: [\"m\", \"s\"] });      // \"75m 30s\"\n```\n\n### `parseDuration` — readable → milliseconds\n\n```ts\nparseDuration(\"1h30m\");          // 5_400_000\nparseDuration(\"2.5h\");           // 9_000_000\nparseDuration(\"1h 30m 5s\");      // 5_405_000\nparseDuration(\"-90s\");           // -90_000\nparseDuration(\"500\");            // 500  (bare number = milliseconds)\nparseDuration(\"1 hour\");         // 3_600_000\n```\n\nThrows a `TypeError` for input it can't understand, so misconfiguration fails loudly.\n\n## API\n\n### `timeAgo(input, options?) =\u003e string`\n\n| Option    | Type                          | Default          | Description                                            |\n| --------- | ----------------------------- | ---------------- | ------------------------------------------------------ |\n| `now`     | `Date \\| number`              | `Date.now()`     | Reference point for the comparison.                    |\n| `locale`  | `string \\| string[]`          | runtime locale   | BCP-47 locale(s) for `Intl.RelativeTimeFormat`.        |\n| `numeric` | `\"auto\" \\| \"always\"`          | `\"auto\"`         | `\"auto\"` allows \"yesterday\"/\"tomorrow\".                |\n| `style`   | `\"long\" \\| \"short\" \\| \"narrow\"` | `\"long\"`       | Width of the output.                                   |\n\n### `formatDuration(ms, options?) =\u003e string`\n\n| Option      | Type             | Default              | Description                                       |\n| ----------- | ---------------- | -------------------- | ------------------------------------------------- |\n| `units`     | `DurationUnit[]` | `[\"d\",\"h\",\"m\",\"s\"]`  | Units to break into, largest → smallest.          |\n| `largest`   | `number`         | all                  | Show at most N largest non-zero units.            |\n| `long`      | `boolean`        | `false`              | Long labels (\"1 hour\") vs short (\"1h\").           |\n| `round`     | `boolean`        | `true`               | Round the smallest displayed unit.                |\n| `separator` | `string`         | `\" \"` / `\", \"`       | Separator between parts.                           |\n\n`DurationUnit` is one of `\"w\" | \"d\" | \"h\" | \"m\" | \"s\" | \"ms\"`.\n\n### `parseDuration(input) =\u003e number`\n\nParses a duration string into milliseconds. A bare number is treated as\nmilliseconds. Throws `TypeError` on unrecognized input.\n\n## Comparison\n\n|                          | `tempora` | `moment` / `dayjs` | `humanize-duration` + `timeago.js` |\n| ------------------------ | :-------: | :----------------: | :--------------------------------: |\n| Zero dependencies        |    ✅     |         ⚠️         |                ✅                  |\n| Relative time            |    ✅     |         ✅         |                ✅                  |\n| Duration format + parse  |    ✅     |         ⚠️         |                ⚠️ (two libs)       |\n| Localized without bundle |    ✅     |         ❌         |                ❌                  |\n| ~1 KB gzipped            |    ✅     |         ❌         |                ❌                  |\n\n## Contributing\n\nContributions are very welcome! Please read [CONTRIBUTING.md](./CONTRIBUTING.md)\nand our [Code of Conduct](./CODE_OF_CONDUCT.md).\n\n```bash\ngit clone https://github.com/didrod205/tempora.git\ncd tempora\nnpm install\nnpm test\n```\n\n## 💖 Sponsor\n\n`tempora` is free and MIT-licensed, built and maintained in spare time. If it\nsaved you from reaching for a heavy date library, please consider supporting it —\nevery bit helps keep the project healthy.\n\n- ⭐ **Star this repo** — the simplest, free way to help others discover it.\n- 🍋 **[Sponsor via Lemon Squeezy](https://elab-studio.lemonsqueezy.com/checkout/buy/5d059b89-51d0-456b-b33a-ed56994f7010)** — one-time or recurring support.\n\n\u003e Sponsoring? Open an issue and we'll add your name/logo here. Thank you! 🙏\n\n## License\n\n[MIT](./LICENSE) © tempora contributors\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdidrod205%2Ftempora","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdidrod205%2Ftempora","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdidrod205%2Ftempora/lists"}