DateTimeFormatter и его возможности

Класс DateTimeFormatter из библиотеки js-joda отвечает за преобразование объектов даты и времени в строки и обратный разбор строк в объекты времени. Форматтеры позволяют:

  • выводить даты в произвольном формате;
  • разбирать строки различных шаблонов;
  • работать с локалями;
  • использовать встроенные ISO-форматы;
  • управлять часовыми поясами;
  • создавать сложные составные шаблоны;
  • настраивать поведение парсинга.

В экосистеме js-joda форматирование построено по тем же принципам, что и в Java API java.time.


Подключение

const {
    LocalDate,
    LocalTime,
    LocalDateTime,
    ZonedDateTime,
    OffsetDateTime,
    ZoneId,
    DateTimeFormatter
} = require('@js-joda/core');

Для работы с локалями дополнительно используется пакет:

npm install @js-joda/locale

Встроенные форматтеры ISO

DateTimeFormatter содержит набор готовых ISO-форматов.

ISO_LOCAL_DATE

const date = LocalDate.of(2025, 3, 15);

const result = date.format(DateTimeFormatter.ISO_LOCAL_DATE);

console.log(result);

Результат:

2025-03-15

ISO_LOCAL_TIME

const time = LocalTime.of(14, 30, 45);

console.log(
    time.format(DateTimeFormatter.ISO_LOCAL_TIME)
);

Результат:

14:30:45

ISO_LOCAL_DATE_TIME

const dateTime = LocalDateTime.of(
    2025,
    3,
    15,
    14,
    30
);

console.log(
    dateTime.format(
        DateTimeFormatter.ISO_LOCAL_DATE_TIME
    )
);

Результат:

2025-03-15T14:30:00

ISO_OFFSET_DATE_TIME

Используется для объектов со смещением UTC.

const dateTime = OffsetDateTime.now();

console.log(
    dateTime.format(
        DateTimeFormatter.ISO_OFFSET_DATE_TIME
    )
);

Пример результата:

2025-03-15T14:30:00+05:00

ISO_ZONED_DATE_TIME

Формат включает идентификатор временной зоны.

const zoned = ZonedDateTime.now(
    ZoneId.of('Asia/Almaty')
);

console.log(
    zoned.format(
        DateTimeFormatter.ISO_ZONED_DATE_TIME
    )
);

Пример:

2025-03-15T14:30:00+05:00[Asia/Almaty]

Создание собственного форматтера

Метод ofPattern

Основной способ создания форматтера — использование шаблона.

const formatter =
    DateTimeFormatter.ofPattern(
        'dd.MM.yyyy'
    );

const date = LocalDate.of(2025, 12, 5);

console.log(
    date.format(formatter)
);

Результат:

05.12.2025

Символы шаблонов

Год

Символ Значение
y год
yy две цифры года
yyyy полный год

Пример:

const date = LocalDate.of(2025, 7, 10);

console.log(
    date.format(
        DateTimeFormatter.ofPattern('yy')
    )
);

console.log(
    date.format(
        DateTimeFormatter.ofPattern('yyyy')
    )
);

Результат:

25
2025

Месяц

Символ Значение
M номер месяца
MM месяц с нулём
MMM краткое название
MMMM полное название
const formatter =
    DateTimeFormatter.ofPattern(
        'dd MMMM yyyy'
    );

Пример:

15 марта 2025

День

Символ Значение
d день
dd день с ведущим нулём
DateTimeFormatter.ofPattern('dd')

Часы

Символ Значение
H часы 0-23
HH часы с нулём
h часы 1-12
DateTimeFormatter.ofPattern('HH:mm')

Минуты и секунды

Символ Значение
m минуты
s секунды
DateTimeFormatter.ofPattern(
    'HH:mm:ss'
)

Доли секунды

Символ Значение
S миллисекунды
DateTimeFormatter.ofPattern(
    'HH:mm:ss.SSS'
)

Пример результата:

14:22:10.345

День недели

Символ Значение
E краткий день
EEEE полный день
DateTimeFormatter.ofPattern(
    'EEEE'
)

Часовой пояс

Символ Значение
X смещение
XX смещение HHMM
XXX смещение HH:MM
z название зоны

Пример:

DateTimeFormatter.ofPattern(
    'yyyy-MM-dd HH:mm XXX'
)

Форматирование даты

Простейший пример

const formatter =
    DateTimeFormatter.ofPattern(
        'dd/MM/yyyy'
    );

const date =
    LocalDate.of(2025, 8, 21);

console.log(
    date.format(formatter)
);

Результат:

21/08/2025

Форматирование даты и времени

const formatter =
    DateTimeFormatter.ofPattern(
        'dd.MM.yyyy HH:mm:ss'
    );

const value =
    LocalDateTime.of(
        2025,
        3,
        15,
        18,
        45,
        12
    );

console.log(
    value.format(formatter)
);

Результат:

15.03.2025 18:45:12

Экранирование текста

Текст внутри шаблона заключается в одинарные кавычки.

const formatter =
    DateTimeFormatter.ofPattern(
        "dd MMMM yyyy 'года'"
    );

Результат:

15 марта 2025 года

Использование кавычек внутри текста

DateTimeFormatter.ofPattern(
    "yyyy-MM-dd 'at' HH:mm"
)

Парсинг строк

Метод parse

const formatter =
    DateTimeFormatter.ofPattern(
        'dd.MM.yyyy'
    );

const date =
    LocalDate.parse(
        '25.12.2025',
        formatter
    );

console.log(date.toString());

Результат:

2025-12-25

Разбор даты и времени

const formatter =
    DateTimeFormatter.ofPattern(
        'dd.MM.yyyy HH:mm'
    );

const value =
    LocalDateTime.parse(
        '15.03.2025 14:30',
        formatter
    );

Ошибки парсинга

Если строка не соответствует шаблону, возникает исключение.

LocalDate.parse(
    '2025/12/25',
    DateTimeFormatter.ofPattern(
        'dd.MM.yyyy'
    )
);

Ошибка:

DateTimeParseException

Работа с локалями

Для локализации требуется пакет:

npm install @js-joda/locale

Подключение локали

require('@js-joda/locale_ru');

Использование русской локали

const { Locale } =
    require('@js-joda/locale');

const formatter =
    DateTimeFormatter
        .ofPattern('dd MMMM yyyy')
        .withLocale(Locale.forLanguageTag('ru'));

const date =
    LocalDate.of(2025, 3, 15);

console.log(
    date.format(formatter)
);

Результат:

15 марта 2025

Английская локаль

const formatter =
    DateTimeFormatter
        .ofPattern('dd MMMM yyyy')
        .withLocale(
            Locale.forLanguageTag('en')
        );

Результат:

15 March 2025

Форматирование временных зон

Работа с ZonedDateTime

const formatter =
    DateTimeFormatter.ofPattern(
        'yyyy-MM-dd HH:mm z'
    );

const zoned =
    ZonedDateTime.now(
        ZoneId.of('Europe/Moscow')
    );

console.log(
    zoned.format(formatter)
);

Пример:

2025-03-15 14:30 MSK

Смещение UTC

const formatter =
    DateTimeFormatter.ofPattern(
        'yyyy-MM-dd HH:mm XXX'
    );

Пример:

2025-03-15 14:30 +05:00

Метод withZone

Позволяет принудительно использовать конкретную временную зону.

const formatter =
    DateTimeFormatter
        .ofPattern(
            'yyyy-MM-dd HH:mm'
        )
        .withZone(
            ZoneId.of('UTC')
        );

Форматирование в UTC

const zoned =
    ZonedDateTime.now(
        ZoneId.of('Asia/Almaty')
    );

const formatter =
    DateTimeFormatter
        .ofPattern(
            'yyyy-MM-dd HH:mm z'
        )
        .withZone(
            ZoneId.of('UTC')
        );

console.log(
    formatter.format(zoned)
);

Неизменяемость форматтеров

DateTimeFormatter является immutable-объектом.

const base =
    DateTimeFormatter.ofPattern(
        'yyyy-MM-dd'
    );

const russian =
    base.withLocale(
        Locale.forLanguageTag('ru')
    );

console.log(base === russian);

Результат:

false

Каждая операция возвращает новый объект.


Комбинирование шаблонов

Составной формат

const formatter =
    DateTimeFormatter.ofPattern(
        'EEEE, dd MMMM yyyy HH:mm:ss'
    );

Пример результата:

суббота, 15 марта 2025 14:22:11

Различие между H и h

24-часовой формат

DateTimeFormatter.ofPattern(
    'HH:mm'
)

Пример:

18:30

12-часовой формат

DateTimeFormatter.ofPattern(
    'hh:mm a'
)

Пример:

06:30 PM

Символ a

Используется для отображения AM/PM.

const formatter =
    DateTimeFormatter.ofPattern(
        'hh:mm a'
    );

Ведущие нули

Количество символов определяет минимальную длину.

DateTimeFormatter.ofPattern('M')

Результат:

3
DateTimeFormatter.ofPattern('MM')

Результат:

03

Различия между yyyy и YYYY

yyyy

Календарный год.

DateTimeFormatter.ofPattern(
    'yyyy-MM-dd'
)

YYYY

Недельный год ISO.

На границе года результаты могут отличаться.

const date =
    LocalDate.of(2020, 12, 31);

console.log(
    date.format(
        DateTimeFormatter.ofPattern(
            'YYYY-ww'
        )
    )
);

Частые ошибки

Использование mm вместо MM

DateTimeFormatter.ofPattern(
    'dd.mm.yyyy'
)

Ошибка:

  • mm — минуты;
  • MM — месяц.

Правильно:

DateTimeFormatter.ofPattern(
    'dd.MM.yyyy'
)

Неверное использование hh

DateTimeFormatter.ofPattern(
    'hh:mm'
)

Без a формат может быть неоднозначным.


Отсутствие экранирования

Неправильно:

DateTimeFormatter.ofPattern(
    'dd MMMM yyyy года'
)

Правильно:

DateTimeFormatter.ofPattern(
    "dd MMMM yyyy 'года'"
)

Практические шаблоны

Формат для API

DateTimeFormatter.ISO_INSTANT

Пример:

2025-03-15T09:30:00Z

Формат для логов

DateTimeFormatter.ofPattern(
    'yyyy-MM-dd HH:mm:ss.SSS'
)

Формат для интерфейса

DateTimeFormatter.ofPattern(
    'dd.MM.yyyy HH:mm'
)

Формат для имени файла

DateTimeFormatter.ofPattern(
    'yyyyMMdd_HHmmss'
)

Пример:

20250315_143000

Производительность

Создание форматтера — относительно дорогая операция. Форматтеры рекомендуется переиспользовать.

Нежелательно:

function format(date) {
    return date.format(
        DateTimeFormatter.ofPattern(
            'yyyy-MM-dd'
        )
    );
}

Лучше:

const FORMATTER =
    DateTimeFormatter.ofPattern(
        'yyyy-MM-dd'
    );

function format(date) {
    return date.format(FORMATTER);
}

Форматирование коллекций дат

const formatter =
    DateTimeFormatter.ofPattern(
        'dd.MM.yyyy'
    );

const dates = [
    LocalDate.of(2025, 1, 10),
    LocalDate.of(2025, 2, 11),
    LocalDate.of(2025, 3, 12)
];

const result =
    dates.map(date =>
        date.format(formatter)
    );

console.log(result);

Сериализация объектов

JSON

const formatter =
    DateTimeFormatter.ISO_LOCAL_DATE_TIME;

const payload = {
    createdAt: LocalDateTime.now()
        .format(formatter)
};

console.log(
    JSON.stringify(payload)
);

Парсинг пользовательского ввода

function parseDate(input) {
    const formatter =
        DateTimeFormatter.ofPattern(
            'dd.MM.yyyy'
        );

    return LocalDate.parse(
        input,
        formatter
    );
}

Проверка формата через try/catch

function isValidDate(value) {
    try {
        LocalDate.parse(
            value,
            DateTimeFormatter.ofPattern(
                'dd.MM.yyyy'
            )
        );

        return true;
    } catch (e) {
        return false;
    }
}

Работа с миллисекундами

const formatter =
    DateTimeFormatter.ofPattern(
        'HH:mm:ss.SSS'
    );

const time =
    LocalTime.of(
        12,
        30,
        15,
        123000000
    );

console.log(
    time.format(formatter)
);

Результат:

12:30:15.123

Работа с наносекундами

const formatter =
    DateTimeFormatter.ofPattern(
        'HH:mm:ss.SSSSSSSSS'
    );

Форматирование Instant

const { Instant } =
    require('@js-joda/core');

const instant = Instant.now();

console.log(
    instant.toString()
);

Результат:

2025-03-15T09:30:00Z

Использование parse напрямую у форматтера

const formatter =
    DateTimeFormatter.ofPattern(
        'dd.MM.yyyy'
    );

const temporal =
    formatter.parse('15.03.2025');

Форматирование с текстом

const formatter =
    DateTimeFormatter.ofPattern(
        "'Дата:' dd.MM.yyyy"
    );

Результат:

Дата: 15.03.2025

Использование нескольких разделителей

DateTimeFormatter.ofPattern(
    'yyyy/MM/dd HH-mm-ss'
)

Формат RFC-подобных строк

DateTimeFormatter.ofPattern(
    'EEE, dd MMM yyyy HH:mm:ss XXX'
)

Пример:

Sat, 15 Mar 2025 14:30:00 +05:00