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 🙏

© 2025 – Pkg Stats / Ryan Hefner

useful-time

v0.0.5

Published

An opinionated JavaScript library for formatting datetime in an easy to process format.

Downloads

347

Readme

Useful Time

A JavaScript library for formatting dates and times into a human-readable format.

The emphasis is on simplicity and ease of use. Humans think about datetime in several ways:

  • An absolute value which can be compared
  • A relative value from a given point in time (like now)
  • seasonal value (Month names/numbers seem to be apt for this)
  • day of week (scheduling events)

Formatters tend to give you just one of those.

useful-time attempts to provide all of those in a single format.

Needs

Parsing a full datetime format in your mind is a bit slow. Computing a time difference is slower still. if you want to schedule events you'd need to look at time from different perspectives.

When using libraries, such as moments.js in a calendar mode (think gMail inbox mail dates), give a relative time format for short time differences, and absolute format for long time differences. This is a bad user experience for humans in my opinion, since the two formats are not compatible and require reinterpretation of the data for a comparison, as well as knowing the current time to compute the relative time.

It's easier to schedule events with the absolute format then to go to all that bother.

Or we can choose not to compromise and use a format that can be read as both absolute and relative. It's possible. We can have all that and a ~~bag of chips~~ day of week.

Format

The library formats dates to follow this pattern: {date}({day of week}) {time}({time until datetime})

The date is formatted using ISO 8601, except for the compact format which uses the obsolete mm-dd format. The day of week, time and time until datetime are formatted using the locale's time format.

Localized Numbers: All numbers (date, time, and duration) are displayed using the locale's native numeral system. For example:

  • Arabic locales use Eastern Arabic numerals (٠-٩)
  • Persian locales use Persian numerals (۰-۹)
  • Bengali locales use Bengali numerals (০-৯)
  • Hindi locales use Devanagari numerals (०-९)

If the locale time is lexically sortable (e.g. using 24h format), the format will also be lexically sortable. Note that default en-US uses 12h format (AM/PM) which is not lexically sortable.

There are 4 formats (for now):

  • compact: {truncated short date format}({abbrev day of week}) {time}({abbrev time until datetime})
  • short: {short date format with year}({short day of week}) {time}({short time until datetime})
  • long: {long date format }({full day of week}) {time}({short time until datetime expressed in natural language})
  • longer: {long date format}({full day of week}) {time}({natural language time until datetime})

For the duration part, you can pick the threshold to switch to the next unit class (e.g., in once, 60 seconds -> minutes; in 1.5, 90 seconds -> minutes; in twice, 120 seconds -> minutes). for example: | duration | 1 | 1.5 | 2 | | :--- | :--- | :--- | :--- | | 23h | 23h | 23h | 23h | | 24h | 1d | 24h | 24h | | 25h | 1d | 25h | 25h | | 35h | 1d | 35h | 35h | | 36h | 1d | 1d | 36h | | 37h | 1d | 1d | 37h | | 47h | 1d | 1d | 47h | | 48h | 2d | 2d | 2d | | 49h | 2d | 2d | 2d |

Usage

// Option 1: Named imports (traditional)
import { format } from 'useful-time';
console.log(format({ to: new Date() }).text);

// Option 2: Default import
import useful_time from 'useful-time';
console.log(useful_time.format({ to: new Date() }).text);

// Option 3: Use the 'ut' alias (recommended for conciseness)
import { ut } from 'useful-time';
console.log(ut.format({ to: new Date() }).text);

Examples

Styles

The library supports 4 styles: compact, short (default), long, and longer.

import { ut } from 'useful-time';

const now = new Date();
const target = new Date(now.getTime() + 2 * 60 * 60 * 1000); // 2 hours from now

// Compact
// Output: 11-27(Wed) 3:45 PM(+2h)
console.log(ut.format({ to: target, style: 'compact' }).text);

// Short (Default)
// Output: 25-11-27(Wed) 3:45 PM(+2h)
console.log(ut.format({ to: target, style: 'short' }).text);

// Long
// Output: 2025-11-27(Wednesday) 3:45 PM(in 2 hours)
console.log(ut.format({ to: target, style: 'long' }).text);

// Longer
// Output: 2025-11-27(Wednesday) 3:45 PM(in 2 hours)
console.log(ut.format({ to: target, style: 'longer' }).text);

Locales

You can specify a locale. If not specified, it defaults to navigator.language, the system language or en-US.

The library automatically adapts to the locale's formatting conventions, including:

  • Numeral systems: Numbers are displayed using locale-specific numerals
  • Time format: 12-hour (AM/PM) or 24-hour format
  • Text direction: Proper handling of RTL languages
  • Weekday abbreviations: Optimized for compactness while maintaining distinctness
const target = new Date();

// English (Western Arabic numerals: 0-9)
// Output: 11-27(Wed) 3:45 PM(+0 hr.)
console.log(ut.format({ to: target, locale: 'en-US' }).text);

// Arabic (Eastern Arabic numerals: ٠-٩)
// Output: ١١-٢٧(الأربعاء) ١٥:٤٥(+٠ س)
console.log(ut.format({ to: target, locale: 'ar-EG' }).text);

// Persian (Persian numerals: ۰-۹)
// Output: ۱۱-۲۷(چهارشنبه) ۱۵:۴۵(+۰ ساعت)
console.log(ut.format({ to: target, locale: 'fa-IR' }).text);

// French
// Output: 11-27(mer.) 15:45(+0 h)
console.log(ut.format({ to: target, locale: 'fr-FR' }).text);

// Hebrew
// Output: 11-27(ד׳) 15:45(+0ש׳)
console.log(ut.format({ to: target, locale: 'he-IL' }).text);

Relative Time

The relative time part adapts to the duration.

const now = new Date();
const past = new Date(now.getTime() - 3 * 24 * 60 * 60 * 1000); // 3 days ago

// Output: ... (-3d)
console.log(ut.format({ to: past, from: now }).text);

API

format(options)

Formats a date to a human-readable format.

Parameters

  • options (required): The options object.
    • to (required): The date to format.
    • from (optional): The date to compare to. Defaults to the current date.
    • style (optional): The style to use. pick from compact, short (default), long, and longer.
    • locale (optional): The locale to use. Defaults to navigator.language, the system language or en-US.
    • threshold (optional): The threshold to use. pick from 1, 1.5, 2, "one", "two", "once", "twice".

Returns

An object with the following properties:

  • text (string): The formatted date string.
  • inFuture (boolean): Whether the date is in the future.
  • inAYearOrMore (boolean): Whether the date is in a year or more.
  • locale (string): The locale used for formatting.
  • timeZone (string): The time zone used for formatting.
  • toString (function): A function that returns the formatted date string.

you can send the object directly to console.log or console.error and it will print the formatted date string.

formatToParts(options)

Formats a date to a human-readable format.

Parameters

  • options (required): The options object.
    • to (required): The date to format.
    • from (optional): The date to compare to. Defaults to the current date.
    • style (optional): The style to use. pick from compact, short (default), long, and longer.
    • locale (optional): The locale to use. Defaults to navigator.language, the system language or en-US.
    • threshold (optional): The threshold to use. pick from 1, 1.5, 2, "one", "two", "once", "twice".

Returns

An array of objects with the following properties:

  • type (string): The type of the part. should be from: "literal" | "date" | "day of week" | "time" | "relative".
  • value (string): The value of the part.

issues

  • ~~the numbers for date and time are not localized.~~ RESOLVED: Date and time numbers now use locale-specific numeral systems (e.g., Eastern Arabic numerals for Arabic, Persian numerals for Farsi, etc.).
  • some locales use AM/PM which is not lexically sortable.
  • ~~some locales need override for the relative time format in compact mode. I've filled the ones that didn't exist, but there are probably more existing that need shorter text for compact mode.~~ I've added a strategy for using abbriviated weekdays from small style when compact style fails to deliver the goods (using an abbreviation where two days have the same abbreviation, like T for Tuesday and Thursday in en-US, or not actually using an abbreviation). You can add an override to fix your locale by submitting a pull request.
  • the tests arn't great, and don't cover a representative set of most used locales.
  • maybe parts should be further subdivided.
  • didn't take the time to check how much AI slop I've introduced, though the implementation looks solid at a glancel