@master4n/temporal-transformer

Convert any epoch timestamp to a human-readable date in TypeScript — auto-detects seconds, milliseconds, microseconds, and nanoseconds with zero configuration. Also converts date strings to epoch, computes calendar-accurate durations, and handles any IANA timezone.
Why temporal-transformer?
Working with epoch timestamps in TypeScript is surprisingly painful:
- You don't know if a value is in seconds, milliseconds, microseconds, or nanoseconds until it breaks in production.
new Date(epoch) silently produces wrong dates if the unit is wrong.
- Parsing date strings without a format string is unreliable and locale-dependent.
- Getting the UTC offset for a given timezone requires knowing a timezone library's specific API.
temporal-transformer solves all of these in one small, fully-typed package:
convertEpoch(1622547800);
convertEpoch(1622547800000);
convertEpoch(1622547800000000);
convertEpoch(1622547800000000000);
Features
- Auto-detects epoch unit — seconds, milliseconds, microseconds, nanoseconds
- Epoch → human-readable date in local timezone and GMT simultaneously
- Date string → epoch with explicit format and timezone (strict parsing, no surprises)
- Calendar-accurate duration between two timestamps (years, months, days, hours, minutes, seconds)
- Timezone conversion — format any epoch in any IANA timezone
- Timezone utilities — list all 600+ IANA timezones, get UTC offset for any zone
- Safe predicates —
isValidEpoch / isValidTimezone that never throw
- Relative time — "3 hours 15 minutes ago" / "2 days from now"
- Full TypeScript support — strict types, enums, and interfaces exported
- Dual ESM + CJS build — works in Next.js, Node.js, Vite, webpack
- Secure input validation — guards against
null, undefined, Infinity, NaN, empty strings, and non-numeric input
Installation
npm install @master4n/temporal-transformer
yarn add @master4n/temporal-transformer
pnpm add @master4n/temporal-transformer
Quick Start
import {
convertEpoch,
convertDateToEpoch,
convertEpochToTimezone,
parseToEpoch,
getDurationBetween,
isValidEpoch,
} from '@master4n/temporal-transformer';
const result = convertEpoch(1622547800000);
console.log(result.dateTime);
console.log(result.dateTimeInGMT);
console.log(result.epochUnit);
console.log(result.relative);
const epoch = convertDateToEpoch(new Date(), 'America/New_York');
console.log(epoch.epochInSeconds);
console.log(epoch.epochInMilliseconds);
const parsed = parseToEpoch('2024-12-25T00:00:00', undefined, 'Asia/Kolkata');
console.log(parsed.epochInMilliseconds);
const formatted = convertEpochToTimezone(1622547800000, 'Asia/Tokyo', 'YYYY-MM-DD HH:mm');
console.log(formatted);
const duration = getDurationBetween(1609459200000, 1622547800000);
console.log(duration.humanReadable);
console.log(isValidEpoch(1622547800000));
console.log(isValidEpoch('bad value'));
console.log(isValidEpoch(Infinity));
API Reference
convertEpoch(epoch, format?)
Converts any epoch value to a human-readable date. Automatically detects the epoch unit (seconds / milliseconds / microseconds / nanoseconds).
function convertEpoch(epoch: number, format?: string): EpochToDate
Returns: EpochToDate
const result = convertEpoch(1622547800000, 'YYYY-MM-DD');
convertDateToEpoch(date, timezone?)
Converts a Date object to epoch values (seconds and milliseconds) in the specified timezone.
function convertDateToEpoch(date: Date, timezone?: string): DateToEpoch
date | Date | — | Any valid JavaScript Date |
timezone | string | System local timezone | IANA timezone name |
Returns: DateToEpoch
const result = convertDateToEpoch(new Date('2024-01-15'), 'America/New_York');
convertEpochToTimezone(epoch, timezone, format?)
Formats an epoch value as a date string in a specific IANA timezone.
function convertEpochToTimezone(epoch: number, timezone: string, format?: string): string
convertEpochToTimezone(1622547800000, 'America/Los_Angeles', 'YYYY-MM-DD HH:mm:ss');
convertEpochToTimezone(1622547800000, 'Asia/Tokyo');
parseToEpoch(input, format?, timezone?)
Parses a date string into epoch values. Uses strict parsing when a format is provided — never silently produces a wrong date.
function parseToEpoch(input: string, format?: string, timezone?: string): DateToEpoch
input | string | — | Date string to parse |
format | string or undefined | Auto (ISO 8601) | Moment.js format string |
timezone | string | System local timezone | IANA timezone to interpret the date in |
Returns: DateToEpoch
Throws: EpochValidationError with EpochError.ParseError if the string cannot be parsed, or EpochError.TimezoneError for an unknown timezone.
parseToEpoch('2024-12-25T00:00:00Z');
parseToEpoch('25/12/2024', 'DD/MM/YYYY', 'Europe/London');
parseToEpoch('2024-12-25 09:00:00', 'YYYY-MM-DD HH:mm:ss', 'Asia/Kolkata');
getDurationBetween(fromEpoch, toEpoch)
Computes a calendar-accurate duration between two epoch timestamps.
function getDurationBetween(fromEpoch: number, toEpoch: number): ParsedDuration
Returns: ParsedDuration
Throws: EpochValidationError with EpochError.RangeError if fromEpoch > toEpoch.
const d = getDurationBetween(1609459200000, 1748000000000);
console.log(d.years);
console.log(d.months);
console.log(d.days);
console.log(d.humanReadable);
console.log(d.totalMilliseconds);
Calendar-accurate: months and years account for actual calendar boundaries (Feb 28/29, 30/31-day months), not fixed 30.44-day approximations.
getTimezoneOffset(timezone, epoch?)
Returns the UTC offset for an IANA timezone, optionally at a specific point in time (useful for DST-aware offsets).
function getTimezoneOffset(timezone: string, epoch?: number): TimezoneOffset
getTimezoneOffset('Asia/Kolkata');
getTimezoneOffset('America/New_York');
getTimezoneOffset('America/New_York', 1622547800000);
getTimezoneList()
Returns the full list of supported IANA timezone names (600+).
function getTimezoneList(): string[]
const zones = getTimezoneList();
zones.includes('Asia/Kolkata');
zones.includes('America/Chicago');
getEpochNow()
Returns the current moment as epoch values (seconds, milliseconds, ISO string, and detected timezone).
function getEpochNow(): EpochNow
const now = getEpochNow();
formatDuration(milliseconds)
Formats a duration in milliseconds as a human-readable string.
function formatDuration(milliseconds: number): string
formatDuration(3661000);
formatDuration(86400000);
formatDuration(90061000);
formatDuration(500);
isValidEpoch(epoch) · isValidTimezone(timezone)
Safe predicates that return boolean without throwing. Useful for conditional logic and input validation at system boundaries.
function isValidEpoch(epoch: unknown): boolean
function isValidTimezone(timezone: string): boolean
isValidEpoch(1622547800000);
isValidEpoch('1622547800000');
isValidEpoch(null);
isValidEpoch(Infinity);
isValidEpoch(NaN);
isValidTimezone('UTC');
isValidTimezone('Asia/Kolkata');
isValidTimezone('Not/ATimezone');
Types Reference
EpochToDate
interface EpochToDate {
epoch: number;
epochUnit: string;
timezone: string;
dateTime: string;
dateTimeInGMT: string;
relative: string;
}
DateToEpoch
interface DateToEpoch {
epochInSeconds: number;
epochInMilliseconds: number;
timezone: string;
dateTime: string;
dateTimeInGMT: string;
}
ParsedDuration
interface ParsedDuration {
years: number;
months: number;
days: number;
hours: number;
minutes: number;
seconds: number;
milliseconds: number;
totalMilliseconds: number;
humanReadable: string;
}
EpochNow
interface EpochNow {
seconds: number;
milliseconds: number;
iso: string;
timezone: string;
}
TimezoneOffset
interface TimezoneOffset {
offset: string;
offsetMinutes: number;
}
EpochUnit enum
enum EpochUnit {
SECONDS = 'seconds',
MILLI_SECONDS = 'milliseconds',
MICRO_SECONDS = 'microseconds',
NANO_SECONDS = 'nanoseconds',
}
DurationUnit type
type DurationUnit =
| 'years' | 'months' | 'weeks' | 'days'
| 'hours' | 'minutes' | 'seconds' | 'milliseconds';
Epoch Unit Auto-Detection
< 10,000,000,000 | seconds | 1622547800 |
< 10,000,000,000,000 | milliseconds | 1622547800000 |
< 10,000,000,000,000,000 | microseconds | 1622547800000000 |
< 1e19 | nanoseconds | 1622547800000000000 |
Negative epochs (dates before Unix epoch, i.e. before 1970-01-01) are fully supported.
Error Handling
All functions throw EpochValidationError (extends Error) with a message from the EpochError enum.
import { EpochValidationError, EpochError } from '@master4n/temporal-transformer';
try {
convertEpoch(null as any);
} catch (err) {
if (err instanceof EpochValidationError) {
console.log(err.message);
console.log(err.name);
}
}
UndefinedOrNull | Input is null or undefined |
NotANumber | Input is not numeric, is Infinity, or is NaN |
Empty | Input is an empty or whitespace-only string |
EpochUnit | Epoch value is too large to classify into any unit |
DateError | Date object passed to convertDateToEpoch is invalid |
TimezoneError | Timezone string is not a recognized IANA timezone |
ParseError | Date string cannot be parsed (in parseToEpoch) |
RangeError | fromEpoch > toEpoch in getDurationBetween |
Common Recipes
Convert a Unix timestamp from an API response
const ts = apiResponse.created_at;
const { dateTime, epochUnit } = convertEpoch(ts);
Validate a user-provided timestamp before processing
const userInput = req.body.timestamp;
if (!isValidEpoch(userInput)) {
return res.status(400).json({ error: 'Invalid timestamp' });
}
const { dateTime } = convertEpoch(Number(userInput));
Show how long ago something happened
const { relative } = convertEpoch(event.createdAt);
console.log(`Event created ${relative}`);
Parse a date string from a form input
const { epochInMilliseconds } = parseToEpoch('25/12/2024', 'DD/MM/YYYY', 'Europe/London');
Compute how long a job ran
const duration = getDurationBetween(job.startedAt, job.finishedAt);
console.log(`Job ran for ${duration.humanReadable}`);
Changelog
1.2.1
- Security fix:
parseToEpoch now validates input against an allowlist of date-safe characters before passing to moment, preventing HTML/XSS payloads from reaching moment's internal parser and appearing in stderr stack traces
- Security fix:
parseToEpoch without an explicit format now uses strict ISO 8601 parsing (moment.ISO_8601, true) instead of moment's lenient auto-detection, eliminating the js Date() fallback that could behave inconsistently across environments
1.2.0
- New:
convertEpochToTimezone — format epoch in any IANA timezone
- New:
parseToEpoch — parse date strings to epoch with strict, timezone-aware parsing
- New:
getDurationBetween — calendar-accurate duration between two epochs
- New:
getTimezoneOffset — get UTC offset (DST-aware) for any IANA timezone
- New:
getTimezoneList — list all supported IANA timezone names
- New:
getEpochNow — current time as seconds, milliseconds, ISO string, and timezone
- New:
formatDuration — format a duration in ms as a human-readable string
- New:
isValidEpoch / isValidTimezone — safe boolean predicates (never throw)
- New types:
ParsedDuration, EpochNow, TimezoneOffset, DurationUnit, StartEndUnit
- New exports:
EpochUnit enum, EpochError enum, EpochValidationError class
- Fix:
validateEpoch no longer crashes on Infinity, -Infinity, NaN, or whitespace-only strings
- Fix: Empty-string check now trims whitespace before checking length
- Deps: Removed redundant
@types/moment; moved typescript and @types/node to devDependencies; updated moment-timezone to ^0.5.46 and typescript to ^5.8.3
1.1.1 and earlier
Initial release — convertEpoch and convertDateToEpoch.
Contributing
Issues and PRs are welcome at github.com/Master4Novice/common.
Credits
Written by Master4Novice.
License
MIT © Master4Novice