npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

react-time-formatter

v1.0.2

Published

React-first date & time formatting library with a clear API and built-in timezone support

Readme

React-first date & time formatting library with a clear API and built-in timezone support

Minzipped size Dependencies Tree-shakable npm version

Features

  • 📅 DateTime — A consistent wrapper around Date with zoned time support
  • ⏱️ Duration — A fixed time span that overflows into larger units automatically
  • 🗓️ Interval — Calendar-aware difference between two dates
  • 🌍 tz — Timezone engine with sensible built-in zones and override support
  • 📦 Fully tree-shakable with both ESM and CJS builds
  • 💙 TypeScript-native with full static type safety
  • 🚫 Zero-dependency

Example

Just write what you mean—the API stays out of your way.
<Duration of={{ minutes: 80 }}>
  {t => <span>{t.HH} hours {t.mm} minutes</span>}
</Duration>

→ 1 hours 20 minutes

Automatically falls back to smaller units when larger ones aren’t requested:
<Interval from='1789-07-14' to='1799-11-09'>
  {t => <span>{t.YY} years {t.MM} months {t.DD} days</span>}
</Interval>

→ 10 years 03 months 26 days

<Interval from='1789-07-14' to='1799-11-09'>
  {t => <span>{t.DD} days</span>}
</Interval>

→ 3770 days

Consistent and non-misleading: shows placeholders instead of correct-looking false values.
<DateTime at={new Date(1e15)}>
  {dt => <span>{dt.hh}:{dt.mm} {dt.A} {dt.MM}/{dt.DD} {dt.YYYY}</span>}
</DateTime>

→ ##:## -- ##/## ####

Setup

Installation

npm install react-time-formatter

ESM import

import { Interval } from 'react-time-formatter'

CJS import

const { Interval } = require('react-time-formatter/Interval');

Guideline

There's only one guideline.

Avoid early or unnecessary dereference

❌ Incorrect: Although this may seem to work fine, HH, mm and ss occur before any logic happens which may lead to incorrect results in some cases.

<Interval from={DateA} to={DateB}>
  {({ HH, mm, ss }) => <span>{HH}:{mm}:{ss}</span>}
</Interval>

✅ Correct: Safe. Each unit is referenced only when it's needed.

<Interval from={DateA} to={DateB}>
  {t => <span>{t.HH}:{t.mm}:{t.ss}</span>}
</Interval>

❌ Incorrect: Whenever unit is referenced (e.g. t.hours) it's assumed to be used.

<Interval from={DateA} to={DateB}>
  {t => {
    if (t.hours < 2) return <span>{t.mm}:{t.ss}</span>
    return (<span>{t.HH}:{t.mm}:{t.ss}</span>)
  }}
</Interval>

For 80 minutes will output → 20:00 (since hours are referenced at t.hours will assume "01:20:00" is being rendered)

✅ Correct: Use total* units for conditions when applicable:

<Interval from={DateA} to={DateB}>
  {t => {
    if (t.totalHours < 2) return <span>{t.mm}:{t.ss}</span>
    return (<span>{t.HH}:{t.mm}:{t.ss}</span>)
  }}
</Interval>

For 80 minutes will correctly render → 80:00

✅ Also correct: Use units that are gonna be used under either condition:

<Interval from={DateA} to={DateB}>
  {t => {
    if (t.minutes < 2) return <span>{t.mm}:{t.ss}</span>
    return (<span>{t.HH}:{t.mm}:{t.ss}</span>)
  }}
</Interval>

For 80 minutes will correctly render → 80:00

API Reference

DateTime

DateTime is a specific point on the timeline.

Props

  • at: number | string | Date | CalendarDate — The date to be displayed.
  • timezone?: 'UTC' | 'Local' | number | Timezone — The timezone in which the date will be displayed.
  • children?: Render — Render function
  • render?: Render — Render function (alias for children)

Output

Passed to render function and abbreviated as dt in this doc.

<DateTime at={new Date('2025-02-05T15:08:09.998')}>
  {dt => <span>{dt.hh}:{dt.mm} {dt.A} {dt.MM}/{dt.DD} {dt.YYYY}</span>}
</DateTime>

→ 03:08 PM 02/05 2025

Pre-formatted keys

Recommended for insertion, since they're already of string type and support readable error-state in form of # or -- symbols for cases when correct date can't be displayed.

| Token | Type | Description | Example | Invalid | | --------- | -------- | ---------------------------- | ---------------- | --------- | | dt.YY | string | 2-digit year | "25" | "##" | | dt.YYYY | string | 4-digit year | "2025" | "####" | | dt.M | string | Month (1–12) | "2" (February) | "#" | | dt.MM | string | 2-digit month | "02" | "##" | | dt.D | string | Day of the month (1–31) | "5" | "#" | | dt.DD | string | 2-digit day of the month | "05" | "##" | | dt.d | string | Day of week (1 = Monday) | "1" | "#" | | dt.H | string | Hour (24h) | "15" | "##" | | dt.HH | string | 2-digit hour (24h) | "15" | "##" | | dt.h | string | Hour (12h) | "3" | "#" | | dt.hh | string | 2-digit hour (12h) | "03" | "##" | | dt.m | string | Minute | "8" | "#" | | dt.mm | string | 2-digit minute | "08" | "##" | | dt.s | string | Second | "9" | "#" | | dt.ss | string | 2-digit second | "09" | "##" | | dt.SSS | string | Milliseconds (3-digit) | "998" | "###" | | dt.A | string | Meridiem (uppercase) | "AM" / "PM" | "--" | | dt.a | string | Meridiem (lowercase) | "am" / "pm" | "--" | | dt.Z | string | UTC offset (+hh:mm or Z) | "+10:00" | "##:##" | | dt.ZZ | string | UTC offset (+hhmm) | "+1000" | "####" |

Raw keys

Are always of number type. Recommended for conditional logic and debugging, will turn into NaN when the input is incorrect.

| Token | Type | Example | Note | | ------------------- | -------- | -------------- | -------------------------------------------- | | dt.year | number | 2025 | | | dt.month | number | 2 (february) | 1-indexed | | dt.date | number | 5 | | | dt.day | number | 1 | Day of the week (1 = Monday) | | dt.hours | number | 15 | | | dt.minutes | number | 8 | | | dt.seconds | number | 9 | | | dt.milliseconds | number | 998 | | | dt.timezoneOffset | number | -600 | In minutes: for example -180 means UTC+3 |

Duration

Duration is a fixed quantity of time units, independent of specific dates or positions on a timeline.

❗ For Duration years are approximated to 365 days and months are approximated to 30 days. Use Interval if these units need to be precisely reflected.

Props

  • of: number RelativeTime — The quantity of time in milliseconds or arbitrary units
  • children?: Render — Render function
  • render?: Render — Render function (alias for children)

Output

Passed to render function and abbreviated as t in this doc.

<Duration of={4800000}>
  {t => <span>{t.HH} hours {t.mm} minutes</span>}
</Duration>

→ 01 hours 20 minutes

Pre-formatted keys

Recommended for insertion, since they're already of string type and support readable error-state in form of # symbols for cases when correct duration can't be displayed.

| Unit | Type | Description | Example | Invalid | | ------- | -------- | ----------------------- | ------- | ------- | | t.Y | string | Years (1–2 digits) | "5" | "#" | | t.YY | string | 2-digit years | "05" | "##" | | t.M | string | Months (1–2 digits) | "2" | "#" | | t.MM | string | 2-digit months | "02" | "##" | | t.W | string | Weeks (1–2 digits) | "2" | "#" | | t.WW | string | 2-digit weeks | "02" | "##" | | t.D | string | Days (1–2 digits) | "4" | "#" | | t.DD | string | 2-digit days | "04" | "##" | | t.H | string | Hours (1–2 digits) | "15" | "##" | | t.HH | string | 2-digit hours | "15" | "##" | | t.m | string | Minutes (1–2 digits) | "8" | "#" | | t.mm | string | 2-digit minutes | "08" | "##" | | t.s | string | Seconds (1–2 digits) | "9" | "#" | | t.ss | string | 2-digit seconds | "09" | "##" | | t.SSS | string | Milliseconds (3 digits) | "998" | "###" |

Raw unit values

Are always of number type and either positive or 0, never negative. Recommended for conditional logic and debugging, will turn into NaN when the input is incorrect.

| Unit | Type | Example | Note | | ---------------- | -------- | ------- | ------------------------------------------------------- | | t.years | number | 5 | For Duration years are approximated to 365 days | | t.months | number | 2 | For Duration months are approximated to 30 days | | t.weeks | number | 2 | Is defined as chunk of 7 days | | t.days | number | 4 | | | t.hours | number | 15 | | | t.minutes | number | 8 | | | t.seconds | number | 9 | | | t.milliseconds | number | 998 | |

Total values

Independent from the rest of keys and their insertion doesn't affect formatting rules. Each key represents the whole duration converted to a single unit. Are always of number type and can be both positive or negative or 0. Will also turn into NaN when the input is incorrect.

| Key | Type | Example | Note | | --------------------- | -------- | -------------- | -------------------------------------------------------- | | t.totalYears | number | 5 | For Duration years are approximated to 365 days | | t.totalMonths | number | 62 | For Duration months are approximated to 365 days | | t.totalWeeks | number | 271 | | | t.totalDays | number | 1903 | | | t.totalHours | number | 45687 | | | t.totalMinutes | number | 2741228 | | | t.totalSeconds | number | 164473689 | | | t.totalMilliseconds | number | 164473689998 | |

Interval

Interval is a calendar-aware span between two dates. Unlike Duration Interval accounts for the variable length of months and years.

Props

  • from: number | string | Date | CalendarDate — Starting point in time
  • to: number | string | Date | CalendarDate — Ending point in time (non-inclusive)
  • timezone?: 'UTC' | 'Local' | number | Timezone — The timezone in which the difference will be calculated
  • children?: Render — Render function
  • render?: Render — Render function (alias for children)

Output

Passed to render function and abbreviated as t in this doc.

<Interval from={new Date('1789-07-14')} to={new Date('1799-11-09')}>
  {t => <span>{t.YY} years {t.MM} months {t.DD} days</span>}
</Interval>

→ 10 years 03 months 26 days

Pre-formatted keys

Recommended for insertion, since they're already of string type and support readable error-state in form of # symbols for cases when correct duration can't be displayed.

| Unit | Type | Description | Example | Invalid | | ------- | -------- | ----------------------- | ------- | ------- | | t.Y | string | Years (1–2 digits) | "5" | "#" | | t.YY | string | 2-digit years | "05" | "##" | | t.M | string | Months (1–2 digits) | "2" | "#" | | t.MM | string | 2-digit months | "02" | "##" | | t.W | string | Weeks (1–2 digits) | "2" | "#" | | t.WW | string | 2-digit weeks | "02" | "##" | | t.D | string | Days (1–2 digits) | "4" | "#" | | t.DD | string | 2-digit days | "04" | "##" | | t.H | string | Hours (1–2 digits) | "15" | "##" | | t.HH | string | 2-digit hours | "15" | "##" | | t.m | string | Minutes (1–2 digits) | "8" | "#" | | t.mm | string | 2-digit minutes | "08" | "##" | | t.s | string | Seconds (1–2 digits) | "9" | "#" | | t.ss | string | 2-digit seconds | "09" | "##" | | t.SSS | string | Milliseconds (3 digits) | "998" | "###" |

Raw unit values

Are always of number type and either positive or 0, never negative. Recommended for conditional logic and debugging, will turn into NaN when the input is incorrect.

| Unit | Type | Example | | ---------------- | -------- | ------- | | t.years | number | 5 | | t.months | number | 2 | | t.weeks | number | 2 | | t.days | number | 4 | | t.hours | number | 15 | | t.minutes | number | 8 | | t.seconds | number | 9 | | t.milliseconds | number | 998 |

Total values

Independent from the rest of keys and their insertion doesn't affect formatting rules. Each key represents the whole duration converted to a single unit. Are always of number type and can be both positive or negative or 0. Will also turn into NaN when the input is incorrect.

| Key | Type | Example | | --------------------- | -------- | -------------- | | t.totalYears | number | 5 | | t.totalMonths | number | 62 | | t.totalWeeks | number | 271 | | t.totalDays | number | 1903 | | t.totalHours | number | 45687 | | t.totalMinutes | number | 2741228 | | t.totalSeconds | number | 164473689 | | t.totalMilliseconds | number | 164473689998 |

Timezone

Timezones are applicable to DateTime or Interval components. The only correct way to apply timezone is via timezone prop. Having timezone in the date-string itself DOES NOT affect how the date will be displayed. Without timezone prop the timezone defaults to local.

❌ Incorrect: Setting timezone right inside the date string. It will only affect how the date will be interpreted.

<DateTime at='2025-02-05T12:00+01:00'>
  {dt => <span>It's {dt.HH}:{dt.mm} in Belgrade!</span>}
</DateTime>

✅ Correct: Applying timezone via timezone prop:

import { DateTime } from 'react-time-formatter';
import Belgrade from 'react-time-formatter/tz/Europe/Belgrade'

You can still put timezone offset inside the date string, just keep in mind it will only affect parsing process and not formatting.

<DateTime at='2025-02-05T12:00+01:00' timezone={Belgrade}>
  {dt => <span>It's {dt.HH}:{dt.mm} in Belgrade!</span>}
</DateTime>

→ It's 12:00 in Belgrade!

❗ Pre-made and automatic timezones are dependent on Int.DateTimeFormat API which is widely available across browsers. But if you need even more control, refer to custom timezones section.

Pre-made timezones

The most basic pre-made timezones can be enabled by just strings, these timezones include: "Local" (also is default) and "UTC":

<DateTime at={Date.now()} timezone="UTC">
  {dt => <span>It's {dt.HH}:{dt.mm} UTC!</span>}
</DateTime>

→ It's 11:00 UTC!

To statically apply a pre-made timezone you can import it from its tz/ path:

Each path corresponds to the IANA name:

import { DateTime } from 'react-time-formatter';
import Belgrade from 'react-time-formatter/tz/Europe/Belgrade'

Just pass it to timezone prop:

<DateTime at={Date.now()} timezone={Belgrade}>
  {dt => <span>It's {dt.HH}:{dt.mm} in Belgrade!</span>}
</DateTime>

→ It's 12:00 in Belgrade!

Automatic timezones

If you want to apply the timezone dynamically or the desired timezone is absent from the pre-made timezones list, you can use createTimezone helper that will create a timezone for you.

To create an automatic timezone, just pass its IANA name:
import { DateTime } from 'react-time-formatter';
import { createTimezone } from 'react-time-formatter/tz/createTimezone'

const Tokyo = createTimezone('Asia/Tokyo');

Similarly, just pass it to timezone prop:

<DateTime at={Date.now()} timezone={Tokyo}>
  {dt => <span>It's {dt.HH}:{dt.mm} in Tokyo!</span>}
</DateTime>

→ It's 10:00 in Tokyo!

Invalid timezones will never produce a valid-looking result:
import { DateTime } from 'react-time-formatter';
import { createTimezone } from 'react-time-formatter/tz/createTimezone'

const What = createTimezone('Invalid/Unknown');
<DateTime at={Date.now()} timezone={What}>
  {dt => <span>It's always {dt.HH}:{dt.mm} in the invalid timezone!</span>}
</DateTime>

→ It's always ##:## in the invalid timezone!

Custom Timezones

Custom timezones are defined by a TimezoneOffsetResolver function (date: Date) => number which takes single Date argument and returns timezone offset in minutes. Static timezones (with no DST) may also be just defined as number.

The simplest custom timezone:

Just a static UTC+3 offset:

const UTC3 = () => -180;

Also valid:

const UTC3 = -180;

Note that -180 translates into UTC+3. Timezone offset is consistent with JS Date.prototype.getTimezoneOffset() function that defines timezone offset as how many minutes should be added to local timezone to get UTC.

<DateTime at={Date.now()} timezone={UTC3}>
  {dt => <span>UTC{t.ZZ} time is {dt.HH}:{dt.mm}</span>}
</DateTime>

→ UTC+0300 time is 14:00

The custom implementation of the local timezone:

Local timezone is applied by default so you don't normally have to implement it. But if you had to, it would look like this:

const Local = (date: Date) => date.getTimezoneOffset();
The custom implementation of the Berlin timezone with its DST rules:

In case you can't rely on browser's timezone implementation and need your timezone to always have the same behaviour, you can define your all DST rules explicitly:

// The manual equivalent of createTimezone('Europe/Berlin') call:
const Berlin = (date: Date): number => {
  const year = date.getUTCFullYear();

  // Find last Sunday in March
  const startDST = new Date(Date.UTC(year, 2, 31, 1)); // March 31, 01:00 UTC
  while (startDST.getUTCDay() !== 0) {
    startDST.setUTCDate(startDST.getUTCDate() - 1);
  }

  // Find last Sunday in October
  const endDST = new Date(Date.UTC(year, 9, 31, 1)); // October 31, 01:00 UTC
  while (endDST.getUTCDay() !== 0) {
    endDST.setUTCDate(endDST.getUTCDate() - 1);
  }

  // If date is within DST range, return UTC+2 = -120
  if (date >= startDST && date < endDST) {
    return -120;
  }

  // Otherwise, return UTC+1 = -60
  return -60;
};
<DateTime at={Date.now()} timezone={Berlin}>
  {dt => <span>It's {dt.HH}:{dt.mm} in Berlin!</span>}
</DateTime>

→ It's 01:00 in Berlin!

Type reference

CalendarDate

Can act as input for: DateTime, Interval Matching type is returned by: DateTime

A flat object representation for date. Must include at least year value. dt is guaranteed to be valid CalendarDate.

// year and month, all the rest values will be set to 0
const valid = {
  year: 2025,
  // 1-indexed, february
  month: 2,
  date: 15,
};

// meaningless without year
const invalid1 = {
  month: 2,
  date: 15
};

// should have at least year value
const invalid2 = {};

const full = {
  year: 2025,
  month: 2,
  date: 15,
  hours: 10,
  minutes: 0,
  seconds: 15,
  milliseconds: 12,

  // UTC+3. Since CalendarDate is just a snapshot, the timezone offset can only be a number.
  // Doesn't affect how the date will be displayed, only how it will be parsed.
  timezoneOffset: -180,
}

RelativeTime

Can act as input for: Duration Matching type is returned by: Duration, Interval

A flat representation of quantity of time. Must include at least one time unit to be valid. t is guaranteed to be valid RelativeTime.

// can't be empty object
const invalid = {}

// can have any units in any quantities
const valid = { minutes: 80 }

// with all units
const full = {
  years: 9,
  months: 10,
  weeks: 3,
  days: 4,
  hours: 5,
  minutes: 7,
  seconds: 15,
  milliseconds: 900
}

License

MIT © 2025 Nikolai Laevskii