Open-source project
vercel/ms avatar
vercel/ms

vercel/ms: converting time strings to milliseconds, and back

Tiny millisecond conversion utility

5,554 stars336 forksTypeScriptMIT

At a glance

What is it?
ms is a small TypeScript utility that turns '2 days' into 172800000 and 60000 into '1m'. It is for engineers who need one dependable conversion at the boundary of config, cache headers or timeouts.
Who is it for?
Adopt ms if you parse human-written durations from config or env vars and want a package with template literal types and edge runtime support. Do not adopt it for calendar arithmetic, timezone conversion or date parsing: it does none of those.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 132 days ago.
What is it written in?
Mainly TypeScript, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on September 27, 2026, and from our analysis. They are not legal advice.

Editorial analysis

The unit string problem ms was written to remove

Configuration files, environment variables and cache headers all express duration in the same way: a number followed by a unit. `CACHE_TTL=2 days` reads better than `CACHE_TTL=172800000`, and a reviewer can check it without a calculator. The cost is that something has to parse it, and hand-rolled parsers are where the bugs live: a regex that accepts `2d` but not `2 days`, a switch that forgets `hrs`, a negative value that flips sign in the wrong place. ms exists to be that parser. The README states the package converts various time formats to milliseconds, and the examples show both directions: `ms('2 days')` returns 172800000, while `ms(60000)` returns `"1m"`. The audience is narrow and real: Node, Deno, Bun and browser code that has to accept a duration written by a human and hand a number to a timer, a cache directive or a rate limiter. It is not a date library and does not pretend to be one.

How parse and format move values through the package

The mechanism is a unit table plus a numeric parse. The README lists the accepted units as TypeScript types, from `Years` (`years`, `year`, `yrs`, `yr`, `y`) down to `Milliseconds` (`milliseconds`, `millisecond`, `msecs`, `msec`, `ms`). Casing and spacing are flexible: the README says formats can be lowercase, uppercase or capitalized, with or without a space. If no unit is passed, as in `ms('100')`, the value is treated as milliseconds and returned unchanged, which the examples confirm with `ms('100') // 100`. Fractional and negative inputs are supported, shown by `ms('2.5 hrs') // 9000000`, `ms('-3 days') // -259200000` and `ms(ms('10 hours')) // "10h"`. The reverse direction takes a number and produces a short string, or a written-out one when `{ long: true }` is passed: `ms(60000, { long: true })` returns `"1 minute"` and `ms(2 * 60000, { long: true })` returns `"2 minutes"`. As of v3.0 the two halves are importable separately as `parse` and `format`, so a bundle that only formats does not carry the parser. The repository is a single `src/` directory built by `tsdown` into `dist/`, with `sideEffects: false` in package.json, which tells bundlers the module can be dropped when unused.

Installing ms and converting a real value

The README lists npm, yarn, pnpm, deno and bun as install paths. Pick the one your project already uses; the package name is `ms` in all of them.

bash
npm install ms
yarn add ms
pnpm add ms
deno add npm:ms
bun add ms

Once installed, the default export is callable, and named exports are available for the split API. The README's own example parses a duration and formats it back, which is the round trip most services end up needing.

ts
import { parse, format } from 'ms';

parse('1h'); // 3600000

format(2000); // "2s"

Passing `{ long: true }` produces the written-out form, so `ms(60000, { long: true })` prints `"1 minute"`. In a Next.js edge function the README shows the same import used to report uptime, with `export const config = { runtime: 'experimental-edge' }` and a `Response` built from `ms(Date.now() - start)`.

If you want the compiler to reject malformed strings before runtime, import `parseStrict` instead of `parse`. The README's example shows `parseStrict('1h')` returning 3600000 and a plain `string` argument producing a `tsc` error. The `StringValue` type is exported for cases where you need to assert a wider string, and the README warns that the assertion is dangerous because a plain string may not match.

Where ms stops being the right tool

ms converts a duration. It does not know what day it is. Anything involving calendars, month lengths, leap seconds or timezones belongs elsewhere, and the unit table makes the trap easy to miss: `ms('1y')` returns 31557600000, which is a Julian year of 365.25 days, not the length of any particular calendar year. A subscription that expires "in 1 year" computed this way will drift from the calendar by hours or days depending on when it starts. The same applies to `mo`: months are a fixed conversion inside the package, not a calendar operation. The second boundary is typing. The README is explicit that the casing and spacing rules are enforced at the type level only when you use `parseStrict`; the default `ms()` call accepts a `StringValue` that the README describes as potentially wider than what the parser handles, which is why the type assertion example is labelled dangerous. The third boundary is packaging. package.json declares `"type": "module"` and an `exports` map whose conditions are `import` and `default`, both pointing at ESM output, and `engines` requires Node `>=20`. A CommonJS consumer on an older runtime will not get a working import from this version without a build step in between.

ms compared with pretty-ms and ms.macro

The closest alternative in everyday use is pretty-ms, which appears in the related searches for this project. The difference is scope: pretty-ms is a formatter first, taking a number and producing a readable string, and its own option set covers things like compact output and verbose labels. ms is symmetric by design. Its README documents both directions in the same callable, `ms('10 hours')` to a number and `ms(ms('10 hours'), { long: true })` back to `"10 hours"`, and it ships `parse`, `format` and `parseStrict` as separate named exports for code that wants only one half. If your codebase only ever renders durations for display, pretty-ms is the more direct fit; if you also need to read durations out of config, keeping one package for both directions avoids two unit tables that can disagree about whether `m` means minutes or months. The README also points to ms.macro, which runs ms as a macro at build time, so the conversion happens during compilation and the runtime carries no parser at all. That is a real trade-off rather than a variant: a macro cannot handle a value that only exists at runtime, such as an environment variable, so it suits hardcoded constants and nothing else.

Maintenance, licence and the upgrade question

The last push to the repository was on 2026-05-20, so the project is not abandoned, but the release history is worth reading before you plan an upgrade. The newest published releases listed are two canaries from 2021-09-15, `3.0.0-canary.0` and `3.0.0-canary.1`, while the last stable tag shown is `2.1.3` from 2020-12-08. package.json, however, declares version `4.0.0`. That gap between what the registry shows in the release list and what the repository declares is the thing to verify first: check the actual version your package manager resolves before assuming the v3 and v4 APIs described in the README are what you will get. The licence is MIT, which permits commercial use, modification and redistribution provided the copyright notice and permission notice are retained; the LICENSE file sits at the repository root. That is a description of the licence text, not legal advice, and if you vendor or fork the package your own obligations should be checked by someone qualified. Upgrade cost is low in absolute terms because the surface is small, but the v3 split into `parse` and `format` and the v4 shift to ESM-only packaging mean a CommonJS codebase has real work to do: either move to ESM or keep an older major pinned.

Reading the repository before you depend on it

The layout tells you what kind of project this is. There is a single `src/` directory, a `tsconfig.json`, a `tsdown.config.ts` build config, `jest.config.ts` with two scripts in package.json, `test:nodejs` and `test:edge`, and `biome.json` for lint and format. The dual test script is the interesting part: the package is tested under both the Node environment and `@edge-runtime/jest-environment`, which matches the README's claim of edge runtime compatibility rather than leaving it as marketing. There is also an `attw` script that runs `attw --pack --profile esm-only .` after a build, a check on whether the published types resolve correctly for consumers. For an engineer deciding whether to add a dependency, that combination is the signal to look at: the project tests the environments it claims to support and checks its own type exports, and it is small enough to read end to end in an afternoon. The README does not document a rollback path or a migration guide from v2 to v3 to v4, so plan on reading the source diff if you are moving a large codebase across majors.

Editorial conclusion

Adopt ms if you parse human-written durations from config or env vars and want a package with template literal types and edge runtime support. Do not adopt it for calendar arithmetic, timezone conversion or date parsing: it does none of those. Before installing, check that your Node version satisfies the engines field (>=20) and that your code consumes ESM, because package.json declares type module and an exports map with only import and default conditions.

Frequently asked questions

What is the Vercel app used for?

The repository this question matches is vercel/ms, a millisecond conversion utility: it converts time formats such as '2 days' to milliseconds and numbers such as 60000 back to a unit string like "1m". It is published as the ms package on npm.

How do I install vercel/ms?

Install it with your package manager under the name ms. The README lists npm install ms, yarn add ms, pnpm add ms, deno add npm:ms and bun add ms.

Does vercel/ms work in the browser and on edge runtimes?

The README states the package works in both browsers and server runtimes such as Node.js, Deno and Bun, and it documents Edge Runtime compatibility with an example for Vercel Edge Functions. The repository also runs a separate jest environment for edge.

How do I get strict type checking on the input string in vercel/ms?

Import parseStrict instead of parse. The README shows parseStrict('1h') returning 3600000 and a plain string argument producing a tsc error, because casing and spacing are enforced at the type level only in that mode.

Can vercel/ms handle negative and fractional durations?

Yes. The README's examples include ms('2.5 hrs') returning 9000000, ms('-3 days') returning -259200000, and ms(-3 * 60000, { long: true }) returning "-3 minutes".

What Node version does vercel/ms require?

package.json sets the engines field to node >=20, and it declares the package as type module with an exports map pointing at ESM output. Consumers on older runtimes or on CommonJS need a build step or an older major.

Official sources

  1. License: MIT
  2. Project website
  3. README
  4. Releases
  5. vercel/ms on GitHub
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/vercel-ms.svg)](https://hysenlabs.com/projects/vercel-ms)