# Date formatter

- Page: https://koobiq.io/en/other/date-formatter/overview

## Overview

DateFormatter is a unified system for formatting dates and times. It keeps the presentation consistent across the whole application and follows the corporate standards.

DateFormatter tracks locale changes through KbqLocaleService on its own and updates the formats when the interface language changes.

### Methods in TypeScript code

DateFormatter methods format a date or a time directly in TypeScript code:

```typescript
const formattedStringOfDate = this.formatter.absoluteLongDate(this.adapter.today());
```

### Pipes in templates

Formatting in HTML templates is done with pipes whose names correspond to the DateFormatter methods:

```html
<div>{{ adapter.today() | kbqAbsoluteLongDate }}</div>
```

The pipes need `KbqFormattersModule` to be imported — it provides `DateFormatter` and exports every pipe. [Usage examples](https://github.com/koobiq/angular-components/tree/main/packages/components-dev/date-pipes)

#### Which family to choose

The same format is available in three flavours:

| Family                   | Example                      | Behaviour                                                                                                |
| ------------------------ | ---------------------------- | -------------------------------------------------------------------------------------------------------- |
| `kbq*` — **recommended** | `kbqAbsoluteLongDate`        | Recomputes on a KbqLocaleService locale change, caches the result by value, arguments and locale         |
| No prefix                | `absoluteLongDate`           | A pure pipe. Recomputes only when the input value changes — **the string goes stale on a locale change** |
| `ImpurePipe` suffix      | `absoluteLongDateImpurePipe` | An impure pipe with no cache: reformats on every change detection cycle                                  |

The unprefixed and `ImpurePipe` flavours are deprecated and will be removed in 22.0.0: each one maps to the `kbq*` pipe of the same format, with the same arguments.

#### The pipes

| Pipe                       | Input        | Arguments                                      | DateFormatter method    |
| -------------------------- | ------------ | ---------------------------------------------- | ----------------------- |
| `kbqAbsoluteShortDate`     | date         | `currYear?: boolean`                           | `absoluteShortDate`     |
| `kbqAbsoluteLongDate`      | date         | `currYear?: boolean`                           | `absoluteLongDate`      |
| `kbqAbsoluteShortDateTime` | date         | `options?: DateTimeOptions`                    | `absoluteShortDateTime` |
| `kbqAbsoluteLongDateTime`  | date         | `options?: DateTimeOptions`                    | `absoluteLongDateTime`  |
| `kbqRelativeShortDate`     | date         | —                                              | `relativeShortDate`     |
| `kbqRelativeLongDate`      | date         | —                                              | `relativeLongDate`      |
| `kbqRelativeShortDateTime` | date         | `options?: DateTimeOptions`                    | `relativeShortDateTime` |
| `kbqRelativeLongDateTime`  | date         | `options?: DateTimeOptions`                    | `relativeLongDateTime`  |
| `kbqRangeShortDate`        | `[from, to]` | —                                              | `rangeShortDate`        |
| `kbqRangeLongDate`         | `[from, to]` | —                                              | `rangeLongDate`         |
| `kbqRangeShortDateTime`    | `[from, to]` | `options?: DateTimeOptions`                    | `rangeShortDateTime`    |
| `kbqRangeMiddleDateTime`   | `[from, to]` | `options?: DateTimeOptions`                    | `rangeMiddleDateTime`   |
| `kbqRangeLongDateTime`     | `[from, to]` | `options?: DateTimeOptions`                    | `rangeLongDateTime`     |
| `kbqDurationShortest`      | `[from, to]` | `options?: DateTimeOptions`                    | `durationShortest`      |
| `kbqDurationShort`         | `[from, to]` | `units?: DurationUnit[]`, `fraction?: boolean` | `durationShort`         |
| `kbqDurationLong`          | `[from, to]` | `units?: DurationUnit[]`, `fraction?: boolean` | `durationLong`          |

`DateTimeOptions` is `{ seconds?: boolean; milliseconds?: boolean; currYear?: boolean }`. `kbqDurationShortest` uses only `seconds` (`true` by default) and `milliseconds` from it.

```html
<div>{{ [task.startedAt, task.finishedAt] | kbqDurationShortest }}</div>
<div>{{ [task.startedAt, task.finishedAt] | kbqDurationLong: ['hours', 'minutes'] }}</div>
```

#### Opened ranges

Pass `null` instead of one of the bounds and the range pipe switches to the opened-range format ("From January 15", "Until June 20") on its own. No separate pipe is needed for that.

```html
<div>{{ [filter.from, filter.to] | kbqRangeLongDate }}</div>
```

`kbqRangeMiddleDateTime` is the exception: the middle format has no opened-range template, so it requires both bounds.

#### Empty and invalid values

When a date is missing or cannot be parsed, the pipe renders an empty string. The input types allow this: a binding that has not been populated yet is `null` or `undefined`, not a date, and for the range and duration pipes that applies to the whole `[from, to]` tuple as well as to a bound inside it.

The range pipes that support an opened range — `kbqRangeShortDate`, `kbqRangeLongDate`, `kbqRangeShortDateTime`, `kbqRangeLongDateTime` — render an empty string only when both bounds are missing or invalid; a single valid bound switches them to the opened-range format instead. `kbqRangeMiddleDateTime` and the duration pipes need both bounds, so they render an empty string as soon as either one is missing or invalid; the duration pipes additionally render an empty string when the start is later than the end.

Call the `DateFormatter` methods directly if you need to be told about the error instead of hiding it — they throw.

#### Custom formats

Formats that have no pipe are available through `DateFormatter`: its public `config` field holds the templates of the active locale.

```typescript
private readonly formatter = inject<DateFormatter<DateTime>>(DateFormatter);

format(from: DateTime, to: DateTime): string {
    return this.formatter.rangeDate(from, to, this.formatter.config.rangeTemplates.closedRange.middle);
}
```

`absoluteDate`, `relativeDate`, `rangeDateTime`, `duration` and `openedRangeDate` work the same way — they take a template as an argument.

### Time zone

By default dates are rendered in the time zone of the host the code runs on: the user's zone in the browser, the server's zone during server-side rendering. The `KBQ_DATE_TIMEZONE` token sets the zone once for the whole application — the pipes, `DateFormatter`, the calendar and the date inputs all switch to it, so there is no offset to pass to every pipe.

```typescript
bootstrapApplication(App, {
    providers: [kbqDateTimezoneProvider('Europe/Moscow')]
});
```

The token accepts:

| Value                      | Example                              | Notes                              |
| -------------------------- | ------------------------------------ | ---------------------------------- |
| IANA zone name             | `'Europe/Moscow'`                    | Follows the DST rules of that zone |
| Offset in minutes from UTC | `180`, `-330`                        | Fixed, with no DST                 |
| Offset as a string         | `'+03:00'`, `'UTC+3'`, `'GMT+05:30'` | The same, written out              |
| `'utc'`                    |                                      |                                    |
| `'system'`                 |                                      | The default — the zone of the host |

An unknown zone name does not break rendering: dates stay in the host zone and a warning is logged in development mode. The same applies to an offset outside `±14:00`, the widest any zone has ever used; a fractional offset is rounded to whole minutes.

#### Changing the zone at runtime

`KbqDateTimezoneService.setTimezone()` changes the zone without a reload, and the `kbq*` pipes re-render:

```typescript
private readonly timezoneService = inject(KbqDateTimezoneService);

setUserTimezone(timezone: string) {
    this.timezoneService.setTimezone(timezone);
}
```

Pure pipes (the ones without the `kbq` prefix) do not react to a zone change, exactly as they do not react to a locale change.

#### A zone for part of the application

The zone reaches the pipes through the date adapter and `DateFormatter`, and those read `KbqDateTimezoneService` from the injector that created them. `kbqDateTimezoneProvider` provides that service alongside the token, so scoping a zone to a subtree means listing it with the adapter and the formatter:

```typescript
@Component({
    providers: [
        kbqDateTimezoneProvider('Asia/Tokyo'),
        { provide: DateAdapter, useClass: LuxonDateAdapter },
        DateFormatter
    ]
})
```

#### Server-side rendering

With SSR the page is rendered in the server zone and re-rendered in the browser zone after hydration. When the two differ, every date on the page changes at once, which reads as flickering. To avoid it, the server and the client have to receive the same `KBQ_DATE_TIMEZONE` value.

The most reliable way to pass it is `TransferState`: the server resolves the zone from a cookie (or from the user profile) and puts it into the state, and the client reads the resolved value — so the two match even when there is no cookie yet.

```typescript
// server config
export const serverConfig = mergeApplicationConfig(appConfig, {
    providers: [
        provideServerRendering(),
        {
            provide: KBQ_DATE_TIMEZONE,
            useFactory: () => {
                const timezone = readTimezoneCookie(inject(REQUEST, { optional: true })) ?? 'utc';

                inject(TransferState).set(TIMEZONE_KEY, timezone);

                return timezone;
            }
        }
    ]
});

// browser config — take exactly what the server rendered with
export const appConfig: ApplicationConfig = {
    providers: [
        {
            provide: KBQ_DATE_TIMEZONE,
            useFactory: () => inject(TransferState).get(TIMEZONE_KEY, 'utc')
        }
    ]
};
```

On the very first visit there is no cookie yet, and the page renders with the fallback (`'utc'` in the example). After hydration, compare it with `Intl.DateTimeFormat().resolvedOptions().timeZone`, write the cookie and, if the zone has to apply right away, call `setTimezone()` — that is a single re-render, and only on the first visit. When the zone is stored in the user profile on the server, even the first visit avoids it.

### Available formats

#### Absolute date

**Example: Absolute date-formatter** (`absolute-date-formatter`)

```ts absolute-date-formatter-example.ts
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, inject } from '@angular/core';
import { LuxonDateModule } from '@koobiq/angular-luxon-adapter/adapter';
import { DateAdapter, DateFormatter, KBQ_LOCALE_SERVICE, KbqFormattersModule } from '@koobiq/components/core';
import { DateTime } from 'luxon';
import { delay } from 'rxjs';
import { distinctUntilChanged } from 'rxjs/operators';

@Component({
    selector: 'absolute-date-formatter-example',
    imports: [LuxonDateModule, KbqFormattersModule],
    templateUrl: 'absolute-date-formatter-example.html',
    styleUrls: ['absolute-date-formatter-example.css'],
    changeDetection: ChangeDetectionStrategy.OnPush
})
export class AbsoluteDateFormatterExample {
    private readonly adapter = inject<DateAdapter<DateTime>>(DateAdapter);
    private readonly dateFormatter = inject<DateFormatter<DateTime>>(DateFormatter);
    private readonly changeDetectorRef = inject(ChangeDetectorRef);
    private readonly localeService = inject(KBQ_LOCALE_SERVICE);

    formats = {
        absolute: {
            long: {
                date: {
                    currentYear: '',
                    notCurrentYear: ''
                },
                dateTime: {
                    currentYear: '',
                    currentYearSeconds: '',
                    currentYearMilliseconds: '',

                    notCurrentYear: '',
                    notCurrentYearSeconds: '',
                    notCurrentYearMilliseconds: ''
                }
            },
            short: {
                date: {
                    currentYear: '',
                    notCurrentYear: ''
                },
                dateTime: {
                    currentYear: '',
                    currentYearSeconds: '',
                    currentYearMilliseconds: '',

                    notCurrentYear: '',
                    notCurrentYearSeconds: '',
                    notCurrentYearMilliseconds: ''
                }
            }
        }
    };

    constructor() {
        this.localeService.changes.pipe(distinctUntilChanged(), delay(0)).subscribe(this.onLocaleChange);
    }

    private onLocaleChange = (locale: string) => {
        this.dateFormatter.setLocale(locale);

        this.populateAbsoluteLong();
        this.populateAbsoluteShort();

        this.changeDetectorRef.markForCheck();
    };

    private populateAbsoluteShort() {
        const now = this.adapter.today();

        const short = this.formats.absolute.short;

        short.date.currentYear = this.dateFormatter.absoluteShortDate(now);
        short.date.notCurrentYear = this.dateFormatter.absoluteShortDate(now.minus({ years: 1 }));

        short.dateTime.currentYear = this.dateFormatter.absoluteShortDateTime(now);
        short.dateTime.currentYearSeconds = this.dateFormatter.absoluteShortDateTime(now, { seconds: true });
        short.dateTime.currentYearMilliseconds = this.dateFormatter.absoluteShortDateTime(now, { milliseconds: true });

        short.dateTime.notCurrentYear = this.dateFormatter.absoluteShortDateTime(now.minus({ years: 1 }));
        short.dateTime.notCurrentYearSeconds = this.dateFormatter.absoluteShortDateTime(now.minus({ years: 1 }), {
            seconds: true
        });
        short.dateTime.notCurrentYearMilliseconds = this.dateFormatter.absoluteShortDateTime(now.minus({ years: 1 }), {
            milliseconds: true
        });
    }

    private populateAbsoluteLong() {
        const now = this.adapter.today();

        const long = this.formats.absolute.long;

        long.date.currentYear = this.dateFormatter.absoluteLongDate(now);
        long.date.notCurrentYear = this.dateFormatter.absoluteLongDate(now.minus({ years: 1 }));

        long.dateTime.currentYear = this.dateFormatter.absoluteLongDateTime(now);
        long.dateTime.currentYearSeconds = this.dateFormatter.absoluteLongDateTime(now, { seconds: true });
        long.dateTime.currentYearMilliseconds = this.dateFormatter.absoluteLongDateTime(now, { milliseconds: true });

        long.dateTime.notCurrentYear = this.dateFormatter.absoluteLongDateTime(now.minus({ years: 1 }));
        long.dateTime.notCurrentYearSeconds = this.dateFormatter.absoluteLongDateTime(now.minus({ years: 1 }), {
            seconds: true
        });
        long.dateTime.notCurrentYearMilliseconds = this.dateFormatter.absoluteLongDateTime(now.minus({ years: 1 }), {
            milliseconds: true
        });
    }
}
```

```html absolute-date-formatter-example.html
<div class="kbq-text-big">
    <div>
        <h3>Long format</h3>
        <div>
            <div class="layout-row example-row-border">
                <div class="flex example-light-text-secondary">Name</div>
                <div class="flex example-light-text-secondary">Value</div>
            </div>
            <div class="layout-row example-row-border">
                <div class="flex">absoluteLongDate (current year)</div>
                <div class="flex">{{ formats.absolute.long.date.currentYear }}</div>
            </div>
            <div class="layout-row example-row-border">
                <div class="flex">absoluteLongDate (not current year)</div>
                <div class="flex">{{ formats.absolute.long.date.notCurrentYear }}</div>
            </div>
            <div class="example-row-border"></div>

            <div class="layout-row example-row-border">
                <div class="flex">absoluteLongDateTime (current year)</div>
                <div class="flex">{{ formats.absolute.long.dateTime.currentYear }}</div>
            </div>
            <div class="layout-row example-row-border">
                <div class="flex">absoluteLongDateTime (current year) (with seconds)</div>
                <div class="flex">{{ formats.absolute.long.dateTime.currentYearSeconds }}</div>
            </div>
            <div class="layout-row example-row-border">
                <div class="flex">absoluteLongDateTime (current year) (with milliseconds)</div>
                <div class="flex">{{ formats.absolute.long.dateTime.currentYearMilliseconds }}</div>
            </div>
            <div class="example-row-border"></div>

            <div class="layout-row example-row-border">
                <div class="flex">absoluteLongDateTime (not current year)</div>
                <div class="flex">{{ formats.absolute.long.dateTime.notCurrentYear }}</div>
            </div>
            <div class="layout-row example-row-border">
                <div class="flex">absoluteLongDateTime (not current year) (with seconds)</div>
                <div class="flex">{{ formats.absolute.long.dateTime.notCurrentYearSeconds }}</div>
            </div>
            <div class="layout-row example-row-border">
                <div class="flex">absoluteLongDateTime (not current year) (with milliseconds)</div>
                <div class="flex">{{ formats.absolute.long.dateTime.notCurrentYearMilliseconds }}</div>
            </div>
        </div>
    </div>
    <div>
        <h3>Short format</h3>
        <div>
            <div class="layout-row example-row-border">
                <div class="flex example-light-text-secondary">Name</div>
                <div class="flex example-light-text-secondary">Value</div>
            </div>
            <div class="layout-row example-row-border">
                <div class="flex">absoluteShortDate (current year)</div>
                <div class="flex">{{ formats.absolute.short.date.currentYear }}</div>
            </div>
            <div class="layout-row example-row-border">
                <div class="flex">absoluteShortDate (not current year)</div>
                <div class="flex">{{ formats.absolute.short.date.notCurrentYear }}</div>
            </div>
            <div class="example-row-border"></div>

            <div class="layout-row example-row-border">
                <div class="flex">absoluteShortDateTime (current year)</div>
                <div class="flex">{{ formats.absolute.short.dateTime.currentYear }}</div>
            </div>
            <div class="layout-row example-row-border">
                <div class="flex">absoluteShortDateTime (current year) (with seconds)</div>
                <div class="flex">{{ formats.absolute.short.dateTime.currentYearSeconds }}</div>
            </div>
            <div class="layout-row example-row-border">
                <div class="flex">absoluteShortDateTime (current year) (with milliseconds)</div>
                <div class="flex">{{ formats.absolute.short.dateTime.currentYearMilliseconds }}</div>
            </div>
            <div class="example-row-border"></div>

            <div class="layout-row example-row-border">
                <div class="flex">absoluteShortDateTime (not current year)</div>
                <div class="flex">{{ formats.absolute.short.dateTime.notCurrentYear }}</div>
            </div>
            <div class="layout-row example-row-border">
                <div class="flex">absoluteShortDateTime (not current year) (with seconds)</div>
                <div class="flex">{{ formats.absolute.short.dateTime.notCurrentYearSeconds }}</div>
            </div>
            <div class="layout-row example-row-border">
                <div class="flex">absoluteShortDateTime (not current year) (with milliseconds)</div>
                <div class="flex">{{ formats.absolute.short.dateTime.notCurrentYearMilliseconds }}</div>
            </div>
        </div>
    </div>
</div>
```

```css absolute-date-formatter-example.css
.example-light-text-secondary {
    color: #ccc;
}

.example-row-border {
    padding: 8px;
    border-bottom: 1px solid #ccc;
}
```

#### Relative date

**Example: Relative date-formatter** (`relative-date-formatter`)

```ts relative-date-formatter-example.ts
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, inject } from '@angular/core';
import { LuxonDateModule } from '@koobiq/angular-luxon-adapter/adapter';
import { DateAdapter, DateFormatter, KBQ_LOCALE_SERVICE, KbqFormattersModule } from '@koobiq/components/core';
import { DateTime } from 'luxon';
import { delay } from 'rxjs';
import { distinctUntilChanged } from 'rxjs/operators';

@Component({
    selector: 'relative-date-formatter-example',
    imports: [LuxonDateModule, KbqFormattersModule],
    templateUrl: 'relative-date-formatter-example.html',
    styleUrls: ['relative-date-formatter-example.css'],
    changeDetection: ChangeDetectionStrategy.OnPush
})
export class RelativeDateFormatterExample {
    private readonly adapter = inject<DateAdapter<DateTime>>(DateAdapter);
    private readonly dateFormatter = inject<DateFormatter<DateTime>>(DateFormatter);
    private readonly changeDetectorRef = inject(ChangeDetectorRef);
    private readonly localeService = inject(KBQ_LOCALE_SERVICE);

    formats = {
        relative: {
            long: {
                beforeYesterdayNotCurrentYear: '',

                beforeYesterdayCurrentYear: '',
                beforeYesterdayCurrentYearSeconds: '',
                beforeYesterdayCurrentYearMilliseconds: '',

                yesterday: '',
                yesterdaySeconds: '',
                yesterdayMilliseconds: '',

                today: '',
                todaySeconds: '',
                todayMilliseconds: '',

                tomorrow: '',
                tomorrowSeconds: '',
                tomorrowMilliseconds: '',

                afterTomorrowCurrentYear: '',
                afterTomorrowCurrentYearSeconds: '',
                afterTomorrowCurrentYearMilliseconds: '',

                afterTomorrowNotCurrentYear: ''
            },
            short: {
                beforeYesterdayNotCurrentYear: '',

                beforeYesterdayCurrentYear: '',
                beforeYesterdayCurrentYearSeconds: '',
                beforeYesterdayCurrentYearMilliseconds: '',

                yesterday: '',
                yesterdaySeconds: '',
                yesterdayMilliseconds: '',

                today: '',
                todaySeconds: '',
                todayMilliseconds: '',

                tomorrow: '',
                tomorrowSeconds: '',
                tomorrowMilliseconds: '',

                afterTomorrowCurrentYear: '',
                afterTomorrowCurrentYearSeconds: '',
                afterTomorrowCurrentYearMilliseconds: '',

                afterTomorrowNotCurrentYear: ''
            }
        }
    };

    constructor() {
        this.localeService.changes.pipe(distinctUntilChanged(), delay(0)).subscribe(this.onLocaleChange);
    }

    private onLocaleChange = (locale: string) => {
        this.adapter.setLocale(locale);

        this.populateRelativeLong(locale);
        this.populateRelativeShort(locale);

        this.changeDetectorRef.markForCheck();
    };

    private populateRelativeShort(locale: string) {
        this.dateFormatter.setLocale(locale);
        this.adapter.setLocale(locale);

        const short = this.formats.relative.short;
        const now = this.adapter.today();

        short.beforeYesterdayNotCurrentYear = this.dateFormatter.relativeShortDate(now.minus({ years: 1, days: 2 }));
        short.beforeYesterdayCurrentYear = this.dateFormatter.relativeShortDate(now.minus({ days: 2 }));
        short.beforeYesterdayCurrentYearSeconds = this.dateFormatter.relativeShortDateTime(now.minus({ days: 2 }), {
            seconds: true
        });
        short.beforeYesterdayCurrentYearMilliseconds = this.dateFormatter.relativeShortDateTime(
            now.minus({ days: 2 }),
            { milliseconds: true }
        );

        short.yesterday = this.dateFormatter.relativeShortDate(now.minus({ days: 1 }));
        short.yesterdaySeconds = this.dateFormatter.relativeShortDateTime(now.minus({ days: 1 }), { seconds: true });
        short.yesterdayMilliseconds = this.dateFormatter.relativeShortDateTime(now.minus({ days: 1 }), {
            milliseconds: true
        });

        short.today = this.dateFormatter.relativeShortDate(now.minus({ hours: 1 }));
        short.todaySeconds = this.dateFormatter.relativeShortDateTime(now.minus({ hours: 1 }), { seconds: true });
        short.todayMilliseconds = this.dateFormatter.relativeShortDateTime(now.minus({ hours: 1 }), {
            milliseconds: true
        });

        short.tomorrow = this.dateFormatter.relativeShortDate(now.plus({ days: 1, hours: 1 }));
        short.tomorrowSeconds = this.dateFormatter.relativeShortDateTime(now.plus({ days: 1, hours: 1 }), {
            seconds: true
        });
        short.tomorrowMilliseconds = this.dateFormatter.relativeShortDateTime(now.plus({ days: 1, hours: 1 }), {
            milliseconds: true
        });

        short.afterTomorrowCurrentYear = this.dateFormatter.relativeShortDate(now.plus({ days: 2 }));
        short.afterTomorrowCurrentYearSeconds = this.dateFormatter.relativeShortDateTime(now.plus({ days: 2 }), {
            seconds: true
        });
        short.afterTomorrowCurrentYearMilliseconds = this.dateFormatter.relativeShortDateTime(now.plus({ days: 2 }), {
            milliseconds: true
        });

        short.afterTomorrowNotCurrentYear = this.dateFormatter.relativeShortDate(now.plus({ years: 1, days: 2 }));
    }

    private populateRelativeLong(locale: string) {
        this.dateFormatter.setLocale(locale);
        this.adapter.setLocale(locale);

        const long = this.formats.relative.long;
        const now = this.adapter.today();

        long.beforeYesterdayNotCurrentYear = this.dateFormatter.relativeLongDate(now.minus({ years: 1, days: 2 }));
        long.beforeYesterdayCurrentYear = this.dateFormatter.relativeLongDate(now.minus({ days: 2 }));
        long.beforeYesterdayCurrentYearSeconds = this.dateFormatter.relativeLongDateTime(now.minus({ days: 2 }), {
            seconds: true
        });
        long.beforeYesterdayCurrentYearMilliseconds = this.dateFormatter.relativeLongDateTime(now.minus({ days: 2 }), {
            milliseconds: true
        });

        long.yesterday = this.dateFormatter.relativeLongDate(now.minus({ days: 1 }));
        long.yesterdaySeconds = this.dateFormatter.relativeLongDateTime(now.minus({ days: 1 }), { seconds: true });
        long.yesterdayMilliseconds = this.dateFormatter.relativeLongDateTime(now.minus({ days: 1 }), {
            milliseconds: true
        });

        long.today = this.dateFormatter.relativeLongDate(now.minus({ hours: 1 }));
        long.todaySeconds = this.dateFormatter.relativeLongDateTime(now.minus({ hours: 1 }), { seconds: true });
        long.todayMilliseconds = this.dateFormatter.relativeLongDateTime(now.minus({ hours: 1 }), {
            milliseconds: true
        });

        long.tomorrow = this.dateFormatter.relativeLongDate(now.plus({ days: 1, hours: 1 }));
        long.tomorrowSeconds = this.dateFormatter.relativeLongDateTime(now.plus({ days: 1, hours: 1 }), {
            seconds: true
        });
        long.tomorrowMilliseconds = this.dateFormatter.relativeLongDateTime(now.plus({ days: 1, hours: 1 }), {
            milliseconds: true
        });

        long.afterTomorrowCurrentYear = this.dateFormatter.relativeLongDate(now.plus({ days: 2 }));
        long.afterTomorrowCurrentYearSeconds = this.dateFormatter.relativeLongDateTime(now.plus({ days: 2 }), {
            seconds: true
        });
        long.afterTomorrowCurrentYearMilliseconds = this.dateFormatter.relativeLongDateTime(now.plus({ days: 2 }), {
            milliseconds: true
        });

        long.afterTomorrowNotCurrentYear = this.dateFormatter.relativeLongDate(now.plus({ years: 1, days: 2 }));
    }
}
```

```html relative-date-formatter-example.html
<div class="kbq-text-big">
    <div>
        <h3>Long format</h3>
        <div>
            <div class="layout-row example-row-border">
                <div class="flex example-light-text-secondary">Name</div>
                <div class="flex example-light-text-secondary">Value</div>
            </div>

            <div class="layout-row example-row-border">
                <div class="flex">Before yesterday (not current year)</div>
                <div class="flex">{{ formats.relative.long.beforeYesterdayNotCurrentYear }}</div>
            </div>
            <div class="example-row-border"></div>

            <div class="layout-row example-row-border">
                <div class="flex">Before yesterday (current year)</div>
                <div class="flex">{{ formats.relative.long.beforeYesterdayCurrentYear }}</div>
            </div>

            <div class="layout-row example-row-border">
                <div class="flex">Before yesterday (current year) (with seconds)</div>
                <div class="flex">{{ formats.relative.long.beforeYesterdayCurrentYearSeconds }}</div>
            </div>

            <div class="layout-row example-row-border">
                <div class="flex">Before yesterday (current year) (with milliseconds)</div>
                <div class="flex">{{ formats.relative.long.beforeYesterdayCurrentYearMilliseconds }}</div>
            </div>
            <div class="example-row-border"></div>

            <div class="layout-row example-row-border">
                <div class="flex">Yesterday</div>
                <div class="flex">{{ formats.relative.long.yesterday }}</div>
            </div>

            <div class="layout-row example-row-border">
                <div class="flex">Yesterday (with seconds)</div>
                <div class="flex">{{ formats.relative.long.yesterdaySeconds }}</div>
            </div>

            <div class="layout-row example-row-border">
                <div class="flex">Yesterday (with milliseconds)</div>
                <div class="flex">{{ formats.relative.long.yesterdayMilliseconds }}</div>
            </div>
            <div class="example-row-border"></div>

            <div class="layout-row example-row-border">
                <div class="flex">Today</div>
                <div class="flex">{{ formats.relative.long.today }}</div>
            </div>

            <div class="layout-row example-row-border">
                <div class="flex">Today (with seconds)</div>
                <div class="flex">{{ formats.relative.long.todaySeconds }}</div>
            </div>

            <div class="layout-row example-row-border">
                <div class="flex">Today (with milliseconds)</div>
                <div class="flex">{{ formats.relative.long.todayMilliseconds }}</div>
            </div>
            <div class="example-row-border"></div>

            <div class="layout-row example-row-border">
                <div class="flex">Tomorrow</div>
                <div class="flex">{{ formats.relative.long.tomorrow }}</div>
            </div>

            <div class="layout-row example-row-border">
                <div class="flex">Tomorrow (with seconds)</div>
                <div class="flex">{{ formats.relative.long.tomorrowSeconds }}</div>
            </div>

            <div class="layout-row example-row-border">
                <div class="flex">Tomorrow (with milliseconds)</div>
                <div class="flex">{{ formats.relative.long.tomorrowMilliseconds }}</div>
            </div>
            <div class="example-row-border"></div>

            <div class="layout-row example-row-border">
                <div class="flex">After tomorrow (current year)</div>
                <div class="flex">{{ formats.relative.long.afterTomorrowCurrentYear }}</div>
            </div>

            <div class="layout-row example-row-border">
                <div class="flex">After tomorrow (current year) (with seconds)</div>
                <div class="flex">{{ formats.relative.long.afterTomorrowCurrentYearSeconds }}</div>
            </div>

            <div class="layout-row example-row-border">
                <div class="flex">After tomorrow (current year) (with milliseconds)</div>
                <div class="flex">{{ formats.relative.long.afterTomorrowCurrentYearMilliseconds }}</div>
            </div>
            <div class="example-row-border"></div>

            <div class="layout-row example-row-border">
                <div class="flex">After tomorrow (not current year)</div>
                <div class="flex">{{ formats.relative.long.afterTomorrowNotCurrentYear }}</div>
            </div>
        </div>
    </div>
    <div>
        <h3>Short format</h3>
        <div>
            <div class="layout-row example-row-border">
                <div class="flex example-light-text-secondary">Name</div>
                <div class="flex example-light-text-secondary">Value</div>
            </div>

            <div class="layout-row example-row-border">
                <div class="flex">Before yesterday (not current year)</div>
                <div class="flex">{{ formats.relative.short.beforeYesterdayNotCurrentYear }}</div>
            </div>
            <div class="example-row-border"></div>

            <div class="layout-row example-row-border">
                <div class="flex">Before yesterday (current year)</div>
                <div class="flex">{{ formats.relative.short.beforeYesterdayCurrentYear }}</div>
            </div>

            <div class="layout-row example-row-border">
                <div class="flex">Before yesterday (current year) (with seconds)</div>
                <div class="flex">{{ formats.relative.short.beforeYesterdayCurrentYearSeconds }}</div>
            </div>

            <div class="layout-row example-row-border">
                <div class="flex">Before yesterday (current year) (with milliseconds)</div>
                <div class="flex">{{ formats.relative.short.beforeYesterdayCurrentYearMilliseconds }}</div>
            </div>
            <div class="example-row-border"></div>

            <div class="layout-row example-row-border">
                <div class="flex">Yesterday</div>
                <div class="flex">{{ formats.relative.short.yesterday }}</div>
            </div>

            <div class="layout-row example-row-border">
                <div class="flex">Yesterday (with seconds)</div>
                <div class="flex">{{ formats.relative.short.yesterdaySeconds }}</div>
            </div>

            <div class="layout-row example-row-border">
                <div class="flex">Yesterday (with milliseconds)</div>
                <div class="flex">{{ formats.relative.short.yesterdayMilliseconds }}</div>
            </div>
            <div class="example-row-border"></div>

            <div class="layout-row example-row-border">
                <div class="flex">Today</div>
                <div class="flex">{{ formats.relative.short.today }}</div>
            </div>

            <div class="layout-row example-row-border">
                <div class="flex">Today (with seconds)</div>
                <div class="flex">{{ formats.relative.short.todaySeconds }}</div>
            </div>

            <div class="layout-row example-row-border">
                <div class="flex">Today (with milliseconds)</div>
                <div class="flex">{{ formats.relative.short.todayMilliseconds }}</div>
            </div>
            <div class="example-row-border"></div>

            <div class="layout-row example-row-border">
                <div class="flex">Tomorrow</div>
                <div class="flex">{{ formats.relative.short.tomorrow }}</div>
            </div>

            <div class="layout-row example-row-border">
                <div class="flex">Tomorrow (with seconds)</div>
                <div class="flex">{{ formats.relative.short.tomorrowSeconds }}</div>
            </div>

            <div class="layout-row example-row-border">
                <div class="flex">Tomorrow (with milliseconds)</div>
                <div class="flex">{{ formats.relative.short.tomorrowMilliseconds }}</div>
            </div>
            <div class="example-row-border"></div>

            <div class="layout-row example-row-border">
                <div class="flex">After tomorrow (current year)</div>
                <div class="flex">{{ formats.relative.short.afterTomorrowCurrentYear }}</div>
            </div>

            <div class="layout-row example-row-border">
                <div class="flex">After tomorrow (current year) (with seconds)</div>
                <div class="flex">{{ formats.relative.short.afterTomorrowCurrentYearSeconds }}</div>
            </div>

            <div class="layout-row example-row-border">
                <div class="flex">After tomorrow (current year) (with milliseconds)</div>
                <div class="flex">{{ formats.relative.short.afterTomorrowCurrentYearMilliseconds }}</div>
            </div>
            <div class="example-row-border"></div>

            <div class="layout-row example-row-border">
                <div class="flex">After tomorrow (not current year)</div>
                <div class="flex">{{ formats.relative.short.afterTomorrowNotCurrentYear }}</div>
            </div>
        </div>
    </div>
</div>
```

```css relative-date-formatter-example.css
.example-light-text-secondary {
    color: #ccc;
}

.example-row-border {
    padding: 8px;
    border-bottom: 1px solid #ccc;
}
```

#### Date range

**Example: Range date-formatter** (`range-date-formatter`)

```ts range-date-formatter-example.ts
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, inject } from '@angular/core';
import { LuxonDateModule } from '@koobiq/angular-luxon-adapter/adapter';
import { DateAdapter, DateFormatter, KBQ_LOCALE_SERVICE, KbqFormattersModule } from '@koobiq/components/core';
import { DateTime } from 'luxon';
import { delay } from 'rxjs';
import { distinctUntilChanged } from 'rxjs/operators';

@Component({
    selector: 'range-date-formatter-example',
    imports: [LuxonDateModule, KbqFormattersModule],
    templateUrl: 'range-date-formatter-example.html',
    styleUrls: ['range-date-formatter-example.css'],
    changeDetection: ChangeDetectionStrategy.OnPush
})
export class RangeDateFormatterExample {
    private readonly adapter = inject<DateAdapter<DateTime>>(DateAdapter);
    private readonly dateFormatter = inject<DateFormatter<DateTime>>(DateFormatter);
    private readonly changeDetectorRef = inject(ChangeDetectorRef);
    private readonly localeService = inject(KBQ_LOCALE_SERVICE);

    formats = {
        range: {
            long: {
                date: {
                    currentMonth: '',
                    notCurrentYear: '',
                    startsNotCurrentYear: '',
                    endsNotCurrentYear: ''
                },
                dateTime: {
                    startsNotCurrentYear: '',
                    startsNotCurrentYearSeconds: '',
                    startsNotCurrentYearMilliseconds: '',

                    endsNotCurrentYear: '',
                    endsNotCurrentYearSeconds: '',
                    endsNotCurrentYearMilliseconds: '',

                    sameDateCurrentYear: '',
                    sameDateCurrentYearSeconds: '',
                    sameDateCurrentYearMilliseconds: '',

                    sameDateNotCurrentYear: '',
                    sameDateNotCurrentYearSeconds: '',
                    sameDateNotCurrentYearMilliseconds: '',

                    notCurrentMonth: '',
                    notCurrentMonthSeconds: '',
                    notCurrentMonthMilliseconds: ''
                }
            },
            middle: {
                dateTime: {
                    currentYear: '',
                    currentYearSeconds: '',
                    currentYearMilliseconds: '',

                    sameDateCurrentYear: '',
                    sameDateCurrentYearSeconds: '',
                    sameDateCurrentYearMilliseconds: '',

                    sameDateNotCurrentYear: '',
                    sameDateNotCurrentYearSeconds: '',
                    sameDateNotCurrentYearMilliseconds: '',

                    notCurrentMonth: '',
                    notCurrentMonthSeconds: '',
                    notCurrentMonthMilliseconds: '',

                    startsNotCurrentYear: '',
                    startsNotCurrentYearSeconds: '',
                    startsNotCurrentYearMilliseconds: '',

                    endsNotCurrentYear: '',
                    endsNotCurrentYearSeconds: '',
                    endsNotCurrentYearMilliseconds: ''
                }
            },
            short: {
                date: {
                    currentMonth: '',
                    notCurrentYear: '',
                    startsNotCurrentYear: '',
                    endsNotCurrentYear: ''
                },
                dateTime: {
                    sameDateCurrentYear: '',
                    sameDateCurrentYearSeconds: '',
                    sameDateCurrentYearMilliseconds: '',

                    sameDateNotCurrentYear: '',
                    sameDateNotCurrentYearSeconds: '',
                    sameDateNotCurrentYearMilliseconds: '',

                    notCurrentMonth: '',
                    notCurrentMonthSeconds: '',
                    notCurrentMonthMilliseconds: '',

                    startsNotCurrentYear: '',
                    startsNotCurrentYearSeconds: '',
                    startsNotCurrentYearMilliseconds: '',

                    endsNotCurrentYear: '',
                    endsNotCurrentYearSeconds: '',
                    endsNotCurrentYearMilliseconds: ''
                }
            }
        },
        openedRange: {
            long: {
                date: {
                    onlyStart: '',
                    onlyStartNotCurrentYear: '',
                    onlyEnd: '',
                    onlyEndNotCurrentYear: ''
                },
                dateTime: {
                    onlyStart: '',
                    onlyStartSeconds: '',
                    onlyStartMilliseconds: '',

                    onlyStartNotCurrentYear: '',
                    onlyStartNotCurrentYearSeconds: '',
                    onlyStartNotCurrentYearMilliseconds: '',

                    onlyEnd: '',
                    onlyEndSeconds: '',
                    onlyEndMilliseconds: '',

                    onlyEndNotCurrentYear: '',
                    onlyEndNotCurrentYearSeconds: '',
                    onlyEndNotCurrentYearMilliseconds: ''
                }
            },
            short: {
                date: {
                    onlyStart: '',
                    onlyStartNotCurrentYear: '',
                    onlyEnd: '',
                    onlyEndNotCurrentYear: ''
                },
                dateTime: {
                    onlyStart: '',
                    onlyStartSeconds: '',
                    onlyStartMilliseconds: '',

                    onlyStartNotCurrentYear: '',
                    onlyStartNotCurrentYearSeconds: '',
                    onlyStartNotCurrentYearMilliseconds: '',

                    onlyEnd: '',
                    onlyEndSeconds: '',
                    onlyEndMilliseconds: '',

                    onlyEndNotCurrentYear: '',
                    onlyEndNotCurrentYearSeconds: '',
                    onlyEndNotCurrentYearMilliseconds: ''
                }
            }
        }
    };

    constructor() {
        this.localeService.changes.pipe(distinctUntilChanged(), delay(0)).subscribe(this.onLocaleChange);
    }

    private onLocaleChange = (locale: string) => {
        this.adapter.setLocale(locale);

        this.populateRangeLong(locale);
        this.populateRangeMiddle(locale);
        this.populateRangeShort(locale);
        this.populateOpenedRangeLong(locale);
        this.populateOpenedRangeShort(locale);

        this.changeDetectorRef.markForCheck();
    };

    private populateRangeShort(locale: string) {
        this.dateFormatter.setLocale(locale);
        this.adapter.setLocale(locale);

        const short = this.formats.range.short;
        const now = this.adapter.today();

        short.date.currentMonth = this.dateFormatter.rangeShortDate(now.set({ day: 1 }), now.set({ day: 10 }));
        short.date.notCurrentYear = this.dateFormatter.rangeShortDate(
            now.set({ day: 1, month: 1 }),
            now.set({ day: 10, month: 2 })
        );
        short.date.startsNotCurrentYear = this.dateFormatter.rangeShortDate(
            now.set({ day: 1, month: 1 }).minus({ years: 1 }),
            now.set({ day: 10, month: 2 })
        );
        short.date.endsNotCurrentYear = this.dateFormatter.rangeShortDate(
            now.set({ day: 1, month: 1 }),
            now.set({ day: 10, month: 2 }).plus({ years: 1 })
        );

        short.dateTime.sameDateCurrentYear = this.dateFormatter.rangeShortDateTime(
            now.set({ day: 10, hour: 10, minute: 14 }),
            now.set({ day: 10, hour: 11, minute: 28 })
        );
        short.dateTime.sameDateCurrentYearSeconds = this.dateFormatter.rangeShortDateTime(
            now.set({ day: 10, hour: 10, minute: 14 }),
            now.set({ day: 10, hour: 11, minute: 28 }),
            { seconds: true }
        );
        short.dateTime.sameDateCurrentYearMilliseconds = this.dateFormatter.rangeShortDateTime(
            now.set({ day: 10, hour: 10, minute: 14 }),
            now.set({ day: 10, hour: 11, minute: 28 }),
            { milliseconds: true }
        );

        short.dateTime.sameDateNotCurrentYear = this.dateFormatter.rangeShortDateTime(
            now.set({ month: 1, day: 11 }).minus({ years: 1 }).set({ hour: 10, minute: 14 }),
            now.set({ month: 1, day: 11 }).minus({ years: 1 }).set({ hour: 11, minute: 28 })
        );
        short.dateTime.sameDateNotCurrentYearSeconds = this.dateFormatter.rangeShortDateTime(
            now.set({ month: 1, day: 11 }).minus({ years: 1 }).set({ hour: 10, minute: 14 }),
            now.set({ month: 1, day: 11 }).minus({ years: 1 }).set({ hour: 11, minute: 28 }),
            { seconds: true }
        );
        short.dateTime.sameDateNotCurrentYearMilliseconds = this.dateFormatter.rangeShortDateTime(
            now.set({ month: 1, day: 11 }).minus({ years: 1 }).set({ hour: 10, minute: 14 }),
            now.set({ month: 1, day: 11 }).minus({ years: 1 }).set({ hour: 11, minute: 28 }),
            { milliseconds: true }
        );

        short.dateTime.notCurrentMonth = this.dateFormatter.rangeShortDateTime(
            now.set({ month: 1, day: 1, hour: 10, minute: 14 }),
            now.set({ month: 2, day: 1, hour: 11, minute: 28 })
        );
        short.dateTime.notCurrentMonthSeconds = this.dateFormatter.rangeShortDateTime(
            now.set({ month: 1, day: 1, hour: 10, minute: 14 }),
            now.set({ month: 2, day: 1, hour: 11, minute: 28 }),
            { seconds: true }
        );
        short.dateTime.notCurrentMonthMilliseconds = this.dateFormatter.rangeShortDateTime(
            now.set({ month: 1, day: 1, hour: 10, minute: 14 }),
            now.set({ month: 2, day: 1, hour: 11, minute: 28 }),
            { milliseconds: true }
        );

        short.dateTime.startsNotCurrentYear = this.dateFormatter.rangeShortDateTime(
            now.set({ month: 1, day: 1 }).minus({ years: 1 }).set({ hour: 10, minute: 14 }),
            now.set({ month: 2, day: 1, hour: 11, minute: 28 })
        );
        short.dateTime.startsNotCurrentYearSeconds = this.dateFormatter.rangeShortDateTime(
            now.set({ month: 1, day: 1 }).minus({ years: 1 }).set({ hour: 10, minute: 14 }),
            now.set({ month: 2, day: 1, hour: 11, minute: 28 }),
            { seconds: true }
        );
        short.dateTime.startsNotCurrentYearMilliseconds = this.dateFormatter.rangeShortDateTime(
            now.set({ month: 1, day: 1 }).minus({ years: 1 }).set({ hour: 10, minute: 14 }),
            now.set({ month: 2, day: 1, hour: 11, minute: 28 }),
            { milliseconds: true }
        );

        short.dateTime.endsNotCurrentYear = this.dateFormatter.rangeShortDateTime(
            now.set({ month: 1, day: 1, hour: 10, minute: 14 }),
            now.set({ day: 1, month: 2 }).plus({ years: 1 }).set({ hour: 11, minute: 28 })
        );
        short.dateTime.endsNotCurrentYearSeconds = this.dateFormatter.rangeShortDateTime(
            now.set({ month: 1, day: 1, hour: 10, minute: 14 }),
            now.set({ day: 1, month: 2 }).plus({ years: 1 }).set({ hour: 11, minute: 28 }),
            { seconds: true }
        );
        short.dateTime.endsNotCurrentYearMilliseconds = this.dateFormatter.rangeShortDateTime(
            now.set({ month: 1, day: 1, hour: 10, minute: 14 }),
            now.set({ day: 1, month: 2 }).plus({ years: 1 }).set({ hour: 11, minute: 28 }),
            { milliseconds: true }
        );
    }

    private populateRangeMiddle(locale: string) {
        this.dateFormatter.setLocale(locale);
        this.adapter.setLocale(locale);

        const middle = this.formats.range.middle;
        const now = this.adapter.today();

        middle.dateTime.currentYear = this.dateFormatter.rangeMiddleDateTime(now.set({ day: 1 }), now.set({ day: 10 }));

        middle.dateTime.currentYearSeconds = this.dateFormatter.rangeMiddleDateTime(
            now.set({ day: 1 }),
            now.set({ day: 10 }),
            { seconds: true }
        );

        middle.dateTime.currentYearMilliseconds = this.dateFormatter.rangeMiddleDateTime(
            now.set({ day: 1 }),
            now.set({ day: 10 }),
            { milliseconds: true }
        );

        middle.dateTime.sameDateCurrentYear = this.dateFormatter.rangeMiddleDateTime(
            now.set({ day: 10, hour: 10, minute: 14 }),
            now.set({ day: 10, hour: 10, minute: 28 })
        );
        middle.dateTime.sameDateCurrentYearSeconds = this.dateFormatter.rangeMiddleDateTime(
            now.set({ day: 10, hour: 10, minute: 14 }),
            now.set({ day: 10, hour: 10, minute: 28 }),
            { seconds: true }
        );
        middle.dateTime.sameDateCurrentYearMilliseconds = this.dateFormatter.rangeMiddleDateTime(
            now.set({ day: 10, hour: 10, minute: 14 }),
            now.set({ day: 10, hour: 10, minute: 28 }),
            { milliseconds: true }
        );

        middle.dateTime.sameDateNotCurrentYear = this.dateFormatter.rangeMiddleDateTime(
            now.set({ month: 1, day: 11 }).minus({ years: 1 }).set({ hour: 10, minute: 14 }),
            now.set({ month: 1, day: 11 }).minus({ years: 1 }).set({ hour: 11, minute: 28 })
        );
        middle.dateTime.sameDateNotCurrentYearSeconds = this.dateFormatter.rangeMiddleDateTime(
            now.set({ month: 1, day: 11 }).minus({ years: 1 }).set({ hour: 10, minute: 14 }),
            now.set({ month: 1, day: 11 }).minus({ years: 1 }).set({ hour: 11, minute: 28 }),
            { seconds: true }
        );
        middle.dateTime.sameDateNotCurrentYearMilliseconds = this.dateFormatter.rangeMiddleDateTime(
            now.set({ month: 1, day: 11 }).minus({ years: 1 }).set({ hour: 10, minute: 14 }),
            now.set({ month: 1, day: 11 }).minus({ years: 1 }).set({ hour: 11, minute: 28 }),
            { milliseconds: true }
        );

        middle.dateTime.notCurrentMonth = this.dateFormatter.rangeMiddleDateTime(
            now.set({ month: 1, day: 1, hour: 10, minute: 14 }),
            now.set({ month: 2, day: 1, hour: 11, minute: 28 })
        );
        middle.dateTime.notCurrentMonthSeconds = this.dateFormatter.rangeMiddleDateTime(
            now.set({ month: 1, day: 1, hour: 10, minute: 14 }),
            now.set({ month: 2, day: 1, hour: 11, minute: 28 }),
            { seconds: true }
        );
        middle.dateTime.notCurrentMonthMilliseconds = this.dateFormatter.rangeMiddleDateTime(
            now.set({ month: 1, day: 1, hour: 10, minute: 14 }),
            now.set({ month: 2, day: 1, hour: 11, minute: 28 }),
            { milliseconds: true }
        );

        middle.dateTime.startsNotCurrentYear = this.dateFormatter.rangeMiddleDateTime(
            now.set({ month: 1, day: 1 }).minus({ years: 1 }).set({ hour: 10, minute: 14 }),
            now.set({ month: 1, day: 1, hour: 11, minute: 28 })
        );
        middle.dateTime.startsNotCurrentYearSeconds = this.dateFormatter.rangeMiddleDateTime(
            now.set({ month: 1, day: 1 }).minus({ years: 1 }).set({ hour: 10, minute: 14 }),
            now.set({ month: 1, day: 1, hour: 11, minute: 28 }),
            { seconds: true }
        );
        middle.dateTime.startsNotCurrentYearMilliseconds = this.dateFormatter.rangeMiddleDateTime(
            now.set({ month: 1, day: 1 }).minus({ years: 1 }).set({ hour: 10, minute: 14 }),
            now.set({ month: 1, day: 1, hour: 11, minute: 28 }),
            { milliseconds: true }
        );

        middle.dateTime.endsNotCurrentYear = this.dateFormatter.rangeMiddleDateTime(
            now.set({ month: 1, day: 1, hour: 10, minute: 14 }),
            now.set({ month: 1, day: 1 }).plus({ years: 1 }).set({ hour: 11, minute: 28 })
        );
        middle.dateTime.endsNotCurrentYearSeconds = this.dateFormatter.rangeMiddleDateTime(
            now.set({ month: 1, day: 1, hour: 10, minute: 14 }),
            now.set({ month: 1, day: 1 }).plus({ years: 1 }).set({ hour: 11, minute: 28 }),
            { seconds: true }
        );
        middle.dateTime.endsNotCurrentYearMilliseconds = this.dateFormatter.rangeMiddleDateTime(
            now.set({ month: 1, day: 1, hour: 10, minute: 14 }),
            now.set({ month: 1, day: 1 }).plus({ years: 1 }).set({ hour: 11, minute: 28 }),
            { milliseconds: true }
        );
    }

    private populateRangeLong(locale: string) {
        this.dateFormatter.setLocale(locale);
        this.adapter.setLocale(locale);

        const long = this.formats.range.long;
        const now = this.adapter.today();

        long.date.currentMonth = this.dateFormatter.rangeLongDate(now.set({ day: 1 }), now.set({ day: 10 }));
        long.date.notCurrentYear = this.dateFormatter.rangeLongDate(
            now.set({ month: 1, day: 1 }),
            now.set({ month: 2, day: 10 })
        );
        long.date.startsNotCurrentYear = this.dateFormatter.rangeLongDate(
            now.set({ month: 1, day: 1 }).minus({ years: 1 }),
            now.set({ month: 2, day: 10 })
        );
        long.date.endsNotCurrentYear = this.dateFormatter.rangeLongDate(
            now.set({ month: 1, day: 1 }),
            now.set({ month: 2, day: 10 }).plus({ years: 1 })
        );
        long.dateTime.sameDateCurrentYear = this.dateFormatter.rangeLongDateTime(
            now.set({ day: 10, hour: 10, minute: 14 }),
            now.set({ day: 10, hour: 11, minute: 28 })
        );
        long.dateTime.sameDateCurrentYearSeconds = this.dateFormatter.rangeLongDateTime(
            now.set({ day: 10, hour: 10, minute: 14 }),
            now.set({ day: 10, hour: 11, minute: 28 }),
            { seconds: true }
        );
        long.dateTime.sameDateCurrentYearMilliseconds = this.dateFormatter.rangeLongDateTime(
            now.set({ day: 10, hour: 10, minute: 14 }),
            now.set({ day: 10, hour: 11, minute: 28 }),
            { milliseconds: true }
        );

        long.dateTime.sameDateNotCurrentYear = this.dateFormatter.rangeLongDateTime(
            now.set({ month: 1, day: 11 }).minus({ years: 1 }).set({ hour: 10, minute: 14 }),
            now.set({ month: 1, day: 11 }).minus({ years: 1 }).set({ hour: 11, minute: 28 })
        );
        long.dateTime.sameDateNotCurrentYearSeconds = this.dateFormatter.rangeLongDateTime(
            now.set({ month: 1, day: 11 }).minus({ years: 1 }).set({ hour: 10, minute: 14 }),
            now.set({ month: 1, day: 11 }).minus({ years: 1 }).set({ hour: 11, minute: 28 }),
            { seconds: true }
        );
        long.dateTime.sameDateNotCurrentYearMilliseconds = this.dateFormatter.rangeLongDateTime(
            now.set({ month: 1, day: 11 }).minus({ years: 1 }).set({ hour: 10, minute: 14 }),
            now.set({ month: 1, day: 11 }).minus({ years: 1 }).set({ hour: 11, minute: 28 }),
            { milliseconds: true }
        );

        long.dateTime.notCurrentMonth = this.dateFormatter.rangeLongDateTime(
            now.set({ month: 1, day: 1, hour: 10, minute: 14 }),
            now.set({ month: 2, day: 1, hour: 11, minute: 28 })
        );
        long.dateTime.notCurrentMonthSeconds = this.dateFormatter.rangeLongDateTime(
            now.set({ month: 1, day: 1, hour: 10, minute: 14 }),
            now.set({ month: 2, day: 1, hour: 11, minute: 28 }),
            { seconds: true }
        );
        long.dateTime.notCurrentMonthMilliseconds = this.dateFormatter.rangeLongDateTime(
            now.set({ month: 1, day: 1, hour: 10, minute: 14 }),
            now.set({ month: 2, day: 1, hour: 11, minute: 28 }),
            { milliseconds: true }
        );

        long.dateTime.startsNotCurrentYear = this.dateFormatter.rangeLongDateTime(
            now.set({ month: 1, day: 1 }).minus({ years: 1 }).set({ hour: 10, minute: 14 }),
            now.set({ month: 2, day: 1, hour: 11, minute: 28 })
        );
        long.dateTime.startsNotCurrentYearSeconds = this.dateFormatter.rangeLongDateTime(
            now.set({ month: 1, day: 1 }).minus({ years: 1 }).set({ hour: 10, minute: 14 }),
            now.set({ month: 2, day: 1, hour: 11, minute: 28 }),
            { seconds: true }
        );
        long.dateTime.startsNotCurrentYearMilliseconds = this.dateFormatter.rangeLongDateTime(
            now.set({ month: 1, day: 1 }).minus({ years: 1 }).set({ hour: 10, minute: 14 }),
            now.set({ month: 2, day: 1, hour: 11, minute: 28 }),
            { milliseconds: true }
        );

        long.dateTime.endsNotCurrentYear = this.dateFormatter.rangeLongDateTime(
            now.set({ month: 1, day: 1, hour: 10, minute: 14 }),
            now.set({ month: 2, day: 1 }).minus({ years: 1 }).set({ hour: 11, minute: 28 })
        );
        long.dateTime.endsNotCurrentYearSeconds = this.dateFormatter.rangeLongDateTime(
            now.set({ month: 1, day: 1, hour: 10, minute: 14 }),
            now.set({ month: 2, day: 1 }).minus({ years: 1 }).set({ hour: 11, minute: 28 }),
            { seconds: true }
        );
        long.dateTime.endsNotCurrentYearMilliseconds = this.dateFormatter.rangeLongDateTime(
            now.set({ month: 1, day: 1, hour: 10, minute: 14 }),
            now.set({ month: 2, day: 1 }).minus({ years: 1 }).set({ hour: 11, minute: 28 }),
            { milliseconds: true }
        );
    }

    private populateOpenedRangeLong(locale: string) {
        this.dateFormatter.setLocale(locale);

        const longOpenedRange = this.formats.openedRange.long;
        const now = this.adapter.today();
        const formatterTemplate = this.dateFormatter.config.rangeTemplates.openedRange.long;

        longOpenedRange.date.onlyStart = this.dateFormatter.openedRangeDate(now, null, formatterTemplate);
        longOpenedRange.date.onlyEnd = this.dateFormatter.openedRangeDate(null, now, formatterTemplate);
        longOpenedRange.date.onlyStartNotCurrentYear = this.dateFormatter.openedRangeDate(
            now.plus({ years: 1 }),
            null,
            formatterTemplate
        );
        longOpenedRange.date.onlyEndNotCurrentYear = this.dateFormatter.openedRangeDate(
            null,
            now.plus({ years: 1 }),
            formatterTemplate
        );

        longOpenedRange.dateTime.onlyStart = this.dateFormatter.openedRangeDateTime(now, null, formatterTemplate);
        longOpenedRange.dateTime.onlyStartSeconds = this.dateFormatter.openedRangeDateTime(
            now,
            null,
            formatterTemplate,
            true
        );
        longOpenedRange.dateTime.onlyStartMilliseconds = this.dateFormatter.openedRangeDateTime(
            now,
            null,
            formatterTemplate,
            false,
            true
        );

        longOpenedRange.dateTime.onlyStartNotCurrentYear = this.dateFormatter.openedRangeDateTime(
            now.plus({ years: 1 }),
            null,
            formatterTemplate
        );
        longOpenedRange.dateTime.onlyStartNotCurrentYearSeconds = this.dateFormatter.openedRangeDateTime(
            now.plus({ years: 1 }),
            null,
            formatterTemplate,
            true
        );
        longOpenedRange.dateTime.onlyStartNotCurrentYearMilliseconds = this.dateFormatter.openedRangeDateTime(
            now.plus({ years: 1 }),
            null,
            formatterTemplate,
            false,
            true
        );

        longOpenedRange.dateTime.onlyEnd = this.dateFormatter.openedRangeDateTime(null, now, formatterTemplate);
        longOpenedRange.dateTime.onlyEndSeconds = this.dateFormatter.openedRangeDateTime(
            null,
            now,
            formatterTemplate,
            true
        );
        longOpenedRange.dateTime.onlyEndMilliseconds = this.dateFormatter.openedRangeDateTime(
            null,
            now,
            formatterTemplate,
            false,
            true
        );

        longOpenedRange.dateTime.onlyEndNotCurrentYear = this.dateFormatter.openedRangeDateTime(
            null,
            now.plus({ years: 1 }),
            formatterTemplate
        );
        longOpenedRange.dateTime.onlyEndNotCurrentYearSeconds = this.dateFormatter.openedRangeDateTime(
            null,
            now.plus({ years: 1 }),
            formatterTemplate,
            true
        );
        longOpenedRange.dateTime.onlyEndNotCurrentYearMilliseconds = this.dateFormatter.openedRangeDateTime(
            null,
            now.plus({ years: 1 }),
            formatterTemplate,
            false,
            true
        );
    }

    private populateOpenedRangeShort(locale: string) {
        this.dateFormatter.setLocale(locale);

        const shortOpenedRange = this.formats.openedRange.short;
        const now = this.adapter.today();
        const formatterTemplate = this.dateFormatter.config.rangeTemplates.openedRange.short;

        shortOpenedRange.date.onlyStart = this.dateFormatter.openedRangeDate(now, null, formatterTemplate);
        shortOpenedRange.date.onlyEnd = this.dateFormatter.openedRangeDate(null, now, formatterTemplate);
        shortOpenedRange.date.onlyStartNotCurrentYear = this.dateFormatter.openedRangeDate(
            now.plus({ years: 1 }),
            null,
            formatterTemplate
        );
        shortOpenedRange.date.onlyEndNotCurrentYear = this.dateFormatter.openedRangeDate(
            null,
            now.plus({ years: 1 }),
            formatterTemplate
        );

        shortOpenedRange.dateTime.onlyStart = this.dateFormatter.openedRangeDateTime(now, null, formatterTemplate);
        shortOpenedRange.dateTime.onlyStartSeconds = this.dateFormatter.openedRangeDateTime(
            now,
            null,
            formatterTemplate,
            true
        );
        shortOpenedRange.dateTime.onlyStartMilliseconds = this.dateFormatter.openedRangeDateTime(
            now,
            null,
            formatterTemplate,
            false,
            true
        );

        shortOpenedRange.dateTime.onlyStartNotCurrentYear = this.dateFormatter.openedRangeDateTime(
            now.plus({ years: 1 }),
            null,
            formatterTemplate
        );
        shortOpenedRange.dateTime.onlyStartNotCurrentYearSeconds = this.dateFormatter.openedRangeDateTime(
            now.plus({ years: 1 }),
            null,
            formatterTemplate,
            true
        );
        shortOpenedRange.dateTime.onlyStartNotCurrentYearMilliseconds = this.dateFormatter.openedRangeDateTime(
            now.plus({ years: 1 }),
            null,
            formatterTemplate,
            false,
            true
        );

        shortOpenedRange.dateTime.onlyEnd = this.dateFormatter.openedRangeDateTime(null, now, formatterTemplate);
        shortOpenedRange.dateTime.onlyEndSeconds = this.dateFormatter.openedRangeDateTime(
            null,
            now,
            formatterTemplate,
            true
        );
        shortOpenedRange.dateTime.onlyEndMilliseconds = this.dateFormatter.openedRangeDateTime(
            null,
            now,
            formatterTemplate,
            false,
            true
        );

        shortOpenedRange.dateTime.onlyEndNotCurrentYear = this.dateFormatter.openedRangeDateTime(
            null,
            now.plus({ years: 1 }),
            formatterTemplate
        );
        shortOpenedRange.dateTime.onlyEndNotCurrentYearSeconds = this.dateFormatter.openedRangeDateTime(
            null,
            now.plus({ years: 1 }),
            formatterTemplate,
            true
        );
        shortOpenedRange.dateTime.onlyEndNotCurrentYearMilliseconds = this.dateFormatter.openedRangeDateTime(
            null,
            now.plus({ years: 1 }),
            formatterTemplate,
            false,
            true
        );
    }
}
```

```html range-date-formatter-example.html
<div class="kbq-text-big">
    <h2>Closed range</h2>
    <div>
        <h3>Long format</h3>
        <div>
            <div class="layout-row example-row-border">
                <div class="flex example-light-text-secondary">Name</div>
                <div class="flex example-light-text-secondary">Value</div>
            </div>
            <div class="layout-row example-row-border">
                <div class="flex">rangeLongDate (current month)</div>
                <div class="flex">{{ formats.range.long.date.currentMonth }}</div>
            </div>
            <div class="layout-row example-row-border">
                <div class="flex">rangeLongDate (not current month)</div>
                <div class="flex">{{ formats.range.long.date.notCurrentYear }}</div>
            </div>
            <div class="layout-row example-row-border">
                <div class="flex">rangeLongDate (start date is not in current year)</div>
                <div class="flex">{{ formats.range.long.date.startsNotCurrentYear }}</div>
            </div>
            <div class="layout-row example-row-border">
                <div class="flex">rangeLongDate (end date is not in current year)</div>
                <div class="flex">{{ formats.range.long.date.endsNotCurrentYear }}</div>
            </div>
            <div class="example-row-border"></div>

            <div class="layout-row example-row-border">
                <div class="flex">rangeLongDateTime (the same day, current year)</div>
                <div class="flex">{{ formats.range.long.dateTime.sameDateCurrentYear }}</div>
            </div>
            <div class="layout-row example-row-border">
                <div class="flex">rangeLongDateTime (the same day, current year) (with seconds)</div>
                <div class="flex">{{ formats.range.long.dateTime.sameDateCurrentYearSeconds }}</div>
            </div>
            <div class="layout-row example-row-border">
                <div class="flex">rangeLongDateTime (the same day, current year) (with milliseconds)</div>
                <div class="flex">{{ formats.range.long.dateTime.sameDateCurrentYearMilliseconds }}</div>
            </div>
            <div class="example-row-border"></div>

            <div class="layout-row example-row-border">
                <div class="flex">rangeLongDateTime (the same day, not current year)</div>
                <div class="flex">{{ formats.range.long.dateTime.sameDateNotCurrentYear }}</div>
            </div>
            <div class="layout-row example-row-border">
                <div class="flex">rangeLongDateTime (the same day, not current year) (with seconds)</div>
                <div class="flex">{{ formats.range.long.dateTime.sameDateNotCurrentYearSeconds }}</div>
            </div>
            <div class="layout-row example-row-border">
                <div class="flex">rangeLongDateTime (the same day, not current year) (with milliseconds)</div>
                <div class="flex">{{ formats.range.long.dateTime.sameDateNotCurrentYearMilliseconds }}</div>
            </div>
            <div class="example-row-border"></div>

            <div class="layout-row example-row-border">
                <div class="flex">rangeLongDateTime (not current month)</div>
                <div class="flex">{{ formats.range.long.dateTime.notCurrentMonth }}</div>
            </div>
            <div class="layout-row example-row-border">
                <div class="flex">rangeLongDateTime (not current month) (with seconds)</div>
                <div class="flex">{{ formats.range.long.dateTime.notCurrentMonthSeconds }}</div>
            </div>
            <div class="layout-row example-row-border">
                <div class="flex">rangeLongDateTime (not current month) (with milliseconds)</div>
                <div class="flex">{{ formats.range.long.dateTime.notCurrentMonthMilliseconds }}</div>
            </div>
            <div class="example-row-border"></div>

            <div class="layout-row example-row-border">
                <div class="flex">rangeLongDateTime (start date is not in current year)</div>
                <div class="flex">{{ formats.range.long.dateTime.startsNotCurrentYear }}</div>
            </div>
            <div class="layout-row example-row-border">
                <div class="flex">rangeLongDateTime (start date is not in current year) (with seconds)</div>
                <div class="flex">{{ formats.range.long.dateTime.startsNotCurrentYearSeconds }}</div>
            </div>
            <div class="layout-row example-row-border">
                <div class="flex">rangeLongDateTime (start date is not in current year) (with milliseconds)</div>
                <div class="flex">{{ formats.range.long.dateTime.startsNotCurrentYearMilliseconds }}</div>
            </div>
            <div class="example-row-border"></div>

            <div class="layout-row example-row-border">
                <div class="flex">rangeLongDateTime (end date is not in current year)</div>
                <div class="flex">{{ formats.range.long.dateTime.endsNotCurrentYear }}</div>
            </div>
            <div class="layout-row example-row-border">
                <div class="flex">rangeLongDateTime (end date is not in current year) (with seconds)</div>
                <div class="flex">{{ formats.range.long.dateTime.endsNotCurrentYearSeconds }}</div>
            </div>
            <div class="layout-row example-row-border">
                <div class="flex">rangeLongDateTime (end date is not in current year) (with milliseconds)</div>
                <div class="flex">{{ formats.range.long.dateTime.endsNotCurrentYearMilliseconds }}</div>
            </div>
        </div>
    </div>
    <div>
        <h3>Middle format</h3>
        <div>
            <div class="layout-row example-row-border">
                <div class="flex example-light-text-secondary">Name</div>
                <div class="flex example-light-text-secondary">Value</div>
            </div>
            <div class="layout-row example-row-border">
                <div class="flex">rangeMiddleDateTime</div>
                <div class="flex">{{ formats.range.middle.dateTime.currentYear }}</div>
            </div>
            <div class="layout-row example-row-border">
                <div class="flex">rangeMiddleDateTime (with seconds)</div>
                <div class="flex">{{ formats.range.middle.dateTime.currentYearSeconds }}</div>
            </div>
            <div class="layout-row example-row-border">
                <div class="flex">rangeMiddleDateTime (with milliseconds)</div>
                <div class="flex">{{ formats.range.middle.dateTime.currentYearMilliseconds }}</div>
            </div>
            <div class="example-row-border"></div>
            <div class="layout-row example-row-border">
                <div class="flex">rangeMiddleDateTime (the same day)</div>
                <div class="flex">{{ formats.range.middle.dateTime.sameDateCurrentYear }}</div>
            </div>
            <div class="layout-row example-row-border">
                <div class="flex">rangeMiddleDateTime (the same day) (with seconds)</div>
                <div class="flex">{{ formats.range.middle.dateTime.sameDateCurrentYearSeconds }}</div>
            </div>
            <div class="layout-row example-row-border">
                <div class="flex">rangeMiddleDateTime (the same day) (with milliseconds)</div>
                <div class="flex">{{ formats.range.middle.dateTime.sameDateCurrentYearMilliseconds }}</div>
            </div>
            <div class="example-row-border"></div>
            <div class="layout-row example-row-border">
                <div class="flex">rangeMiddleDateTime (the same day, not current year)</div>
                <div class="flex">{{ formats.range.middle.dateTime.sameDateNotCurrentYear }}</div>
            </div>
            <div class="layout-row example-row-border">
                <div class="flex">rangeMiddleDateTime (the same day, not current year) (with seconds)</div>
                <div class="flex">{{ formats.range.middle.dateTime.sameDateNotCurrentYearSeconds }}</div>
            </div>
            <div class="layout-row example-row-border">
                <div class="flex">rangeMiddleDateTime (the same day, not current year) (with milliseconds)</div>
                <div class="flex">{{ formats.range.middle.dateTime.sameDateNotCurrentYearMilliseconds }}</div>
            </div>
            <div class="example-row-border"></div>
            <div class="layout-row example-row-border">
                <div class="flex">rangeMiddleDateTime (not current month)</div>
                <div class="flex">{{ formats.range.middle.dateTime.notCurrentMonth }}</div>
            </div>
            <div class="layout-row example-row-border">
                <div class="flex">rangeMiddleDateTime (not current month) (with seconds)</div>
                <div class="flex">{{ formats.range.middle.dateTime.notCurrentMonthSeconds }}</div>
            </div>
            <div class="layout-row example-row-border">
                <div class="flex">rangeMiddleDateTime (not current month) (with milliseconds)</div>
                <div class="flex">{{ formats.range.middle.dateTime.notCurrentMonthMilliseconds }}</div>
            </div>
            <div class="example-row-border"></div>
            <div class="layout-row example-row-border">
                <div class="flex">rangeMiddleDateTime (start date is not in current year)</div>
                <div class="flex">{{ formats.range.middle.dateTime.startsNotCurrentYear }}</div>
            </div>
            <div class="layout-row example-row-border">
                <div class="flex">rangeMiddleDateTime (start date is not in current year) (with seconds)</div>
                <div class="flex">{{ formats.range.middle.dateTime.startsNotCurrentYearSeconds }}</div>
            </div>
            <div class="layout-row example-row-border">
                <div class="flex">rangeMiddleDateTime (start date is not in current year) (with milliseconds)</div>
                <div class="flex">{{ formats.range.middle.dateTime.startsNotCurrentYearMilliseconds }}</div>
            </div>
            <div class="example-row-border"></div>
            <div class="layout-row example-row-border">
                <div class="flex">rangeMiddleDateTime (end date is not in current year)</div>
                <div class="flex">{{ formats.range.middle.dateTime.endsNotCurrentYear }}</div>
            </div>
            <div class="layout-row example-row-border">
                <div class="flex">rangeMiddleDateTime (end date is not in current year) (with seconds)</div>
                <div class="flex">{{ formats.range.middle.dateTime.endsNotCurrentYearSeconds }}</div>
            </div>
            <div class="layout-row example-row-border">
                <div class="flex">rangeMiddleDateTime (end date is not in current year) (with milliseconds)</div>
                <div class="flex">{{ formats.range.middle.dateTime.endsNotCurrentYearMilliseconds }}</div>
            </div>
        </div>
    </div>
    <div>
        <h3>Short format</h3>
        <div>
            <div class="layout-row example-row-border">
                <div class="flex example-light-text-secondary">Name</div>
                <div class="flex example-light-text-secondary">Value</div>
            </div>
            <div class="layout-row example-row-border">
                <div class="flex">rangeShortDate (current month)</div>
                <div class="flex">{{ formats.range.short.date.currentMonth }}</div>
            </div>
            <div class="layout-row example-row-border">
                <div class="flex">rangeShortDate (not current month)</div>
                <div class="flex">{{ formats.range.short.date.notCurrentYear }}</div>
            </div>
            <div class="layout-row example-row-border">
                <div class="flex">rangeShortDate (start date is not in current year)</div>
                <div class="flex">{{ formats.range.short.date.startsNotCurrentYear }}</div>
            </div>
            <div class="layout-row example-row-border">
                <div class="flex">rangeShortDate (end date is not in current year)</div>
                <div class="flex">{{ formats.range.short.date.endsNotCurrentYear }}</div>
            </div>
            <div class="example-row-border"></div>

            <div class="layout-row example-row-border">
                <div class="flex">rangeShortDateTime (the same day, current year)</div>
                <div class="flex">{{ formats.range.short.dateTime.sameDateCurrentYear }}</div>
            </div>
            <div class="layout-row example-row-border">
                <div class="flex">rangeShortDateTime (the same day, current year) (with seconds)</div>
                <div class="flex">{{ formats.range.short.dateTime.sameDateCurrentYearSeconds }}</div>
            </div>
            <div class="layout-row example-row-border">
                <div class="flex">rangeShortDateTime (the same day, current year) (with milliseconds)</div>
                <div class="flex">{{ formats.range.short.dateTime.sameDateCurrentYearMilliseconds }}</div>
            </div>
            <div class="example-row-border"></div>

            <div class="layout-row example-row-border">
                <div class="flex">rangeShortDateTime (the same day, not current year)</div>
                <div class="flex">{{ formats.range.short.dateTime.sameDateNotCurrentYear }}</div>
            </div>
            <div class="layout-row example-row-border">
                <div class="flex">rangeShortDateTime (the same day, not current year) (with seconds)</div>
                <div class="flex">{{ formats.range.short.dateTime.sameDateNotCurrentYearSeconds }}</div>
            </div>
            <div class="layout-row example-row-border">
                <div class="flex">rangeShortDateTime (the same day, not current year) (with milliseconds)</div>
                <div class="flex">{{ formats.range.short.dateTime.sameDateNotCurrentYearMilliseconds }}</div>
            </div>
            <div class="example-row-border"></div>

            <div class="layout-row example-row-border">
                <div class="flex">rangeShortDateTime (not current month)</div>
                <div class="flex">{{ formats.range.short.dateTime.notCurrentMonth }}</div>
            </div>
            <div class="layout-row example-row-border">
                <div class="flex">rangeShortDateTime (not current month) (with seconds)</div>
                <div class="flex">{{ formats.range.short.dateTime.notCurrentMonthSeconds }}</div>
            </div>
            <div class="layout-row example-row-border">
                <div class="flex">rangeShortDateTime (not current month) (with milliseconds)</div>
                <div class="flex">{{ formats.range.short.dateTime.notCurrentMonthMilliseconds }}</div>
            </div>
            <div class="example-row-border"></div>

            <div class="layout-row example-row-border">
                <div class="flex">rangeShortDateTime (start date is not in current year)</div>
                <div class="flex">{{ formats.range.short.dateTime.startsNotCurrentYear }}</div>
            </div>
            <div class="layout-row example-row-border">
                <div class="flex">rangeShortDateTime (start date is not in current year) (with seconds)</div>
                <div class="flex">{{ formats.range.short.dateTime.startsNotCurrentYearSeconds }}</div>
            </div>
            <div class="layout-row example-row-border">
                <div class="flex">rangeShortDateTime (start date is not in current year) (with milliseconds)</div>
                <div class="flex">{{ formats.range.short.dateTime.startsNotCurrentYearMilliseconds }}</div>
            </div>
            <div class="example-row-border"></div>

            <div class="layout-row example-row-border">
                <div class="flex">rangeShortDateTime (end date is not in current year)</div>
                <div class="flex">{{ formats.range.short.dateTime.endsNotCurrentYear }}</div>
            </div>
            <div class="layout-row example-row-border">
                <div class="flex">rangeShortDateTime (end date is not in current year) (with seconds)</div>
                <div class="flex">{{ formats.range.short.dateTime.endsNotCurrentYearSeconds }}</div>
            </div>
            <div class="layout-row example-row-border">
                <div class="flex">rangeShortDateTime (end date is not in current year) (with milliseconds)</div>
                <div class="flex">{{ formats.range.short.dateTime.endsNotCurrentYearMilliseconds }}</div>
            </div>
        </div>
    </div>

    <h2>Opened range</h2>
    <div>
        <h3>Long format</h3>
        <div>
            <div class="layout-row example-row-border">
                <div class="flex example-light-text-secondary">Name</div>
                <div class="flex example-light-text-secondary">Value</div>
            </div>
            <div class="layout-row example-row-border">
                <div class="flex">openedRangeLongDate (only start)</div>
                <div class="flex">{{ formats.openedRange.long.date.onlyStart }}</div>
            </div>
            <div class="layout-row example-row-border">
                <div class="flex">openedRangeLongDate (only end)</div>
                <div class="flex">{{ formats.openedRange.long.date.onlyEnd }}</div>
            </div>
            <div class="layout-row example-row-border">
                <div class="flex">openedRangeLongDate (only start, start date is not in current year)</div>
                <div class="flex">{{ formats.openedRange.long.date.onlyStartNotCurrentYear }}</div>
            </div>
            <div class="layout-row example-row-border">
                <div class="flex">openedRangeLongDate (only end, end date is not in current year)</div>
                <div class="flex">{{ formats.openedRange.long.date.onlyEndNotCurrentYear }}</div>
            </div>
            <div class="example-row-border"></div>

            <div class="layout-row example-row-border">
                <div class="flex">openedRangeLongDateTime (only start)</div>
                <div class="flex">{{ formats.openedRange.long.dateTime.onlyStart }}</div>
            </div>
            <div class="layout-row example-row-border">
                <div class="flex">openedRangeLongDateTime (only start) (with seconds)</div>
                <div class="flex">{{ formats.openedRange.long.dateTime.onlyStartSeconds }}</div>
            </div>
            <div class="layout-row example-row-border">
                <div class="flex">openedRangeLongDateTime (only start) (with milliseconds)</div>
                <div class="flex">{{ formats.openedRange.long.dateTime.onlyStartMilliseconds }}</div>
            </div>
            <div class="example-row-border"></div>

            <div class="layout-row example-row-border">
                <div class="flex">openedRangeLongDateTime (only start, start date is not in current year)</div>
                <div class="flex">{{ formats.openedRange.long.dateTime.onlyStartNotCurrentYear }}</div>
            </div>
            <div class="layout-row example-row-border">
                <div class="flex">
                    openedRangeLongDateTime (only start, start date is not in current year) (with seconds)
                </div>
                <div class="flex">{{ formats.openedRange.long.dateTime.onlyStartNotCurrentYearSeconds }}</div>
            </div>
            <div class="layout-row example-row-border">
                <div class="flex">
                    openedRangeLongDateTime (only start, start date is not in current year) (with milliseconds)
                </div>
                <div class="flex">{{ formats.openedRange.long.dateTime.onlyStartNotCurrentYearMilliseconds }}</div>
            </div>
            <div class="example-row-border"></div>

            <div class="layout-row example-row-border">
                <div class="flex">openedRangeLongDateTime (only end)</div>
                <div class="flex">{{ formats.openedRange.long.dateTime.onlyEnd }}</div>
            </div>
            <div class="layout-row example-row-border">
                <div class="flex">openedRangeLongDateTime (only end) (with seconds)</div>
                <div class="flex">{{ formats.openedRange.long.dateTime.onlyEndSeconds }}</div>
            </div>
            <div class="layout-row example-row-border">
                <div class="flex">openedRangeLongDateTime (only end) (with milliseconds)</div>
                <div class="flex">{{ formats.openedRange.long.dateTime.onlyEndMilliseconds }}</div>
            </div>
            <div class="example-row-border"></div>

            <div class="layout-row example-row-border">
                <div class="flex">openedRangeLongDateTime (only end, end date is not in current year)</div>
                <div class="flex">{{ formats.openedRange.long.dateTime.onlyEndNotCurrentYear }}</div>
            </div>
            <div class="layout-row example-row-border">
                <div class="flex">
                    openedRangeLongDateTime (only end, end date is not in current year) (with seconds)
                </div>
                <div class="flex">{{ formats.openedRange.long.dateTime.onlyEndNotCurrentYearSeconds }}</div>
            </div>
            <div class="layout-row example-row-border">
                <div class="flex">
                    openedRangeLongDateTime (only end, end date is not in current year) (with milliseconds)
                </div>
                <div class="flex">{{ formats.openedRange.long.dateTime.onlyEndNotCurrentYearMilliseconds }}</div>
            </div>
            <div class="example-row-border"></div>
        </div>
    </div>
    <div>
        <h3>Short format</h3>
        <div>
            <div class="layout-row example-row-border">
                <div class="flex example-light-text-secondary">Name</div>
                <div class="flex example-light-text-secondary">Value</div>
            </div>
            <div class="layout-row example-row-border">
                <div class="flex">openedRangeShortDate (only start)</div>
                <div class="flex">{{ formats.openedRange.short.date.onlyStart }}</div>
            </div>
            <div class="layout-row example-row-border">
                <div class="flex">openedRangeShortDate (only end)</div>
                <div class="flex">{{ formats.openedRange.short.date.onlyEnd }}</div>
            </div>
            <div class="layout-row example-row-border">
                <div class="flex">openedRangeShortDate (only start, start date is not in current year)</div>
                <div class="flex">{{ formats.openedRange.short.date.onlyStartNotCurrentYear }}</div>
            </div>
            <div class="layout-row example-row-border">
                <div class="flex">openedRangeShortDate (only end, end date is not in current year)</div>
                <div class="flex">{{ formats.openedRange.short.date.onlyEndNotCurrentYear }}</div>
            </div>
            <div class="example-row-border"></div>

            <div class="layout-row example-row-border">
                <div class="flex">openedRangeShortDateTime (only start)</div>
                <div class="flex">{{ formats.openedRange.short.dateTime.onlyStart }}</div>
            </div>
            <div class="layout-row example-row-border">
                <div class="flex">openedRangeShortDateTime (only start) (with seconds)</div>
                <div class="flex">{{ formats.openedRange.short.dateTime.onlyStartSeconds }}</div>
            </div>
            <div class="layout-row example-row-border">
                <div class="flex">openedRangeShortDateTime (only start) (with milliseconds)</div>
                <div class="flex">{{ formats.openedRange.short.dateTime.onlyStartMilliseconds }}</div>
            </div>
            <div class="example-row-border"></div>

            <div class="layout-row example-row-border">
                <div class="flex">openedRangeShortDateTime (only start, start date is not in current year)</div>
                <div class="flex">{{ formats.openedRange.short.dateTime.onlyStartNotCurrentYear }}</div>
            </div>
            <div class="layout-row example-row-border">
                <div class="flex">
                    openedRangeShortDateTime (only start, start date is not in current year) (with seconds)
                </div>
                <div class="flex">{{ formats.openedRange.short.dateTime.onlyStartNotCurrentYearSeconds }}</div>
            </div>
            <div class="layout-row example-row-border">
                <div class="flex">
                    openedRangeShortDateTime (only start, start date is not in current year) (with milliseconds)
                </div>
                <div class="flex">{{ formats.openedRange.short.dateTime.onlyStartNotCurrentYearMilliseconds }}</div>
            </div>
            <div class="example-row-border"></div>

            <div class="layout-row example-row-border">
                <div class="flex">openedRangeShortDateTime (only end)</div>
                <div class="flex">{{ formats.openedRange.short.dateTime.onlyEnd }}</div>
            </div>
            <div class="layout-row example-row-border">
                <div class="flex">openedRangeShortDateTime (only end) (with seconds)</div>
                <div class="flex">{{ formats.openedRange.short.dateTime.onlyEndSeconds }}</div>
            </div>
            <div class="layout-row example-row-border">
                <div class="flex">openedRangeShortDateTime (only end) (with milliseconds)</div>
                <div class="flex">{{ formats.openedRange.short.dateTime.onlyEndMilliseconds }}</div>
            </div>
            <div class="example-row-border"></div>

            <div class="layout-row example-row-border">
                <div class="flex">openedRangeShortDateTime (only end, end date is not in current year)</div>
                <div class="flex">{{ formats.openedRange.short.dateTime.onlyEndNotCurrentYear }}</div>
            </div>
            <div class="layout-row example-row-border">
                <div class="flex">
                    openedRangeShortDateTime (only end, end date is not in current year) (with seconds)
                </div>
                <div class="flex">{{ formats.openedRange.short.dateTime.onlyEndNotCurrentYearSeconds }}</div>
            </div>
            <div class="layout-row example-row-border">
                <div class="flex">
                    openedRangeShortDateTime (only end, end date is not in current year) (with milliseconds)
                </div>
                <div class="flex">{{ formats.openedRange.short.dateTime.onlyEndNotCurrentYearMilliseconds }}</div>
            </div>
            <div class="example-row-border"></div>
        </div>
    </div>
</div>
```

```css range-date-formatter-example.css
.example-light-text-secondary {
    color: #ccc;
}

.example-row-border {
    padding: 8px;
    border-bottom: 1px solid #ccc;
}
```

#### Duration

**Example: Duration date-formatter** (`duration-date-formatter`)

```ts duration-date-formatter-example.ts
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, inject } from '@angular/core';
import { LuxonDateModule } from '@koobiq/angular-luxon-adapter/adapter';
import { DateAdapter, DateFormatter, KBQ_LOCALE_SERVICE, KbqFormattersModule } from '@koobiq/components/core';
import { DateTime } from 'luxon';
import { delay } from 'rxjs';
import { distinctUntilChanged } from 'rxjs/operators';

@Component({
    selector: 'duration-date-formatter-example',
    imports: [LuxonDateModule, KbqFormattersModule],
    templateUrl: 'duration-date-formatter-example.html',
    styleUrls: ['duration-date-formatter-example.css'],
    changeDetection: ChangeDetectionStrategy.OnPush
})
export class DurationDateFormatterExample {
    private readonly adapter = inject<DateAdapter<DateTime>>(DateAdapter);
    private readonly formatter = inject<DateFormatter<DateTime>>(DateFormatter);
    private readonly changeDetectorRef = inject(ChangeDetectorRef);
    private readonly localeService = inject(KBQ_LOCALE_SERVICE);

    formats = {
        duration: {
            shortest: {
                withSeconds: {
                    seconds: '',
                    minutesSeconds: '',
                    withMilliseconds: '',
                    hoursMinutesSeconds: '',
                    hoursMinutes: ''
                },
                onlyMinutes: {
                    seconds: '',
                    minutesSeconds: '',
                    withMilliseconds: '',
                    hoursMinutesSeconds: '',
                    hoursMinutes: ''
                }
            },
            long: {
                seconds: '',

                minutesSeconds: '',
                minutes: '',
                minutesMoreThanHour: '',

                hoursMinutes: '',
                hoursMinutesMoreThanDay: '',
                hours: '',

                daysHours: '',
                days: '',
                daysMoreThanWeek: '',

                weeksDays: '',
                weeks: '',

                monthsWeeks: '',
                months: '',
                monthsWithFract: '',

                yearsMonth: '',
                years: '',
                yearsWithFract: ''
            },
            short: {
                secondsMilliseconds: '',
                seconds: '',

                minutesSeconds: '',
                minutes: '',
                minutesMoreThanHour: '',

                hoursMinutes: '',
                hoursMinutesMoreThanDay: '',
                hours: '',

                daysHours: '',
                days: '',
                daysMoreThanWeek: '',

                weeksDays: '',
                weeks: '',

                monthsWeeks: '',
                months: '',
                monthsWithFract: '',

                yearsMonth: '',
                years: '',
                yearsWithFract: ''
            }
        }
    };

    constructor() {
        this.localeService.changes.pipe(distinctUntilChanged(), delay(0)).subscribe(this.onLocaleChange);
    }

    private onLocaleChange = (locale: string) => {
        this.adapter.setLocale(locale);

        this.populateDurationShortest(locale);
        this.populateDurationLong(locale);
        this.populateDurationShort(locale);

        this.changeDetectorRef.markForCheck();
    };

    private populateDurationShortest(locale: string) {
        this.formatter.setLocale(locale);

        const shortest = this.formats.duration.shortest;
        const now = this.adapter.today();
        const start = now.set({ day: 10, hour: 10, minute: 0, second: 0, millisecond: 0 });

        let end = now.set({ day: 10, hour: 10, minute: 0, second: 25 });

        shortest.withSeconds.seconds = this.formatter.durationShortest(start, end);
        shortest.onlyMinutes.seconds = this.formatter.durationShortest(start, end, false);

        end = now.set({ day: 10, hour: 10, minute: 0, second: 25, millisecond: 125 });
        shortest.withSeconds.withMilliseconds = this.formatter.durationShortest(start, end, true, true);
        shortest.onlyMinutes.withMilliseconds = this.formatter.durationShortest(start, end, false, true);

        end = now.set({ day: 10, hour: 10, minute: 2, second: 25 });
        shortest.withSeconds.minutesSeconds = this.formatter.durationShortest(start, end);
        shortest.onlyMinutes.minutesSeconds = this.formatter.durationShortest(start, end, false);

        end = now.set({ day: 12, hour: 10, minute: 2, second: 25 });
        shortest.withSeconds.hoursMinutesSeconds = this.formatter.durationShortest(start, end);
        shortest.onlyMinutes.hoursMinutesSeconds = this.formatter.durationShortest(start, end, false);
    }

    private populateDurationLong(locale: string) {
        this.formatter.setLocale(locale);

        const long = this.formats.duration.long;
        const now = this.adapter.today();
        const start = now.set({ day: 10, hour: 0, minute: 0, second: 0, millisecond: 0 });

        long.seconds = this.formatter.durationLong(start, start.plus({ second: 21 }));

        long.minutesSeconds = this.formatter.durationLong(start, start.plus({ minute: 1, second: 25 }));
        long.minutes = this.formatter.durationLong(start, start.plus({ minute: 22 }));
        long.minutesMoreThanHour = this.formatter.durationLong(start, start.plus({ hour: 2 }), ['minutes']);

        long.hoursMinutes = this.formatter.durationLong(start, start.plus({ hour: 1, minute: 21 }));
        long.hoursMinutesMoreThanDay = this.formatter.durationLong(start, start.plus({ day: 1, hour: 8, minute: 20 }), [
            'hours',
            'minutes'
        ]);
        long.hours = this.formatter.durationLong(start, start.plus({ day: 1, hour: 8, minute: 20 }), ['hours']);

        long.daysHours = this.formatter.durationLong(start, start.plus({ day: 1, hour: 8, minute: 25 }));
        long.days = this.formatter.durationLong(start, start.plus({ day: 2, hour: 8 }), ['days']);
        long.daysMoreThanWeek = this.formatter.durationLong(start, start.plus({ day: 21 }), ['days']);

        long.weeksDays = this.formatter.durationLong(start, start.plus({ day: 15 }));
        long.weeks = this.formatter.durationLong(start, start.plus({ day: 15 }), ['weeks']);

        long.monthsWeeks = this.formatter.durationLong(start, start.plus({ month: 1, day: 25 }));
        long.months = this.formatter.durationLong(start, start.plus({ month: 2 }), ['months']);
        long.monthsWithFract = this.formatter.durationLong(start, start.plus({ month: 1, day: 16 }), ['months'], true);

        long.yearsMonth = this.formatter.durationLong(start, start.plus({ year: 3, month: 11 }));
        long.years = this.formatter.durationLong(start, start.plus({ year: 5, month: 9 }), ['years']);
        long.yearsWithFract = this.formatter.durationLong(start, start.plus({ year: 5, month: 9 }), ['years'], true);
    }

    private populateDurationShort(locale: string) {
        this.formatter.setLocale(locale);

        const short = this.formats.duration.short;
        const now = this.adapter.today();
        const start = now.set({ day: 10, hour: 0, minute: 0, second: 0, millisecond: 0 });

        short.secondsMilliseconds = this.formatter.durationShort(start, start.plus({ second: 21, millisecond: 365 }), [
            'seconds',
            'milliseconds'
        ]);
        short.seconds = this.formatter.durationShort(start, start.plus({ second: 21, millisecond: 365 }));

        short.minutesSeconds = this.formatter.durationShort(start, start.plus({ minute: 1, second: 25 }));
        short.minutes = this.formatter.durationShort(start, start.plus({ minute: 22 }));
        short.minutesMoreThanHour = this.formatter.durationShort(start, start.plus({ hour: 2 }), ['minutes']);

        short.hoursMinutes = this.formatter.durationShort(start, start.plus({ hour: 1, minute: 21 }));
        short.hoursMinutesMoreThanDay = this.formatter.durationShort(
            start,
            start.plus({ day: 1, hour: 8, minute: 20 }),
            ['hours', 'minutes']
        );
        short.hours = this.formatter.durationShort(start, start.plus({ day: 1, hour: 8, minute: 20 }), ['hours']);

        short.daysHours = this.formatter.durationShort(start, start.plus({ day: 1, hour: 8 }));
        short.days = this.formatter.durationShort(start, start.plus({ day: 2, hour: 8 }), ['days']);
        short.daysMoreThanWeek = this.formatter.durationShort(start, start.plus({ day: 15 }), ['days']);

        short.weeksDays = this.formatter.durationShort(start, start.plus({ day: 15 }));
        short.weeks = this.formatter.durationShort(start, start.plus({ day: 15 }), ['weeks']);

        short.monthsWeeks = this.formatter.durationShort(start, start.plus({ month: 1, day: 25 }));
        short.months = this.formatter.durationShort(start, start.plus({ month: 2 }), ['months']);
        short.monthsWithFract = this.formatter.durationShort(
            start,
            start.plus({ month: 2, day: 16 }),
            ['months'],
            true
        );

        short.yearsMonth = this.formatter.durationShort(start, start.plus({ year: 3, month: 11 }));
        short.years = this.formatter.durationShort(start, start.plus({ year: 5, month: 9 }), ['years']);
        short.yearsWithFract = this.formatter.durationShort(start, start.plus({ year: 5, month: 9 }), ['years'], true);
    }
}
```

```html duration-date-formatter-example.html
<div class="kbq-text-big">
    <h3>The shortest format</h3>
    <div>
        <div class="layout-row example-row-border">
            <div class="flex example-light-text-secondary">Name</div>
            <div class="flex example-light-text-secondary">Seconds</div>
            <div class="flex example-light-text-secondary">Only minutes</div>
        </div>
        <div class="layout-row example-row-border">
            <div class="flex">Seconds</div>
            <div class="flex">{{ formats.duration.shortest.withSeconds.seconds }}</div>
            <div class="flex">{{ formats.duration.shortest.onlyMinutes.seconds }}</div>
        </div>
        <div class="layout-row example-row-border">
            <div class="flex">Seconds and milliseconds</div>
            <div class="flex">{{ formats.duration.shortest.withSeconds.withMilliseconds }}</div>
            <div class="flex">{{ formats.duration.shortest.onlyMinutes.withMilliseconds }}</div>
        </div>
        <div class="layout-row example-row-border">
            <div class="flex">Minutes and seconds</div>
            <div class="flex">{{ formats.duration.shortest.withSeconds.minutesSeconds }}</div>
            <div class="flex">{{ formats.duration.shortest.onlyMinutes.minutesSeconds }}</div>
        </div>
        <div class="layout-row example-row-border">
            <div class="flex">Hours, minutes and seconds</div>
            <div class="flex">{{ formats.duration.shortest.withSeconds.hoursMinutesSeconds }}</div>
            <div class="flex">{{ formats.duration.shortest.onlyMinutes.hoursMinutesSeconds }}</div>
        </div>
    </div>

    <h3>Long format</h3>
    <div>
        <div class="layout-row example-row-border">
            <div class="flex example-light-text-secondary">Name</div>
            <div class="flex example-light-text-secondary">Value</div>
        </div>
        <div class="layout-row example-row-border">
            <div class="flex">Seconds</div>
            <div class="flex">{{ formats.duration.long.seconds }}</div>
        </div>
        <div class="layout-row example-row-border">
            <div class="flex">Minutes and seconds</div>
            <div class="flex">{{ formats.duration.long.minutesSeconds }}</div>
        </div>
        <div class="layout-row example-row-border">
            <div class="flex">Minutes</div>
            <div class="flex">{{ formats.duration.long.minutes }}</div>
        </div>
        <div class="layout-row example-row-border">
            <div class="flex">Minutes (more than hour)</div>
            <div class="flex">{{ formats.duration.long.minutesMoreThanHour }}</div>
        </div>
        <div class="layout-row example-row-border">
            <div class="flex">Hours and minutes</div>
            <div class="flex">{{ formats.duration.long.hoursMinutes }}</div>
        </div>
        <div class="layout-row example-row-border">
            <div class="flex">Hours and minutes (more than day)</div>
            <div class="flex">{{ formats.duration.long.hoursMinutesMoreThanDay }}</div>
        </div>
        <div class="layout-row example-row-border">
            <div class="flex">Hours</div>
            <div class="flex">{{ formats.duration.long.hours }}</div>
        </div>
        <div class="layout-row example-row-border">
            <div class="flex">Days and hours</div>
            <div class="flex">{{ formats.duration.long.daysHours }}</div>
        </div>
        <div class="layout-row example-row-border">
            <div class="flex">Days</div>
            <div class="flex">{{ formats.duration.long.days }}</div>
        </div>
        <div class="layout-row example-row-border">
            <div class="flex">Days (more then week)</div>
            <div class="flex">{{ formats.duration.long.daysMoreThanWeek }}</div>
        </div>
        <div class="layout-row example-row-border">
            <div class="flex">Weeks and days</div>
            <div class="flex">{{ formats.duration.long.weeksDays }}</div>
        </div>
        <div class="layout-row example-row-border">
            <div class="flex">Weeks</div>
            <div class="flex">{{ formats.duration.long.weeks }}</div>
        </div>
        <div class="layout-row example-row-border">
            <div class="flex">Months and weeks</div>
            <div class="flex">{{ formats.duration.long.monthsWeeks }}</div>
        </div>
        <div class="layout-row example-row-border">
            <div class="flex">Months</div>
            <div class="flex">{{ formats.duration.long.months }}</div>
        </div>
        <div class="layout-row example-row-border">
            <div class="flex">Months with fraction</div>
            <div class="flex">{{ formats.duration.long.monthsWithFract }}</div>
        </div>
        <div class="layout-row example-row-border">
            <div class="flex">Years and months</div>
            <div class="flex">{{ formats.duration.long.yearsMonth }}</div>
        </div>
        <div class="layout-row example-row-border">
            <div class="flex">Years</div>
            <div class="flex">{{ formats.duration.long.years }}</div>
        </div>
        <div class="layout-row example-row-border">
            <div class="flex">Years with fraction</div>
            <div class="flex">{{ formats.duration.long.yearsWithFract }}</div>
        </div>
    </div>

    <h3>Short format</h3>
    <div>
        <div class="layout-row example-row-border">
            <div class="flex example-light-text-secondary">Name</div>
            <div class="flex example-light-text-secondary">Value</div>
        </div>
        <div class="layout-row example-row-border">
            <div class="flex">Seconds and milliseconds</div>
            <div class="flex">{{ formats.duration.short.secondsMilliseconds }}</div>
        </div>
        <div class="layout-row example-row-border">
            <div class="flex">Seconds</div>
            <div class="flex">{{ formats.duration.short.seconds }}</div>
        </div>
        <div class="layout-row example-row-border">
            <div class="flex">Minutes and seconds</div>
            <div class="flex">{{ formats.duration.short.minutesSeconds }}</div>
        </div>
        <div class="layout-row example-row-border">
            <div class="flex">Minutes</div>
            <div class="flex">{{ formats.duration.short.minutes }}</div>
        </div>
        <div class="layout-row example-row-border">
            <div class="flex">Minutes (more than hour)</div>
            <div class="flex">{{ formats.duration.short.minutesMoreThanHour }}</div>
        </div>
        <div class="layout-row example-row-border">
            <div class="flex">Hours and minutes</div>
            <div class="flex">{{ formats.duration.short.hoursMinutes }}</div>
        </div>
        <div class="layout-row example-row-border">
            <div class="flex">Hours and minutes (more than day)</div>
            <div class="flex">{{ formats.duration.short.hoursMinutesMoreThanDay }}</div>
        </div>
        <div class="layout-row example-row-border">
            <div class="flex">Hours</div>
            <div class="flex">{{ formats.duration.short.hours }}</div>
        </div>
        <div class="layout-row example-row-border">
            <div class="flex">Days and hours</div>
            <div class="flex">{{ formats.duration.short.daysHours }}</div>
        </div>
        <div class="layout-row example-row-border">
            <div class="flex">Days</div>
            <div class="flex">{{ formats.duration.short.days }}</div>
        </div>
        <div class="layout-row example-row-border">
            <div class="flex">Days (more then week)</div>
            <div class="flex">{{ formats.duration.short.daysMoreThanWeek }}</div>
        </div>
        <div class="layout-row example-row-border">
            <div class="flex">Weeks and days</div>
            <div class="flex">{{ formats.duration.short.weeksDays }}</div>
        </div>
        <div class="layout-row example-row-border">
            <div class="flex">Weeks</div>
            <div class="flex">{{ formats.duration.short.weeks }}</div>
        </div>
        <div class="layout-row example-row-border">
            <div class="flex">Months and weeks</div>
            <div class="flex">{{ formats.duration.short.monthsWeeks }}</div>
        </div>
        <div class="layout-row example-row-border">
            <div class="flex">Months</div>
            <div class="flex">{{ formats.duration.short.months }}</div>
        </div>
        <div class="layout-row example-row-border">
            <div class="flex">Months with fraction</div>
            <div class="flex">{{ formats.duration.short.monthsWithFract }}</div>
        </div>
        <div class="layout-row example-row-border">
            <div class="flex">Years and months</div>
            <div class="flex">{{ formats.duration.short.yearsMonth }}</div>
        </div>
        <div class="layout-row example-row-border">
            <div class="flex">Years</div>
            <div class="flex">{{ formats.duration.short.years }}</div>
        </div>
        <div class="layout-row example-row-border">
            <div class="flex">Years with fraction</div>
            <div class="flex">{{ formats.duration.short.yearsWithFract }}</div>
        </div>
    </div>
</div>
```

```css duration-date-formatter-example.css
.example-light-text-secondary {
    color: #ccc;
}

.example-row-border {
    padding: 8px;
    border-bottom: 1px solid #ccc;
}
```

## Examples

### Setup

To use the formatter, you need to import: `KbqFormattersModule` and `KbqLuxonDateModule`.
`KbqLuxonDateModule` imports `KBQ_LOCALE_SERVICE`, so there is no need to import it separately.

**Example: date-formatter-typical-use** (`date-formatter-typical-use`)

```ts date-formatter-typical-use-example.ts
import { ChangeDetectionStrategy, Component, inject } from '@angular/core';
import { KbqLuxonDateModule } from '@koobiq/angular-luxon-adapter/adapter';
import { DateAdapter, DateFormatter, KBQ_LOCALE_SERVICE, KbqFormattersModule } from '@koobiq/components/core';
import { DateTime } from 'luxon';

@Component({
    selector: 'date-formatter-typical-use-example',
    imports: [KbqLuxonDateModule, KbqFormattersModule],
    template: '{{ date }}',
    changeDetection: ChangeDetectionStrategy.OnPush
})
export class DateFormatterTypicalUseExample {
    private readonly adapter = inject<DateAdapter<DateTime>>(DateAdapter);
    private readonly dateFormatter = inject<DateFormatter<DateTime>>(DateFormatter);
    private readonly localeService = inject(KBQ_LOCALE_SERVICE);

    date: string;

    constructor() {
        this.localeService.setLocale('en-US');

        this.date = this.dateFormatter.absoluteLongDate(this.adapter.today());
    }
}
```

It is also possible to connect all the necessary services without using modules (`KbqFormattersModule` and `KbqLuxonDateModule`).

**Example: date-formatter-special-use** (`date-formatter-special-use`)

```ts date-formatter-special-use-example.ts
import { ChangeDetectionStrategy, Component, inject } from '@angular/core';
import { LuxonDateAdapter } from '@koobiq/angular-luxon-adapter/adapter';
import { DateAdapter, DateFormatter, KBQ_DATE_LOCALE } from '@koobiq/components/core';
import { DateTime } from 'luxon';

@Component({
    selector: 'date-formatter-special-use-example',
    template: '{{ date }}',
    providers: [
        {
            provide: DateAdapter,
            useClass: LuxonDateAdapter,
            deps: [KBQ_DATE_LOCALE]
        },
        { provide: DateFormatter, deps: [DateAdapter] }
    ],
    changeDetection: ChangeDetectionStrategy.OnPush
})
export class DateFormatterSpecialUseExample {
    private readonly adapter = inject<DateAdapter<DateTime>>(DateAdapter);
    private readonly dateFormatter = inject<DateFormatter<DateTime>>(DateFormatter);

    date: string;

    constructor() {
        this.dateFormatter.setLocale('en-US');
        this.dateFormatter.setLocale('ru-RU');

        this.date = this.dateFormatter.absoluteLongDate(this.adapter.today());
    }
}
```
