# tinydate: a 349 byte date formatter that compiles a pattern once

> tinydate is a tiny JavaScript date formatter that turns a template pattern into a reusable render function, so a template is parsed once and reused for every date. It suits code that stamps many timestamps in a loop, and it deliberately implements only a small subset of Date methods.

**lukeed/tinydate** — A tiny (349B) reusable date formatter. Extremely fast!

- Repository: https://github.com/lukeed/tinydate
- Website: https://jsfiddle.net/lukeed/aoy0xeze/
- Stars: 1,067 · Forks: 16
- Language: JavaScript
- License: MIT
- Published: 2026-10-19 · Updated: 2026-10-19 · Language: en
- Canonical page: https://hysenlabs.com/projects/lukeed-tinydate

## The pattern is compiled once, then reused

The core idea of tinydate is a separation of parsing from rendering. You call tinydate with a template pattern, and it returns a function. That function is the thing you call, over and over, with a date. The README describes it as returning a render function that efficiently re-renders your deconstructed template, and the module is inspired by tinytime.

The usage example makes the shape clear:

```js
const tinydate = require('tinydate');
const fooDate = new Date('5/1/2017, 4:30:09 PM');

const stamp = tinydate('Current time: [{HH}:{mm}:{ss}]');

stamp(fooDate);
//=> Current time: [16:30:09]

stamp();
//=> Current time: [17:09:34]
```

The pattern string is written once at setup and the closure is captured. The second call passes no argument at all and the output shows the current time, which follows from the API: the returned function optionally accepts a date value as its only argument, and the default is a new Date() taken at call time.

That is the whole architecture of the library. There is no parser object, no formatter instance, and no configuration state beyond the optional dictionary.

## Eight patterns, and a dictionary you have to format yourself

The built in pattern set is deliberately small, and the README lists all of it: {YYYY} for the full year, {YY} for the short year, {MM} for the month, {DD} for the day, {HH} for hours on a 24 hour clock, {mm} for minutes, {ss} for seconds and {fff} for milliseconds. Examples are given for each, so 2017, 17, 04, 01, 06, 59, 09 and 555.

The second argument is a dictionary, an optional object of template patterns that may override existing ones or declare new ones. The documentation puts a strong caveat on it: all dictionary items must be a function, and each must control its own formatting. The example given is a custom {MMMM} that calls toLocaleString with a month option, and a {DD} override that returns the raw getDate value. The output is a July 4 date.

```js
const today = new Date('2019-07-04, 5:30:00 PM');

// Example custom dictionary:
//   - Adds {MMMM}
//   - Overrides {DD}
const stamp = tinydate('Today is: {MMMM} {DD}, {YYYY}', {
	MMMM: d => d.toLocaleString('en-US', { month: 'long' }),
	DD: d => d.getDate()
});

stamp(today);
//=> 'Today is: July 4, 2019'
```

The warning about padding is the part to internalise. The documentation says that when you define your own {ss} template, tinydate will not pad its value to two digits. Your function receives the date and returns the final string, so the zero padding that the built in patterns do for you is now your responsibility. That is the price of the extension point, and it is the same price in every small library that hands you the raw value.

## Where the speed comes from, and what the numbers measure

The README includes a benchmark section with numbers from Node v10.13.0, comparing three libraries:

```
tinydate    x 160,834,214 ops/sec ±0.21% (96 runs sampled)
tinytime    x  44,602,162 ops/sec ±0.34% (97 runs sampled)
time-stamp  x     888,153 ops/sec ±1.27% (86 runs sampled)
```

These are the project's own reported figures, measured by the author on a specific Node version, and they are worth reading for what they show rather than as a general claim. The comparison is against tinytime, the library tinydate is inspired by, and against time-stamp. The gap between tinydate and tinytime is roughly three and a half times, and the gap to time-stamp is two orders of magnitude, which tells you the family of libraries this belongs to differs from the family time-stamp belongs to.

The caveat is that the benchmark was run on Node 10.13.0, and it measures operations per second on a fixed pattern. It does not tell you what happens with a custom dictionary full of toLocaleString calls, where the cost moves into the Intl machinery, and it does not tell you anything about a modern runtime where the baseline costs have shifted.

The repository contains a bench/ directory and the package scripts show how the numbers are produced. The bench script runs a build first, and the test script does the same, so both measurements and tests work against the built output rather than the source.

## Installing it, and the three entry points in the manifest

Installation is the standard npm command:

```
$ npm install --save tinydate
```

The manifest declares three entry points, which is how the same package serves a bundler, a CommonJS require and a script tag. The main field points at dist/tinydate.js, the module field at dist/tinydate.mjs for ES module aware bundlers, and unpkg at dist/tinydate.min.js for direct inclusion in a page from a CDN. TypeScript users are served by a types field pointing at index.d.ts, and the files array ships dist plus the type declaration files, so the source is not published.

The build is handled by bundt, and the package scripts are minimal: a build script running bundt, a bench script running node bench with a prebench step that builds first, and a test script running tape over the test directory piped into tap-spec for readable output, with a pretest step that also builds. The dev dependencies are exactly those three tools.

The engine requirement is stated as node >=4, which tells you the code targets a very old runtime baseline. TypeScript definitions sit at the top level of the repository next to the source and test directories, so the library is typed for consumers even though the implementation is plain JavaScript.

There is a demo page on jsfiddle linked from the README, which is the fastest way to see the pattern syntax behave before installing anything.

## The honest boundary: a limited subset of Date methods

The README puts the limitation in the first paragraph, and it is the most important sentence for anyone choosing a library. This module only provides a limited subset of Date methods, and if you need more, the README names tinytime or date-fns as great alternatives. This is an unusually clear invitation to pick something else, and it deserves to be taken at face value rather than skimmed.

Concretely, what the eight built in patterns do not cover is large. There is no weekday or month name without writing a dictionary entry yourself, no time zone handling, no relative or human friendly output such as three days ago, no parsing of a date string, and no locale selection for the built in patterns. The {MM} and {DD} values are zero padded numbers, always, and always in the local time of the Date object you pass in, because that is all the underlying Date exposes through the accessors being used.

The custom dictionary softens this. Any function that receives the date and returns a string can be registered under any token, which means toLocaleString, time zone offsets and weekday names are all reachable with a few lines. But at that point you are writing the formatter yourself and tinydate is only the template plumbing around it, which is a fine reason to use the library and a fine reason not to.

## A small, quiet project with a long gap since the last release

The licence is MIT, credited to Luke Edwards, and the repository carries a license file at the top level alongside the readme, the source directory, the test directory, the bench directory, the type declarations and a CI workflow badge.

The release history is the part to look at. The tags are v1.3.0 from 2020-06-29, v1.2.0 from 2019-07-29 and v1.1.0 from 2019-06-04. The last push to the master branch was on 2026-09-22, so the code is being touched, but no new version has been published since the middle of 2020. For a library this stable in behaviour, that is not a problem in itself, since the API surface is eight patterns and a dictionary, but it does mean the published npm version is what most people will install and that version is several years old.

There is no tagline-driven roadmap in the README, no issue tracker guidance and no contributing section, which is consistent with a project of this size. The trade for a 349 byte library is that there is almost nothing to maintain and therefore almost no support. If you adopt it, the risk is not that it breaks, it is that a pattern you need later will never be added.

## Conclusion

Adopt tinydate when you format the same template for many dates, since the pattern is parsed once into a render function and the per-call cost is only the substitution, which is why it fits a loop or a server rendering many timestamps. Do not adopt it when you need locale aware output, time zone handling, relative dates or the wider Date surface, because the README states it covers only a limited subset of Date methods and points to tinytime or date-fns instead. Before you ship it, check that the eight built in patterns cover your format, and if you add a custom dictionary entry remember that tinydate will not pad it, so your own function has to do that.

## FAQ

### How do I install tinydate?

Run `npm install --save tinydate`. The package publishes a CommonJS entry at dist/tinydate.js, an ES module entry at dist/tinydate.mjs, a minified browser build at dist/tinydate.min.js for unpkg, and TypeScript types from index.d.ts.

### What date patterns does tinydate support?

Eight are built in: {YYYY}, {YY}, {MM}, {DD}, {HH} in 24 hour form, {mm}, {ss} and {fff}. Anything else, such as a long month name, has to be given as a function in the optional dictionary argument.

### Can I add my own format tokens to tinydate?

Yes, the second argument is a dictionary of template patterns that may override existing ones or declare new ones. Every entry must be a function, and the README notes that tinydate will not pad a custom value, so your function has to return the padded string itself.

### When should I use date-fns instead of tinydate?

The README states that tinydate provides only a limited subset of Date methods and names tinytime or date-fns as alternatives if you need more. It also points out that the built in patterns cover no time zone, relative or locale aware formatting, so those needs belong in a fuller library.

## Sources

- [License: MIT](https://github.com/lukeed/tinydate/blob/master/LICENSE)
- [lukeed/tinydate on GitHub](https://github.com/lukeed/tinydate)
- [Project website](https://jsfiddle.net/lukeed/aoy0xeze/)
- [README](https://github.com/lukeed/tinydate/blob/master/README.md)
- [Releases](https://github.com/lukeed/tinydate/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/lukeed-tinydate
