typed-time
v7.0.0
Published
Type-safe, semantic time for JavaScript & TypeScript
Maintainers
Readme
⌛ typed-time
Type-safe, semantic time for JavaScript & TypeScript
Catch timezone, scheduling, and business-time bugs at compile time.
Why typed-time exists
Time bugs are some of the most expensive production bugs:
- Mixing local time and UTC
- Billing or SLAs calculated using server time
- DST breaking scheduled jobs
- Background workers replaying events with the wrong clock
- APIs passing timestamps that lose meaning
JavaScript's Date is mutable, ambiguous, and semantically empty.
typed-time fixes this by putting time meaning into the type system.
Installation
npm install typed-timeCore idea
type Time<Kind extends string> = {
readonly __kind: Kind
readonly epochMs: number
}A timestamp with meaning.
Basic usage
import { nowUTC, addMinutes } from "typed-time"
const now = nowUTC()
const later = addMinutes(now, 30)Time zones (explicit only)
import { nowLocal, localToUTC } from "typed-time"
const local = nowLocal()
const utc = localToUTC(local)No silent conversions. No guessing.
Business time vs System time
import { BusinessUTC, SystemUTC } from "typed-time"
function billCustomer(at: BusinessUTC) {}
billCustomer({} as SystemUTC)
// ❌ Compile-time errorThis prevents billing, SLA, and scheduling bugs.
Serialization & JSON safety
import { serialize, deserialize } from "typed-time"
const payload = serialize(nowUTC())
const time = deserialize<"UTC">(payload)Safe across APIs, queues, DBs, and workers.
Node.js real-world example
import { BusinessUTC, addMinutes } from "typed-time"
export function scheduleOrder(
requestedAt: BusinessUTC
): BusinessUTC {
return addMinutes(requestedAt, 15)
}Impossible to pass Date, local time, or system time by mistake.
Another example
// Time comparison example
import { TypedTime, format, isBefore, isSameDate, isSameTime, isAfter } from "typed-time";
// Current time
const now = TypedTime.nowUTC();
// Business time example
const meeting = TypedTime.from("2026-01-24T15:17:00Z");
// Formatting
console.log(format(now, "hh:mm a dd/MM/yyyy")); // "03:17 pm 24/01/2026"
// Comparison
console.log(isBefore(now, meeting)); // true
console.log(isSameDate(now, meeting)); // true
// Time-only comparison
const lunchTime = TypedTime.from("2026-01-24T12:30:00Z");
console.log(isSameTime(meeting, lunchTime)); // false
// ===========================================================
// Another example
function testSchedule() {
const start = TypedTime.from("2026-01-24T09:00:00Z");
const end = TypedTime.from("2026-01-24T17:00:00Z");
const now = TypedTime.nowUTC();
if (isBefore(now, start)) console.log("Workday not started");
if (isAfter(now, end)) console.log("Workday finished");
console.log("Current time:", format(now, "hh:mm a dd/MM/yyyy"));
}
testSchedule();Migration strategy from Date
Step 1: Stop Date at boundaries
function handler(time: UTC) {}
handler(utc(Date.now()))Step 2: Centralize time creation
export const nowBusiness = () => businessUTC(Date.now())Step 3: Keep formatting libraries
format(new Date(time.epochMs), "yyyy-MM-dd")Step 4: Migrate critical paths first
Billing, scheduling, cron, workers.
Step 5: Let TypeScript guide you
Type errors show where time meaning was unclear.
Performance
- Zero runtime abstractions
- Plain objects
- No parsing
- Cheaper than Date in hot paths
Philosophy
Time is not just a number.
If meaning matters, it belongs in the type system.
License
MIT © Ethern Myth

