Verbatempus - A Clock full of Words

Usage

Verbatempus turns a JavaScript Date into an English phrase you can read or say aloud, at four levels of verbosity. No dependencies; runs in Node and the browser; renders in any IANA time zone.

Install

npm install verbatempus

ES modules

import { format } from 'verbatempus';

format(new Date(2024, 9, 24, 10, 24));
// 'it is twenty four minutes past ten oclock in the morning'

format(new Date(2024, 9, 24, 10, 24), { level: 'terse', parts: 'both' });
// 'it is thursday at quarter after ten'

CommonJS

const { format } = require('verbatempus');

Plain <script>, no build step

The browser bundle defines a global Verbatempus.

<script src="https://cdn.jsdelivr.net/npm/verbatempus@2/dist/verbatempus.iife.js"></script>
<script>
  document.body.textContent = Verbatempus.format(new Date(), { level: 'short' });
</script>

To avoid a third-party request, copy dist/verbatempus.iife.js out of the npm package and serve it yourself. The split-flap and flip-dot displays do this.

Try it

This runs the library in your browser.

Loading...

format(new Date())

Tokens

Options

format(date?, options?) -> string

date defaults to now. null, a non-Date, or an invalid Date throws.

optionvaluesdefault
level'verbose' 'lengthy' 'short' 'terse''verbose'
parts'time' 'date' 'both''time''both' is one sentence
timeZonean IANA name, e.g. 'America/New_York'local timean invalid name throws a RangeError
case'lower' 'upper''lower'
charset'full' 'alpha''full''alpha' keeps A-Z and spaces only

Unknown option values throw instead of falling back silently.

Time zones

const d = new Date('2024-10-24T23:30:00Z');

format(d, { timeZone: 'UTC' });              // 'it is half past eleven oclock in the evening'
format(d, { timeZone: 'America/New_York' }); // 'it is half past seven oclock in the evening'
format(d, { timeZone: 'Asia/Kolkata' });     // 'it is five oclock in the morning'

Case and charset

For displays that can only show capital letters, such as a split-flap board:

format(d, { level: 'verbose', parts: 'both', case: 'upper', charset: 'alpha' });
// 'IT IS THURSDAY OCTOBER THE TWENTY FOURTH TWENTY TWENTY FOUR AND IT IS TWENTY FOUR
//  MINUTES PAST TEN OCLOCK IN THE MORNING'

Levels

Thursday 24 October 2024, 10:24.

leveltimedate
verboseit is twenty four minutes past ten oclock in the morningit is thursday october the twenty fourth twenty twenty four
lengthyit is twenty four past ten in the morningit is thursday october twenty fourth
shortit is almost half past ten amit is thursday the twenty fourth
terseits quarter after tenit is thursday

Every phrase at every level, for all 1440 minutes of the day, is checked against a golden fixture in the test suite.

Date and time

parts: 'both' is one sentence: the date, a joiner, then the time without its own it is. Verbose joins with , and it is; the other levels join with at.

format(d, { level: 'verbose', parts: 'both' });
// 'it is thursday october the twenty fourth twenty twenty four, and it is
//  twenty four minutes past ten oclock in the morning'

format(d, { level: 'lengthy', parts: 'both' });
// 'it is thursday october twenty fourth at twenty four past ten in the morning'

formatParts

Same arguments, structured result. Use it when a display needs to lay words out itself.

formatParts(new Date(2024, 9, 24, 10, 24), { level: 'short' });
// {
//   text: 'it is almost half past ten am',
//   tokens: [
//     { type: 'lead',   value: 'it is' },
//     { type: 'rel',    value: 'almost' },
//     { type: 'minute', value: 'half' },
//     { type: 'rel',    value: 'past' },
//     { type: 'hour',   value: 'ten' },
//     { type: 'suffix', value: 'am' },
//   ],
//   level: 'short',
//   parts: 'time',
//   maxLength: 39
// }

Token types: lead, minute, rel, hour, oclock, suffix, weekday, month, day, year, join. text is always the tokens read in order, after case and charset.

MAX_LENGTH

The longest phrase, in characters, for each level and parts. Fixed-size displays should size themselves from it.

leveltimedateboth
verbose6165132
lengthy464084
short393471
terse261541

Time covers all 1440 minutes; date covers every calendar day from 2010 to 2099.

Named functions

Thin wrappers over format. level and parts are fixed; timeZone, case and charset pass through as the second argument.

verboseTime     lengthyTime     shortTime     terseTime
verboseDate     lengthyDate     shortDate     terseDate
verboseDateTime lengthyDateTime shortDateTime terseDateTime

shortDate(new Date(2024, 9, 24));  // 'it is thursday the twenty fourth'

Upgrading from 1.0