dateface
v0.1.1
Published
Tiny, dependency-free, timezone-aware date formatting for JavaScript and TypeScript. Simple showDate(input, format) API.
Maintainers
Readme
dateface
Tiny, dependency-free, timezone-aware date formatting for JavaScript and TypeScript.
dateface gives you one obvious function, showDate(input, format), that turns a Date, an ISO string, or a Unix timestamp into a nicely formatted string in the user's local timezone, using only native Intl APIs under the hood.
import { showDate } from "dateface";
showDate(1757917800, "DD MMM YYYY"); // "15 Sep 2025"
showDate(1757917800000, "DD MMM YYYY"); // "15 Sep 2025" (ms works too)
showDate(new Date(), "YYYY-MM-DD HH:mm:ss");
showDate(date, "relative"); // "2 hours ago"- Zero runtime dependencies. Just
Intl. - Timezone-correct. Absolute instants are converted to the target zone; DST handled by the engine.
- Seconds or milliseconds. Auto-detected, or set explicitly.
- ESM + CommonJS, first-class TypeScript types, tree-shakeable.
Table of contents
- Installation
- Quick start
- Supported inputs
- Formatting tokens
- Presets
- Relative time
- Timezone behavior
- Locale
- Options
- Browser usage
- Node.js usage
- Error handling
- API reference
- Why this package exists
- License
Installation
pnpm add dateface
# or
npm install dateface
# or
yarn add datefaceQuick start
import { showDate } from "dateface";
// Unix seconds
showDate(1757917800, "DD MMM YYYY"); // "15 Sep 2025"
// Unix milliseconds, same instant
showDate(1757917800000, "DD MMM YYYY"); // "15 Sep 2025"
// ISO string, rendered in the user's local timezone
showDate("2025-09-15T18:30:00Z", "DD MMM YYYY, hh:mm A");
// A Date object
showDate(new Date(), "DD/MM/YYYY");
// Presets
showDate(Date.now(), "datetime"); // "15 Sep 2025, 6:30 PM"
showDate(Date.now(), "relative"); // "just now"
// Explicit timezone / locale
showDate(1757917800, "DD MMM YYYY, HH:mm", {
timezone: "Asia/Kolkata",
locale: "en-IN",
}); // "15 Sep 2025, 12:00"Supported inputs
type DateInput = Date | string | number;| Input | Notes |
| -------- | ----------------------------------------------------------------------------------- |
| Date | Used as-is. An invalid Date throws. |
| string | Parsed by the native Date parser. Prefer ISO-8601 ("2025-09-15T18:30:00Z"). |
| number | Unix timestamp in seconds or milliseconds, auto-detected (see below). |
Seconds vs. milliseconds
A numeric input is interpreted by magnitude: values with an absolute value below 1e11 are treated as seconds, otherwise milliseconds. This cleanly distinguishes a normal seconds timestamp (1757917800, year 2025) from a milliseconds one (1757917800000, year 2025), and both resolve to the same instant.
The distinction is inherently ambiguous for small numbers, so you can force it:
showDate(1000, "YYYY-MM-DD HH:mm:ss", { unit: "milliseconds" }); // 1970-01-01 00:00:01
showDate(1000, "YYYY-MM-DD HH:mm:ss", { unit: "seconds" }); // 1970-01-01 00:16:400, negative values (pre-1970), and dates around the epoch all work.
Formatting tokens
Build any pattern from these tokens. Values shown for 2025-09-15T06:30:05Z rendered in UTC (a Monday).
| Token | Meaning | Example |
| ------ | -------------------------- | ----------- |
| YYYY | 4-digit year | 2025 |
| YY | 2-digit year | 25 |
| MMMM | Month, full (localized) | September |
| MMM | Month, short (localized) | Sep |
| MM | Month, 2-digit | 09 |
| M | Month | 9 |
| DD | Day of month, 2-digit | 15 |
| D | Day of month | 15 |
| dddd | Weekday, full (localized) | Monday |
| ddd | Weekday, short (localized) | Mon |
| HH | Hour 24h, 2-digit | 06 |
| H | Hour 24h | 6 |
| hh | Hour 12h, 2-digit | 06 |
| h | Hour 12h | 6 |
| mm | Minute, 2-digit | 30 |
| m | Minute | 30 |
| ss | Second, 2-digit | 05 |
| s | Second | 5 |
| A | Meridiem, uppercase | AM |
| a | Meridiem, lowercase | am |
Anything that isn't a token is emitted verbatim (/, -, :, spaces, etc.).
Escaping literal text
Wrap literal text in [...] so letters aren't interpreted as tokens:
showDate(date, "DD MMM YYYY [at] HH:mm"); // "15 Sep 2025 at 06:30"
showDate(date, "[Year] YYYY"); // "Year 2025"Note on
MMMM/MMM/dddd/ddd: these are localized viaoptions.locale.A/aare always the ASCII stringsAM/PM(and lowercase) for predictability. Use thedatetime/timepresets if you want locale-aware meridiems.
Common patterns
showDate(date, "DD/MM/YYYY"); // 15/09/2025
showDate(date, "DD-MM-YYYY"); // 15-09-2025
showDate(date, "DD MMM YYYY"); // 15 Sep 2025
showDate(date, "DD MMMM YYYY"); // 15 September 2025
showDate(date, "MMM DD, YYYY"); // Sep 15, 2025
showDate(date, "YYYY-MM-DD"); // 2025-09-15
showDate(date, "DD MMM YYYY, hh:mm A"); // 15 Sep 2025, 06:30 AM
showDate(date, "YYYY-MM-DD HH:mm:ss"); // 2025-09-15 06:30:05Presets
Pass a preset name instead of a token string:
| Preset | Output (example) |
| ------------ | ---------------------- |
| "short" | 15 Sep 2025 |
| "long" | 15 September 2025 |
| "datetime" | 15 Sep 2025, 6:30 PM |
| "time" | 6:30 PM |
| "relative" | 2 hours ago |
showDate(date, "short");
showDate(date, "datetime");
showDate(date, "relative");Presets expand to token patterns (except relative); you can inspect them via the exported PRESET_PATTERNS.
Relative time
showDate(date, "relative") produces human-friendly output. It's implemented as a preset (not a separate function) so the API stays uniform: one function to learn. The wording comes from Intl.RelativeTimeFormat, so it's locale-aware and correctly pluralized; the sub-minute case is special-cased as "just now".
showDate(t, "relative"); // "just now" | "2 minutes ago" | "yesterday" | ...
showDate(future, "relative"); // "in 3 hours" | "tomorrow" | ...
showDate(t, "relative", { now }); // pass a reference point (great for SSR / tests)| Elapsed | Output |
| ------------ | --------------------------- |
| < 45 s | just now |
| 1-44 min | N minutes ago |
| ~1-21 h | N hours ago |
| ~1 day | yesterday |
| 2-6 days | N days ago |
| ~1-3 weeks | N weeks ago |
| ~1-11 months | N months ago |
| >= ~1 year | last year / N years ago |
A stand-alone
formatRelative(date, now, locale)is also exported if you prefer to call it directly.
Timezone behavior
A Unix timestamp / ISO instant is an absolute point in time. dateface:
- Parses the input.
- Normalizes it to an absolute instant (
Date). - Determines the target timezone (
options.timezoneor the runtime default). - Converts the instant into that timezone via
Intl. - Formats the resulting local date/time.
Timestamps are never interpreted as local time, and DST transitions are handled by the engine's IANA database.
const t = 1757917800; // 2025-09-15T06:30:00Z
showDate(t, "YYYY-MM-DD HH:mm", { timezone: "UTC" }); // 2025-09-15 06:30
showDate(t, "YYYY-MM-DD HH:mm", { timezone: "Asia/Kolkata" }); // 2025-09-15 12:00
showDate(t, "YYYY-MM-DD HH:mm", { timezone: "America/New_York" }); // 2025-09-15 02:30
showDate(t, "YYYY-MM-DD HH:mm", { timezone: "Europe/London" }); // 2025-09-15 07:30
// Near midnight the local date can differ from UTC:
showDate("2025-09-15T02:00:00Z", "YYYY-MM-DD", { timezone: "America/New_York" });
// "2025-09-14"If no timezone is given, the default is:
Intl.DateTimeFormat().resolvedOptions().timeZone- Browser: the end-user's own timezone. You get correct local output with zero config.
- Node.js: the server's timezone (from
TZor the OS). See below.
If the environment can't resolve a zone, it falls back to "UTC". An unsupported timezone value throws a ShowDateError.
Locale
options.locale (a BCP-47 tag) controls localized month/weekday names and relative wording. It defaults to "en-US".
showDate(t, "DD MMMM YYYY", { locale: "en-US" }); // 15 September 2025
showDate(t, "DD MMM YYYY", { locale: "en-GB" }); // 15 Sept 2025
showDate(t, "DD MMMM YYYY", { locale: "fr-FR" }); // 15 septembre 2025
showDate(t, "relative", { locale: "es", now }); // hace 2 horasNo locale is hard-coded into the core; en-US is only the default.
Options
interface ShowDateOptions {
timezone?: string; // IANA name; default = runtime zone
locale?: string; // BCP-47; default = "en-US"
unit?: "auto" | "seconds" | "milliseconds"; // numeric input; default = "auto"
now?: Date | string | number; // reference point for "relative"
}The third argument is always optional; the common case stays showDate(input, format).
Browser usage
import { showDate } from "dateface";
// Automatically uses the visitor's local timezone.
document.querySelector("#published")!.textContent =
showDate(post.publishedAt, "DD MMM YYYY, hh:mm A");Node.js usage
A server cannot know a remote browser user's timezone. The runtime default is the server's timezone, which is rarely what you want when rendering for users. Pass the timezone explicitly (e.g. from the user's profile or an HTTP hint):
import { showDate } from "dateface";
function renderForUser(instant: number, userTimeZone: string) {
return showDate(instant, "DD MMM YYYY, HH:mm", { timezone: userTimeZone });
}
renderForUser(1757917800, "America/New_York"); // "15 Sep 2025, 02:30"For deterministic server-side relative rendering, pass now explicitly.
Error handling
dateface never silently returns "Invalid Date". Invalid input throws a typed ShowDateError:
import { showDate, ShowDateError } from "dateface";
try {
showDate("invalid-date", "DD MMM YYYY");
} catch (err) {
if (err instanceof ShowDateError) {
console.error(err.message); // Unable to parse date string: "invalid-date". ...
}
}Throwing cases: unparseable strings, invalid Date objects, non-finite / out-of-range numbers, unsupported input types, and unknown timezones.
API reference
showDate(input, format, options?) => string
The main entry point. Also available as the default export.
input: Date | string | numberformat: "short" | "long" | "datetime" | "time" | "relative" | stringoptions?: ShowDateOptions
formatRelative(date: Date, now: Date, locale: string) => string
Low-level relative formatter used by the "relative" preset.
getDefaultTimeZone() => string
The runtime's resolved IANA timezone, or "UTC" as a fallback.
isValidTimeZone(tz: string) => boolean
Whether the runtime's Intl supports the given zone.
PRESET_PATTERNS
Record mapping preset names (except relative) to their token patterns.
ShowDateError
Error subclass thrown for all invalid input.
Types
DateInput, DateFormat, FormatPreset, TimestampUnit, ShowDateOptions are all exported.
Why this package exists
Most date formatting either drags in a large dependency (Moment, and even smaller libs add weight) or forces you to wrestle with Intl.DateTimeFormat's option objects every time. The everyday need is simply: "show me this instant in the user's timezone, in this shape."
dateface is exactly that: a single, memorable function built entirely on native Intl, so it stays tiny, correct across timezones and DST, and dependency-free.
