Парсинг JSON-дат

Формат JSON не содержит отдельного типа данных для даты. Любая дата в JSON представляется строкой. Наиболее распространённый формат — ISO 8601:

{
  "createdAt": "2026-05-22T14:30:00.000Z"
}

После десериализации через JSON.parse() значение остаётся строкой:

const json = `{
  "createdAt": "2026-05-22T14:30:00.000Z"
}`

const data = JSON.parse(json)

console.log(typeof data.createdAt) // string

Для преобразования строки в объект Date библиотека date-fns предоставляет набор инструментов для безопасного и предсказуемого парсинга.


Установка date-fns

npm install date-fns

Импорт функций:

import { parseISO } from 'date-fns'

Функция parseISO

Основной инструмент для обработки JSON-дат — parseISO().

import { parseISO } from 'date-fns'

const date = parseISO('2026-05-22T14:30:00.000Z')

console.log(date)
console.log(date instanceof Date)

Результат:

2026-05-22T14:30:00.000Z
true

Функция преобразует ISO-строку в полноценный объект Date.


Почему parseISO лучше new Date

Многие разработчики используют:

const date = new Date('2026-05-22T14:30:00.000Z')

Однако parseISO() обладает рядом преимуществ:

Предсказуемость

parseISO() предназначена именно для ISO-форматов и работает более стабильно между средами выполнения.

Явное намерение

Код становится понятнее:

parseISO(value)

сразу показывает, что ожидается ISO-строка.

Совместимость с date-fns

Функция хорошо интегрируется с остальными инструментами библиотеки:

import { parseISO, format, addDays } from 'date-fns'

const date = parseISO('2026-05-22T14:30:00.000Z')

console.log(format(date, 'dd.MM.yyyy'))
console.log(addDays(date, 7))

Парсинг даты из JSON-ответа API

Типичный пример работы с REST API:

const response = await fetch('/api/users')
const users = await response.json()

Ответ сервера:

[
  {
    "id": 1,
    "name": "Alex",
    "createdAt": "2026-05-22T14:30:00.000Z"
  }
]

Преобразование строковых дат:

import { parseISO } from 'date-fns'

const normalizedUsers = users.map(user => ({
  ...user,
  createdAt: parseISO(user.createdAt)
}))

После этого можно безопасно использовать функции date-fns:

import { formatDistanceToNow } from 'date-fns'

console.log(
  formatDistanceToNow(normalizedUsers[0].createdAt)
)

Парсинг вложенных JSON-структур

JSON-объекты часто содержат даты во вложенных полях.

Пример:

{
  "user": {
    "profile": {
      "birthday": "1998-03-12"
    }
  }
}

Обработка:

import { parseISO } from 'date-fns'

const data = JSON.parse(json)

const birthday = parseISO(
  data.user.profile.birthday
)

Массовое преобразование дат

В крупных приложениях ручной вызов parseISO() для каждого поля становится неудобным.

Универсальная функция преобразования

import { parseISO, isValid } from 'date-fns'

function reviveDates(obj) {
  for (const key in obj) {
    const value = obj[key]

    if (typeof value === 'string') {
      const parsed = parseISO(value)

      if (isValid(parsed)) {
        obj[key] = parsed
      }
    }

    if (
      typeof value === 'object' &&
      value !== null
    ) {
      reviveDates(value)
    }
  }

  return obj
}

Использование:

const parsed = reviveDates(JSON.parse(json))

Использование JSON.parse с reviver

JSON.parse() поддерживает второй аргумент — функцию reviver.

Это позволяет автоматически преобразовывать даты при десериализации.

Базовый пример

import { parseISO, isValid } from 'date-fns'

const json = `{
  "createdAt": "2026-05-22T14:30:00.000Z"
}`

const data = JSON.parse(json, (key, value) => {
  if (typeof value === 'string') {
    const parsed = parseISO(value)

    if (isValid(parsed)) {
      return parsed
    }
  }

  return value
})

Теперь дата автоматически становится объектом Date.


Проверка валидности даты

Даже если строка похожа на дату, она может быть некорректной.

Пример:

parseISO('2026-99-99')

Для проверки используется isValid():

import { parseISO, isValid } from 'date-fns'

const date = parseISO('2026-99-99')

console.log(isValid(date))

Результат:

false

Парсинг даты без времени

ISO-формат может содержать только дату:

2026-05-22

Обработка:

import { parseISO } from 'date-fns'

const date = parseISO('2026-05-22')

Важно понимать особенности timezone.


Часовые пояса и JSON-даты

UTC-формат

Строка:

2026-05-22T14:30:00.000Z

Суффикс Z означает UTC.

Локальное время

При выводе дата может автоматически конвертироваться в локальный timezone пользователя.

const date = parseISO('2026-05-22T14:30:00.000Z')

console.log(date.toString())

Формат без timezone

Строка:

2026-05-22T14:30:00

не содержит информации о часовом поясе.

Результат зависит от локальной среды выполнения.


Проблемы с timezone при парсинге

Смещение даты

parseISO('2026-05-22')

В некоторых часовых поясах отображение может давать предыдущий день.

Например:

Thu May 21 2026 21:00:00 GMT-0300

Это связано с внутренним хранением даты.


Безопасная работа с календарными датами

Если время не важно, рекомендуется:

  • хранить дату отдельно от времени;
  • избегать UTC-конверсий;
  • использовать форматирование без прямого вывода Date.

Пример:

import { parseISO, format } from 'date-fns'

const birthday = parseISO('1998-03-12')

console.log(
  format(birthday, 'yyyy-MM-dd')
)

Обработка nullable-полей

API часто возвращают null.

Пример:

{
  "deletedAt": null
}

Безопасный парсинг:

import { parseISO } from 'date-fns'

const deletedAt = data.deletedAt
  ? parseISO(data.deletedAt)
  : null

Обработка массива дат

JSON:

{
  "events": [
    "2026-05-20T10:00:00Z",
    "2026-05-21T12:00:00Z",
    "2026-05-22T15:00:00Z"
  ]
}

Преобразование:

import { parseISO } from 'date-fns'

const dates = data.events.map(parseISO)

Парсинг нестандартных JSON-дат

Некоторые API используют собственные форматы:

{
  "createdAt": "22.05.2026 14:30"
}

parseISO() здесь не подойдёт.

Используется функция parse().

import { parse } from 'date-fns'

const date = parse(
  '22.05.2026 14:30',
  'dd.MM.yyyy HH:mm',
  new Date()
)

Отличие parseISO от parse

parseISO

Используется только для ISO-строк.

parseISO('2026-05-22T14:30:00Z')

parse

Используется для произвольных шаблонов.

parse(
  '22/05/2026',
  'dd/MM/yyyy',
  new Date()
)

Типичные ISO-форматы JSON

Полная дата UTC

2026-05-22T14:30:00.000Z

Без миллисекунд

2026-05-22T14:30:00Z

Только дата

2026-05-22

С timezone offset

2026-05-22T14:30:00+03:00

Все эти варианты корректно обрабатываются parseISO().


Ошибки при парсинге JSON-дат

Передача undefined

parseISO(undefined)

Результат:

Invalid Date

Передача null

parseISO(null)

Также вернётся невалидная дата.

Отсутствие проверки

Ошибка:

format(parseISO(value), 'dd.MM.yyyy')

если value содержит мусорные данные.

Безопасный вариант:

import {
  parseISO,
  isValid,
  format
} from 'date-fns'

const parsed = parseISO(value)

if (isValid(parsed)) {
  console.log(
    format(parsed, 'dd.MM.yyyy')
  )
}

Автоматическое преобразование только ISO-строк

Простая проверка:

function isIsoDate(value) {
  return /^\d{4}-\d{2}-\d{2}T/.test(value)
}

Использование:

import { parseISO } from 'date-fns'

const data = JSON.parse(json, (key, value) => {
  if (
    typeof value === 'string' &&
    isIsoDate(value)
  ) {
    return parseISO(value)
  }

  return value
})

Работа с TypeScript

Типизация API-ответа

type UserDto = {
  id: number
  createdAt: string
}

После преобразования:

type User = {
  id: number
  createdAt: Date
}

Функция маппинга:

import { parseISO } from 'date-fns'

function mapUser(dto: UserDto): User {
  return {
    ...dto,
    createdAt: parseISO(dto.createdAt)
  }
}

Производительность при большом количестве дат

При обработке тысяч объектов стоит учитывать:

  • parseISO() создаёт новые объекты Date;
  • массовый парсинг требует памяти;
  • повторный парсинг одной и той же строки нежелателен.

Плохой вариант:

format(parseISO(date), 'dd.MM.yyyy')
format(parseISO(date), 'HH:mm')
format(parseISO(date), 'yyyy')

Лучше:

const parsed = parseISO(date)

format(parsed, 'dd.MM.yyyy')
format(parsed, 'HH:mm')
format(parsed, 'yyyy')

Интеграция с localStorage

Даты при сохранении сериализуются в строки.

localStorage.setItem(
  'user',
  JSON.stringify(user)
)

После чтения:

const user = JSON.parse(
  localStorage.getItem('user')
)

Поля дат снова становятся строками.

Восстановление:

import { parseISO } from 'date-fns'

user.createdAt = parseISO(user.createdAt)

Интеграция с GraphQL

GraphQL-серверы часто используют scalar DateTime.

Ответ:

{
  "createdAt": "2026-05-22T14:30:00.000Z"
}

Клиентская нормализация:

import { parseISO } from 'date-fns'

const normalize = entity => ({
  ...entity,
  createdAt: parseISO(entity.createdAt)
})

Комбинирование parseISO и format

Наиболее распространённый сценарий:

import { parseISO, format } from 'date-fns'

const result = format(
  parseISO('2026-05-22T14:30:00Z'),
  'dd.MM.yyyy HH:mm'
)

console.log(result)

Результат:

22.05.2026 14:30

Сериализация обратно в JSON

После обработки объект Date снова можно сериализовать:

const json = JSON.stringify({
  createdAt: new Date()
})

Результат:

{
  "createdAt":"2026-05-22T14:30:00.000Z"
}

JavaScript автоматически вызывает toISOString().


Полный цикл работы с JSON-датами

import {
  parseISO,
  format
} from 'date-fns'

const response = `{
  "id": 1,
  "createdAt": "2026-05-22T14:30:00.000Z"
}`

const raw = JSON.parse(response)

const entity = {
  ...raw,
  createdAt: parseISO(raw.createdAt)
}

console.log(
  format(entity.createdAt, 'dd.MM.yyyy HH:mm')
)

Практический шаблон для production-кода

import {
  parseISO,
  isValid
} from 'date-fns'

export function parseJsonDate(value) {
  if (!value || typeof value !== 'string') {
    return null
  }

  const parsed = parseISO(value)

  return isValid(parsed)
    ? parsed
    : null
}

Использование:

const createdAt = parseJsonDate(
  response.createdAt
)

Такой подход:

  • предотвращает ошибки;
  • упрощает повторное использование;
  • централизует обработку дат;
  • делает код предсказуемым и безопасным.