## Intl DurationUnitFormat

Format time duration in quantities of units such as years, months, days, hours, minutes and seconds.

## Overview

The goal of this project is to provide a way to format time durations (like `1 minute 30 seconds`) since the standards don't provide a way to do it.

ECMAScript Internationalization has a long-standing [open issue](https://github.com/tc39/ecma402/issues/47) to provide an `Intl.DurationFormat` API that can accommodate time duration use cases, but as of February 2020 the [proposal](https://github.com/younies/proposal-intl-duration-format) is still at [Stage 1](https://github.com/tc39/proposals/blob/master/ecma402/README.md).

This project does its best at creating a `DurationFormat` implementation that resembles other standard APIs, but only work with time durations.

I'm not in any way part of the standardization process, that's why this package is `-unofficial-`.

## Use cases

1. Natural language time: `1 minute 20 seconds`
1. Timers: `01:20`
1. Short formats `1 hr 2 min 30 sec` or `1h 2m 30s`

## Usage

```js
const duration = new DurationUnitFormat(locales, options);
const parts = duration.formatToParts(90);

/*
parts = [
  { type: 'minute', value: '1' },
  { type: 'literal', value: ' ' },
  { type: 'unit', value: 'minute' },
  { type: 'second', value: '30' },
  { type: 'literal', value: ' ' },
  { type: 'unit', value: 'seconds' },
];
 */
```

## Installation

`npm i intl-unofficial-duration-unit-format`

This package depends on [intl-messageformat](https://github.com/yahoo/intl-messageformat) which is listed in `peerDependencies`.

`intl-messageformat` itself depends on the global [`Intl`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl) object. Refer to [`intl-messageformat` README](https://github.com/formatjs/formatjs/tree/master/packages/intl-messageformat#modern-intl-dependency) on how to set it up correctly on node.js or in the browser.

### Webpack / Parcel / Rollup / ....

All modern bundlers allow you to import npm libraries

```js
import DurationUnitFormat from 'intl-unofficial-duration-unit-format';
```


## Public API

### `DurationUnitFormat` constructor

To create a duration unit to format, use the `DurationUnitFormat` constructor, it takes two parameters:

1. `locales: String | Array<String>` A string with a BCP 47 language tag, or an array of such strings. For more details check [this](https://github.com/yahoo/intl-messageformat/blob/master/README.md#locale-resolution).
1. `options: Object` An optional configuration object. Refer to [`Options`](#options) for additional details.

### `formatToParts(number)`

Once the duration object is created with `duration = new DurationUnitFormat()`, you can call `formatToParts` passing a numeric value.

`number` must be expressed in seconds, so a value of `60` corresponds to `1 minute`.


## Options

## `style`

One of

1. `DurationUnitFormat.styles.LONG` or just `long` for long format time (`1 minute 30 seconds`)
1. `DurationUnitFormat.styles.SHORT` or just `short` for short format time (`1 min 30 sec`)
1. `DurationUnitFormat.styles.NARROW` or just `narrow` for narrow format time (`1m 30s`)
1. `DurationUnitFormat.styles.TIMER` for timers (`1:30`)
1. `DurationUnitFormat.styles.CUSTOM` for custom formats (`1 minute 30 seconds`)

The default is `LONG`.

When the style is `TIMER` numbers are padded and different default formats are applied.

The options `formatDuration` and `formatUnits` only apply when the style is `CUSTOM`.

Given the input of `3600` (1 hour), it'll generate

| `style`  | `format` |Output |
|----------|----------|--------|
| `CUSTOM` | undefined (defaults to `{seconds}`) | `3600 seconds` |
| `CUSTOM` | `{minutes} {seconds}` | `60 minutes` |
| `CUSTOM` | `{hour} {minutes} {seconds}` | `1 hour` |
| `TIMER`  | undefined (defaults to `{minutes}:{seconds}`)| `60:00` |
| `TIMER`  | `{seconds}s` | `3600s` |
| `TIMER`  | `{hour}:{minutes}:{seconds}` | `1:00:00` |
| `TIMER`  | `{days}d {hour}:{minutes}:{seconds}` | `0d 01:00:00` |
| `LONG`   | undefined (defaults to `{seconds}`) | `3600 seconds` |
| `LONG`   | `{minutes} {seconds}` | `60 minutes` |
| `LONG`   | `{hour} {minutes} {seconds}` | `1 hour` |
| `SHORT`  | undefined (defaults to `{seconds}`) | `3600 sec` |
| `SHORT`  | `{minutes} {seconds}` | `60 min` |
| `SHORT`  | `{hour} {minutes} {seconds}` | `1 hr` |
| `NARROW` | undefined (defaults to `{seconds}`) | `3600s` |
| `NARROW` | `{minutes} {seconds}` | `60m` |
| `NARROW` | `{hour} {minutes} {seconds}` | `1h` |

As shown from the examples, when `TIMER` is used

1. empty units are kept and rendered as `00` or `0` if they're the highest unit in the format. Empty units in `CUSTOM` are discarded.
1. values are padded to at least 2 digits

Note that on Node.js 10.x, the default implementation of Intl.NumberFormat does not support styles, so you need to use the `@formatjs/intl-unified-numberformat` polyfill.

### `format`

Defines the format of the output string. Default to `{seconds}`.

It can be any string containing any of the placeholders `{days}`, `{hours}`, `{minutes}`, `{seconds}` or any other literal. The format of the placeholders can be customized by [`formatDuration`](#formatDuration).

Given the input of `3661` (1 hour, 1 minute and 1 second), it'll generate

| `format` | Output |
|----------|--------|
| `{seconds}` | `3661 seconds` |
| `{minutes} {seconds}` | `61 minutes 1 second` |
| `{minutes} and {seconds}` | `61 minutes and 1 second` |
| `{hours} {minutes} {seconds}` | `1 hour 1 minute 1 second` |
| `{hours} {minutes}` | `1 hour 1 minute` |
| `{hours}` | `1 hour` |
| `{hours}` | `1 hour` |
| `{days}`  | `0 days` |


### `formatDuration`

Defines the format of the output placeholders. Default to `{value} {unit}`.

It can be any string containing `{value}`, `{unit}` or any other literal. `{value}` corresponds to the numeric value (`1`), while `{unit}` is the time unit (`minute`, `second`).

Given the input of `1`, it'll generate

| `formatDuration`  | Output      |
|-------------------|-------------|
| `{value} {unit}`  | `1 second`  |
| `{value}{unit}`   | `1second`   |
| `{unit}: {value}` | `second: 1` |

## `formatUnits`

Defines the format and localization of each placeholder's `{unit}`. Defaults to the object

```js
{
  [DurationUnitFormat.units.DAY]: '{value, plural, one {day} other {days}}',
  [DurationUnitFormat.units.HOUR]: '{value, plural, one {hour} other {hours}}',
  [DurationUnitFormat.units.MINUTE]: '{value, plural, one {minute} other {minutes}}',
  [DurationUnitFormat.units.SECOND]: '{value, plural, one {second} other {seconds}}',
}
```

The object key must be one of the possible units, and the value is a string using the ICU format defined in [intl-messageformat](https://github.com/yahoo/intl-messageformat/).

Given the input of `1`, it'll generate

| `formatUnits[DurationUnitFormat.units.SECOND]` | Output |
|--------|--------|
| `{value, plural, one {second} other {seconds}}` | `second` |
| `{value, plural, other {秒}}` | `秒` |
| `s` | `s` |

## `round`

Whether or not to round results when smaller units are missing. Default to `false`.

Given the input of `30` and the format `{minutes}`

| `round` | Output      |
|---------|-------------|
| `false` | `0 minutes` |
| `true`  | `1 minute`  |
