A timezone-aware formatting and display library that makes it effortless to show timestamps in the user's local timezone.
"Tickets go on sale at 11 PM PST." "Cool... what time is that for me?"
Users shouldn't have to do timezone math. localized-time automatically converts timestamps to the viewer's local timezone and shows the original for reference.
- Automatic timezone conversion - Powered by the Temporal API
- Relative time - Display "in 5 hours" or "3 days ago"
- Web Component - Works with any framework or vanilla JS
- React Component - First-class React support
- Accessible - ARIA attributes, keyboard navigation
- Customizable - CSS variables, format presets, render props
- Lightweight - ~30KB gzipped per entry point (includes Temporal polyfill)
Available on npm:
npm install localized-time
# or
bun add localized-time
# or
yarn add localized-time<script type="module">
import "localized-time/web";
</script>
<p>
Tickets go on sale at
<localized-time from="2025-05-18T23:00:00-08:00"></localized-time>
</p>import { LocalizedTime } from "localized-time/react";
function TicketSale() {
return (
<p>
Tickets go on sale at{" "}
<LocalizedTime from="2025-05-18T23:00:00-08:00" />
</p>
);
}Click any timestamp to see a popup with:
- Your local time (converted)
- Original time (source timezone)
- Time difference (+3h, -9h, etc.)
The from attribute/prop accepts ISO 8601 strings with timezone:
2025-05-18T23:00:00-08:00 (offset)
2025-05-18T23:00:00Z (UTC)
2025-05-18T23:00:00+09:00 (positive offset)
| Format | Example Output |
|---|---|
short |
11:00 PM |
medium (default) |
May 18, 11:00 PM |
long |
Sun, May 18, 2025, 11:00 PM |
relative |
in 5 hours |
<localized-time from="..." format="short"></localized-time>
<localized-time from="..." format="long"></localized-time>
<localized-time from="..." format="relative"></localized-time>The relative format displays human-friendly relative times like "in 5 hours", "3 days ago", or "tomorrow". It automatically chooses the most appropriate unit (seconds, minutes, hours, days, months, or years).
<!-- Shows "in 5 hours" if the event is 5 hours away -->
<p>Event starts <localized-time from="2025-05-18T23:00:00Z" format="relative"></localized-time></p>// React
<p>Event starts <LocalizedTime from="2025-05-18T23:00:00Z" format="relative" /></p>Clicking a relative time still shows the popup with exact times and timezone details.
Force display in a specific timezone:
<localized-time from="..." viewer-tz="Europe/London"></localized-time><LocalizedTime from="..." viewerTz="Europe/London" />| Attribute | Type | Description |
|---|---|---|
from |
string | Required. ISO 8601 timestamp with timezone |
viewer-tz |
string | Override viewer's timezone (IANA or offset) |
format |
string | "short", "medium", "long", or "relative" |
| Prop | Type | Description |
|---|---|---|
from |
string | Required. ISO 8601 timestamp with timezone |
viewerTz |
string | Override viewer's timezone |
format |
FormatOption | "short", "medium", "long", "relative", or custom Intl options |
className |
string | CSS class for the trigger element |
style |
CSSProperties | Inline styles |
renderPopup |
function | Custom popup renderer (data: PopupData) => ReactNode |
popupClassName |
string | CSS class for the popup |
For advanced usage, import from localized-time/core:
import {
convertTimestamp,
formatTime,
formatRelativeTime,
createPopupData,
// Adapter system
createEngine,
temporalAdapter,
type TimeAdapter,
} from "localized-time/core";
// Convert a timestamp
const result = convertTimestamp("2025-05-18T23:00:00-08:00", "America/New_York");
console.log(result.localTime); // ZonedDateTime
console.log(result.diffHours); // 3
// Format for display
const formatted = formatTime(result.localTime, "medium"); // "May 19, 2:00 AM"
const relative = formatRelativeTime(result.localTime); // "in 5 hours"localized-time {
--lt-underline-color: #6366f1;
--lt-focus-color: #6366f1;
--lt-popup-bg: #ffffff;
--lt-popup-color: #1a1a1a;
--lt-popup-border-color: #e0e0e0;
--lt-popup-radius: 8px;
--lt-popup-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
--lt-popup-font: system-ui, sans-serif;
--lt-popup-font-size: 14px;
--lt-popup-label-color: #666666;
--lt-popup-divider-color: #e0e0e0;
}<LocalizedTime
from="2025-05-18T23:00:00-08:00"
renderPopup={(data) => (
<div className="my-popup">
<strong>{data.localTimeFormatted}</strong>
<span>Originally: {data.originalTimeFormatted}</span>
</div>
)}
/>Support for other time libraries (Luxon, Day.js) via adapters:
import { createEngine, type TimeAdapter } from "localized-time/core";
import { DateTime } from "luxon";
const luxonAdapter: TimeAdapter<DateTime> = {
parse: (iso) => DateTime.fromISO(iso, { setZone: true }),
toZone: (dt, tz) => dt.setZone(tz),
getViewerTimezone: () => DateTime.local().zoneName ?? "UTC",
getTimezone: (dt) => dt.zoneName ?? "UTC",
getOffsetHours: (dt) => dt.offset / 60,
toEpochMillis: (dt) => dt.toMillis(),
};
const engine = createEngine(luxonAdapter);Modern browsers with ES2022 support. The Temporal API is polyfilled automatically.
# Install dependencies
bun install
# Run tests
bun test
# Build library
bun run build
# Build demo
bun run build:demo
# Start dev server
bun run dev