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

@ggviana.eth/duration

v0.1.7

Published

Type-safe duration handling in TypeScript. Parse ISO 8601 strings, perform arithmetic, compare durations, and convert between units. Accepts numbers, objects, or strings. Immutable, zero dependencies.

Readme

Duration

A lightweight, fully-typed TypeScript library for working with time durations. Create, manipulate, and format durations with an intuitive, immutable API.

import Duration from '@ggviana.eth/duration'

const timer = Duration.fromMinutes(5).and(30, 'seconds')
console.log(timer.toString())  // PT5M30S
console.log(timer)             // Duration { 5m 30s }

const extended = timer.add(Duration.fromSeconds(45))
console.log(extended.toMinutes())  // 6 minutes

Features

  • 🔷 Fully Typed - Complete TypeScript support with strict mode
  • 🎯 Type-Safe Units - Prevent runtime errors with compile-time unit validation
  • 🔒 Immutable - All operations return new instances
  • 🌐 ISO 8601 - Parse and serialize to standard duration format
  • 📦 Dual Format - Works with ESM and CommonJS
  • 🪶 Zero Dependencies - Lightweight and self-contained
  • 100% Tested - Comprehensive test coverage
  • 🎨 Developer Friendly - Intuitive API with method chaining

Installation

npm install @ggviana.eth/duration

Quick Start

import Duration from '@ggviana.eth/duration'

// Create durations
const d1 = Duration.fromHours(2)
const d2 = Duration.fromMinutes(30)
const d3 = Duration.of(45, 'seconds')

// Combine durations
const total = d1.add(d2).add(d3)
console.log(total.toString())  // PT2H30M45S

// Parse ISO 8601
const parsed = Duration.parse('P1DT6H30M')
console.log(parsed.toHours())  // 30

// Create from object
const duration = new Duration({
  hours: 1,
  minutes: 30,
  seconds: 45
})
console.log(duration.toSeconds())  // 5445

API Documentation

Creating Durations

Constructor

new Duration(milliseconds: number)
new Duration(duration: Duration)
new Duration(object: Partial<DurationLike>)

Create a Duration from milliseconds, another Duration (copy), or a duration-like object with optional time components.

const d1 = new Duration(5000)  // 5 seconds
const d2 = new Duration(d1)    // Copy
const d3 = new Duration({
  hours: 1,
  minutes: 30,
  seconds: 15
})

Static Factory Methods

Duration.fromMilliseconds(ms: number): Duration
Duration.fromSeconds(seconds: number): Duration
Duration.fromMinutes(minutes: number): Duration
Duration.fromHours(hours: number): Duration
Duration.fromDays(days: number): Duration
Duration.fromWeeks(weeks: number): Duration

Create durations from specific time units.

const fiveMinutes = Duration.fromMinutes(5)
const oneDay = Duration.fromDays(1)
const twoWeeks = Duration.fromWeeks(2)

Generic Factory

Duration.of(value: number, unit: TimeUnit): Duration

Create a duration with any supported time unit. Accepts both singular and plural forms.

Duration.of(30, 'second')   // or 'seconds'
Duration.of(5, 'minute')    // or 'minutes'
Duration.of(2, 'hour')      // or 'hours'
Duration.of(1, 'day')       // or 'days'
Duration.of(3, 'week')      // or 'weeks'

Parsing

Duration.parse(isoString: string): Duration
Duration.parseISO8601(isoString: string): Duration

Parse an ISO 8601 duration string.

Duration.parse('PT1H30M')      // 1 hour 30 minutes
Duration.parse('P1DT2H')       // 1 day 2 hours
Duration.parse('PT30.5S')      // 30.5 seconds
Duration.parse('P7DT3H15M30S') // Complex duration

Arithmetic Operations

add()

add(other: DurationInput): Duration

Add another duration, returning a new Duration.

const d1 = Duration.fromMinutes(30)
const d2 = Duration.fromMinutes(15)
const total = d1.add(d2)  // 45 minutes

// Also accepts numbers (milliseconds) or objects
d1.add(30000)  // Add 30 seconds
d1.add({ minutes: 5, seconds: 30 })

and()

and(value: number, unit: TimeUnit): Duration

Fluent API for adding time with a specific unit. Perfect for chaining.

const time = Duration.fromHours(1)
  .and(30, 'minutes')
  .and(45, 'seconds')

console.log(time.toString())  // PT1H30M45S

subtract()

subtract(other: DurationInput): Duration

Subtract another duration. Result bottoms out at zero (no negative durations).

const d1 = Duration.fromMinutes(30)
const d2 = Duration.fromMinutes(10)
const remaining = d1.subtract(d2)  // 20 minutes

// Bottoms out at zero
const d3 = d2.subtract(d1)  // 0 (not negative)

roundTo()

roundTo(unit: TimeUnit): Duration

Round to the nearest time unit.

const d = Duration.fromSeconds(90)
d.roundTo('minute')  // 2 minutes

Duration.fromMinutes(25).roundTo('hour')  // 0 hours
Duration.fromMinutes(35).roundTo('hour')  // 1 hour

Comparison Methods

All comparison methods accept DurationInput (Duration, number, ISO 8601 string, or partial DurationLike object).

equals(other: DurationInput): boolean
isLessThan(other: DurationInput): boolean
isGreaterThan(other: DurationInput): boolean
isLessThanOrEqual(other: DurationInput): boolean
isGreaterThanOrEqual(other: DurationInput): boolean
isZero(): boolean

Examples:

const d1 = Duration.fromMinutes(5)
const d2 = Duration.fromSeconds(300)

d1.equals(d2)              // true
d1.equals('PT5M')          // true (ISO 8601 string)
d1.isLessThan(d2)          // false
d1.isGreaterThan(1000)     // true (1000ms = 1s)
d1.isGreaterThan('PT1S')   // true (string comparison)
d1.isZero()                // false

Duration.fromMilliseconds(0).isZero()  // true

Conversion Methods

toMilliseconds(): number
toSeconds(): number        // Truncates fractional seconds
toMinutes(): number        // Truncates fractional minutes
toHours(): number          // Truncates fractional hours
toDays(): number           // Truncates fractional days
toWeeks(): number          // Truncates fractional weeks

Examples:

const d = Duration.fromMinutes(5).and(30, 'seconds')

d.toMilliseconds()  // 330000
d.toSeconds()       // 330
d.toMinutes()       // 5 (truncated)

toObject()

toObject(): DurationLike

Break down the duration into its component parts.

const d = Duration.parse('P1DT2H30M45.5S')
console.log(d.toObject())
// {
//   weeks: 0,
//   days: 1,
//   hours: 2,
//   minutes: 30,
//   seconds: 45,
//   milliseconds: 500
// }

toJSON()

toJSON(): DurationLike

Returns the same as toObject() for JSON serialization.

const d = Duration.fromMinutes(5)
JSON.stringify(d)
// {"weeks":0,"days":0,"hours":0,"minutes":5,"seconds":0,"milliseconds":0}

Serialization

toString()

toString(): string

Convert to ISO 8601 duration format.

Duration.fromHours(1).toString()          // PT1H
Duration.fromMinutes(30).toString()       // PT30M
Duration.fromDays(1).and(2, 'hours').toString()  // P1DT2H

// Supports decimal seconds
Duration.fromMilliseconds(1500).toString()  // PT1.5S

// Zero duration
new Duration(0).toString()  // PT0S

Custom Inspect

Durations have a custom console.log() format for better debugging.

const d = Duration.fromWeeks(2)
  .and(3, 'days')
  .and(5, 'hours')
  .and(30, 'minutes')

console.log(d)  // Duration { 2w 3d 5h 30m }

Utility Methods

Duration.min()

Duration.min(a: DurationInput, b: DurationInput): Duration

Return the smaller of two durations.

const min = Duration.min(
  Duration.fromMinutes(5),
  Duration.fromMinutes(10)
)
console.log(min.toMinutes())  // 5

Duration.max()

Duration.max(a: DurationInput, b: DurationInput): Duration

Return the larger of two durations.

const max = Duration.max(
  Duration.fromMinutes(5),
  Duration.fromMinutes(10)
)
console.log(max.toMinutes())  // 10

Duration.isDuration()

Duration.isDuration(obj: unknown): obj is Duration | Partial<DurationLike> | string

Type guard to check if a value is a Duration, duration-like object, or valid ISO 8601 duration string.

Duration.isDuration(Duration.fromMinutes(5))  // true
Duration.isDuration({ hours: 1, minutes: 30 })  // true
Duration.isDuration('PT1H30M')  // true
Duration.isDuration('P1D')  // true
Duration.isDuration({ invalid: 'object' })  // false
Duration.isDuration('invalid string')  // false
Duration.isDuration(null)  // false

TypeScript Support

Duration is written in TypeScript and includes full type definitions.

Type Exports

import Duration, {
  DurationLike,
  TimeUnit,
  DurationInput
} from '@ggviana.eth/duration'

Type Definitions

// Supported time units (singular and plural)
type TimeUnit =
  | 'millisecond' | 'milliseconds'
  | 'second' | 'seconds'
  | 'minute' | 'minutes'
  | 'hour' | 'hours'
  | 'day' | 'days'
  | 'week' | 'weeks'

// Duration object with all time components
// Used for toObject()/toJSON() and as Partial<DurationLike> for construction
interface DurationLike {
  weeks: number
  days: number
  hours: number
  minutes: number
  seconds: number
  milliseconds: number
}

// Accepted input types
type DurationInput = number | string | Duration | Partial<DurationLike>

Type Safety Examples

// Type-safe unit parameter
const d1 = Duration.of(5, 'minutes')  // ✓
const d2 = Duration.of(5, 'years')    // ✗ TypeScript error

// Type-safe object construction
const d3: Partial<DurationLike> = {
  hours: 1,
  minutes: 30,
  invalid: 'field'  // ✗ TypeScript error
}

// Type guard usage
function processDuration(input: unknown) {
  if (Duration.isDuration(input)) {
    // input is now typed as Duration | Partial<DurationLike>
    const duration = Duration.toDuration(input)
    console.log(duration.toMinutes())
  }
}

Practical Examples

Timer / Countdown

const countdown = Duration.fromMinutes(5)
let remaining = countdown

const interval = setInterval(() => {
  remaining = remaining.subtract(Duration.fromSeconds(1))

  console.log(remaining)  // Duration { 4m 59s }, { 4m 58s }, ...

  if (remaining.isZero()) {
    console.log('Time is up!')
    clearInterval(interval)
  }
}, 1000)

Calculate Time Difference

const start = Date.now()
// ... some operation ...
const end = Date.now()

const elapsed = new Duration(end - start)
console.log(`Operation took ${elapsed.toSeconds()} seconds`)

Work Session Tracker

const workSession = Duration.fromHours(8)
  .subtract(Duration.fromMinutes(30))  // Lunch break
  .subtract(Duration.fromMinutes(15))  // Coffee break

const breaks = Duration.fromMinutes(45)
const actualWork = workSession.subtract(breaks)

console.log(`Actual work time: ${actualWork.toHours()} hours`)

Video Duration Formatting

function formatVideoDuration(ms: number): string {
  const d = new Duration(ms)
  const parts = d.toObject()

  if (parts.hours > 0) {
    return `${parts.hours}:${pad(parts.minutes)}:${pad(parts.seconds)}`
  }
  return `${parts.minutes}:${pad(parts.seconds)}`
}

function pad(n: number): string {
  return n.toString().padStart(2, '0')
}

console.log(formatVideoDuration(125000))  // "2:05"
console.log(formatVideoDuration(3725000)) // "1:02:05"

SLA / Deadline Calculations

const slaResponse = Duration.fromHours(4)
const slaResolution = Duration.fromDays(1)

const incidentReceived = new Date('2024-01-01T09:00:00Z')
const responseDeadline = new Date(
  incidentReceived.getTime() + slaResponse.toMilliseconds()
)
const resolutionDeadline = new Date(
  incidentReceived.getTime() + slaResolution.toMilliseconds()
)

console.log(`Must respond by: ${responseDeadline}`)
console.log(`Must resolve by: ${resolutionDeadline}`)

Rate Limiting

const rateLimitWindow = Duration.fromMinutes(1)
const maxRequests = 100

class RateLimiter {
  private requests: number[] = []

  canMakeRequest(): boolean {
    const now = Date.now()
    const windowStart = now - rateLimitWindow.toMilliseconds()

    // Remove old requests
    this.requests = this.requests.filter(t => t > windowStart)

    return this.requests.length < maxRequests
  }

  recordRequest(): void {
    this.requests.push(Date.now())
  }
}

Constants

Duration provides constants for all time units:

Duration.Units.Millisecond  // 1
Duration.Units.Second       // 1000
Duration.Units.Minute       // 60000
Duration.Units.Hour         // 3600000
Duration.Units.Day          // 86400000
Duration.Units.Week         // 604800000

Immutability

All Duration operations return new instances. The original is never modified.

const original = Duration.fromMinutes(10)
const doubled = original.add(original)
const halved = original.subtract(Duration.fromMinutes(5))

console.log(original.toMinutes())  // 10 (unchanged)
console.log(doubled.toMinutes())   // 20
console.log(halved.toMinutes())    // 5

Development

Setup

npm install

Available Scripts

  • npm run build - Build ESM/CJS bundles with type declarations
  • npm test - Run test suite
  • npm run test:watch - Run tests in watch mode
  • npm run test:coverage - Run tests with coverage report
  • npm run lint - Check for linting errors
  • npm run lint:fix - Fix linting errors automatically
  • npm run type-check - Run TypeScript type checking

Testing

Tests are written with Vitest and achieve 100% code coverage.

npm test
npm run test:coverage

Building

The build produces both ESM and CommonJS outputs with TypeScript declarations:

npm run build

Output files:

  • dist/index.js - ESM module
  • dist/index.cjs - CommonJS module
  • dist/index.d.ts - TypeScript declarations (ESM)
  • dist/index.d.cts - TypeScript declarations (CJS)

Browser Support

Duration works in all modern browsers and Node.js environments that support:

  • ES2022
  • Private class fields (#field)

Minimum versions:

  • Node.js 16+
  • Chrome 90+
  • Firefox 90+
  • Safari 15+
  • Edge 90+

License

MIT

Contributing

Contributions are welcome! Please ensure:

  • Tests pass (npm test)
  • Linting passes (npm run lint)
  • Type checking passes (npm run type-check)
  • Add tests for new features
  • Update documentation as needed

Made with ❤️ by the Duration team