← JavaScript/Дата и время#93 из 383← ПредыдущийСледующий →+20 XP
Полезно по теме:Гайд: как учить JavaScriptПрактика: JS базаПрактика: async и сетьТермин: Closure

Дата и время

Какую проблему решает Date

Ты разрабатываешь интернет-магазин. В каждом заказе нужно хранить дату создания, показывать «сколько времени прошло с заказа» и «осталось X дней до доставки». Google Calendar, GitHub, Twitter — везде нужна работа с датами. Встроенный объект Date — стандартный инструмент для этого в JavaScript.

На основе предыдущих уроков

  • «Числа» — Date хранит дату как число (миллисекунды)
  • «Строки» — форматирование дат в строку
  • «Методы массивов» — .sort() с датами использует getTime()
  • Создание даты

    const now = new Date()              // текущее время
    
    const d1 = new Date(2024, 0, 15)   // 15 января 2024 — месяц 0-indexed!
    const d2 = new Date(2024, 11, 31)  // 31 декабря 2024
    
    const d3 = new Date('2024-06-20')  // из строки ISO (UTC!)
    const d4 = new Date('2024-06-20T15:30:00')  // с временем
    
    const ts = Date.now()              // timestamp без создания объекта

    Получение компонентов

    const d = new Date(2024, 11, 25, 18, 30, 0)  // 25 декабря 2024, 18:30
    
    d.getFullYear()  // 2024
    d.getMonth()     // 11 — ДЕКАБРЬ! Месяцы 0-11, не 1-12!
    d.getDate()      // 25 — день месяца (1-31)
    d.getDay()       // 3 — день недели (0=вс, 1=пн, 2=вт, ..., 6=сб)
    d.getHours()     // 18
    d.getMinutes()   // 30
    d.getTime()      // timestamp в мс

    Главная ловушка: getMonth() возвращает 0–11. Январь = 0, декабрь = 11. Всегда добавляй +1 при отображении.

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

    const d = new Date(2024, 5, 20, 14, 30)  // 20 июня 2024, 14:30
    
    // Локализованное форматирование — рекомендуется
    d.toLocaleDateString('ru-RU')                           // '20.06.2024'
    d.toLocaleTimeString('ru-RU', { timeStyle: 'short' })   // '14:30'
    d.toLocaleString('ru-RU')                               // '20.06.2024, 14:30:00'
    
    // С кастомными опциями
    d.toLocaleDateString('ru-RU', {
      weekday: 'long',   // 'четверг'
      day: 'numeric',    // '20'
      month: 'long',     // 'июня'
      year: 'numeric',   // '2024'
    })  // 'четверг, 20 июня 2024 г.'

    Арифметика дат

    Даты можно вычитать — результат в миллисекундах:

    const orderDate = new Date(2024, 0, 10)   // 10 января
    const deliveryDate = new Date(2024, 0, 17) // 17 января
    
    const diffMs = deliveryDate - orderDate
    const diffDays = Math.round(diffMs / (1000 * 60 * 60 * 24))  // 7 дней
    
    // Добавление дней — через setDate
    const nextWeek = new Date(orderDate)
    nextWeek.setDate(nextWeek.getDate() + 7)  // + 7 дней

    Паттерн timeAgo

    function timeAgo(date) {
      const diffMs = Date.now() - date.getTime()
      const diffMin = Math.floor(diffMs / 60000)
      const diffHrs = Math.floor(diffMin / 60)
      const diffDays = Math.floor(diffHrs / 24)
    
      if (diffMin < 1)   return 'только что'
      if (diffMin < 60)  return diffMin + ' мин назад'
      if (diffHrs < 24)  return diffHrs + ' ч назад'
      if (diffDays < 7)  return diffDays + ' дн назад'
      return date.toLocaleDateString('ru-RU')
    }

    Типичные ошибки

    1. Забыли +1 к getMonth():

    // Сломано — декабрь показывается как месяц 11:
    const d = new Date(2024, 11, 25)
    console.log(d.getMonth())        // 11 — а должно быть 12!
    console.log(`${d.getDate()}.${d.getMonth()}.${d.getFullYear()}`)
    // '25.11.2024' — неверно!
    
    // Исправлено:
    console.log(`${d.getDate()}.${d.getMonth() + 1}.${d.getFullYear()}`)
    // '25.12.2024'

    2. Создают дату с числовым месяцем без учёта сдвига:

    // Сломано — хотели июнь (6), получили июль:
    const june = new Date(2024, 6, 1)  // это ИЮЛЬ (индекс 6)!
    
    // Исправлено:
    const june = new Date(2024, 5, 1)  // 5 = июнь

    3. Сравнивают объекты Date оператором ==:

    // Сломано — сравниваются ссылки на объекты, а не значения:
    const d1 = new Date(2024, 0, 1)
    const d2 = new Date(2024, 0, 1)
    console.log(d1 == d2)   // false — разные объекты!
    
    // Исправлено:
    console.log(d1.getTime() === d2.getTime())  // true
    // или: d1 < d2, d1 > d2 — работают через автоматический вызов getTime()

    В реальных проектах

  • Форматирование: Intl.DateTimeFormat — более мощная альтернатива toLocaleDateString
  • date-fns, dayjs — популярные библиотеки для удобной работы с датами
  • Временные зоны: всегда храни даты в UTC, показывай в локальном времени пользователя
  • ISO строки: для передачи дат через API используй date.toISOString() — '2024-06-20T15:30:00.000Z'
  • Примеры

    Форматирование дат заказов, timeAgo и расчёт сроков доставки

    // Форматирование даты заказа
    function formatOrderDate(date) {
      return date.toLocaleDateString('ru-RU', {
        day: '2-digit',
        month: 'long',
        year: 'numeric',
        hour: '2-digit',
        minute: '2-digit',
      })
    }
    
    // Сколько времени прошло с события
    function timeAgo(date) {
      const diffMs = Date.now() - date.getTime()
      const diffMin = Math.floor(diffMs / 60000)
      const diffHrs = Math.floor(diffMin / 60)
      const diffDays = Math.floor(diffHrs / 24)
    
      if (diffMin < 1)   return 'только что'
      if (diffMin < 60)  return `${diffMin} мин назад`
      if (diffHrs < 24)  return `${diffHrs} ч назад`
      if (diffDays < 30) return `${diffDays} дн назад`
      return date.toLocaleDateString('ru-RU')
    }
    
    // Дата доставки — через N рабочих дней (пропускаем выходные)
    function addWorkingDays(date, days) {
      const result = new Date(date)
      let added = 0
      while (added < days) {
        result.setDate(result.getDate() + 1)
        const dayOfWeek = result.getDay()
        if (dayOfWeek !== 0 && dayOfWeek !== 6) added++  // не воскресенье и не суббота
      }
      return result
    }
    
    // Сколько дней до события
    function daysUntil(targetDate) {
      const today = new Date()
      today.setHours(0, 0, 0, 0)
      const target = new Date(targetDate)
      target.setHours(0, 0, 0, 0)
      return Math.round((target - today) / (1000 * 60 * 60 * 24))
    }
    
    // Демонстрация
    const orderDate = new Date(2024, 5, 15, 14, 32)  // 15 июня 2024, 14:32
    console.log('Заказ:', formatOrderDate(orderDate))
    // '15 июня 2024 г., 14:32'
    
    const delivery = addWorkingDays(orderDate, 3)
    console.log('Доставка:', delivery.toLocaleDateString('ru-RU'))
    
    // timeAgo с искусственными датами
    const justNow = new Date(Date.now() - 30 * 1000)     // 30 секунд назад
    const hourAgo = new Date(Date.now() - 2 * 3600000)   // 2 часа назад
    const daysAgo = new Date(Date.now() - 5 * 86400000)  // 5 дней назад
    
    console.log(timeAgo(justNow))  // 'только что'
    console.log(timeAgo(hourAgo))  // '2 ч назад'
    console.log(timeAgo(daysAgo))  // '5 дн назад'
    
    // Новый год
    const newYear = new Date(new Date().getFullYear() + 1, 0, 1)
    console.log('Дней до НГ:', daysUntil(newYear))

    Дата и время

    Какую проблему решает Date

    Ты разрабатываешь интернет-магазин. В каждом заказе нужно хранить дату создания, показывать «сколько времени прошло с заказа» и «осталось X дней до доставки». Google Calendar, GitHub, Twitter — везде нужна работа с датами. Встроенный объект Date — стандартный инструмент для этого в JavaScript.

    На основе предыдущих уроков

  • «Числа» — Date хранит дату как число (миллисекунды)
  • «Строки» — форматирование дат в строку
  • «Методы массивов» — .sort() с датами использует getTime()
  • Создание даты

    const now = new Date()              // текущее время
    
    const d1 = new Date(2024, 0, 15)   // 15 января 2024 — месяц 0-indexed!
    const d2 = new Date(2024, 11, 31)  // 31 декабря 2024
    
    const d3 = new Date('2024-06-20')  // из строки ISO (UTC!)
    const d4 = new Date('2024-06-20T15:30:00')  // с временем
    
    const ts = Date.now()              // timestamp без создания объекта

    Получение компонентов

    const d = new Date(2024, 11, 25, 18, 30, 0)  // 25 декабря 2024, 18:30
    
    d.getFullYear()  // 2024
    d.getMonth()     // 11 — ДЕКАБРЬ! Месяцы 0-11, не 1-12!
    d.getDate()      // 25 — день месяца (1-31)
    d.getDay()       // 3 — день недели (0=вс, 1=пн, 2=вт, ..., 6=сб)
    d.getHours()     // 18
    d.getMinutes()   // 30
    d.getTime()      // timestamp в мс

    Главная ловушка: getMonth() возвращает 0–11. Январь = 0, декабрь = 11. Всегда добавляй +1 при отображении.

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

    const d = new Date(2024, 5, 20, 14, 30)  // 20 июня 2024, 14:30
    
    // Локализованное форматирование — рекомендуется
    d.toLocaleDateString('ru-RU')                           // '20.06.2024'
    d.toLocaleTimeString('ru-RU', { timeStyle: 'short' })   // '14:30'
    d.toLocaleString('ru-RU')                               // '20.06.2024, 14:30:00'
    
    // С кастомными опциями
    d.toLocaleDateString('ru-RU', {
      weekday: 'long',   // 'четверг'
      day: 'numeric',    // '20'
      month: 'long',     // 'июня'
      year: 'numeric',   // '2024'
    })  // 'четверг, 20 июня 2024 г.'

    Арифметика дат

    Даты можно вычитать — результат в миллисекундах:

    const orderDate = new Date(2024, 0, 10)   // 10 января
    const deliveryDate = new Date(2024, 0, 17) // 17 января
    
    const diffMs = deliveryDate - orderDate
    const diffDays = Math.round(diffMs / (1000 * 60 * 60 * 24))  // 7 дней
    
    // Добавление дней — через setDate
    const nextWeek = new Date(orderDate)
    nextWeek.setDate(nextWeek.getDate() + 7)  // + 7 дней

    Паттерн timeAgo

    function timeAgo(date) {
      const diffMs = Date.now() - date.getTime()
      const diffMin = Math.floor(diffMs / 60000)
      const diffHrs = Math.floor(diffMin / 60)
      const diffDays = Math.floor(diffHrs / 24)
    
      if (diffMin < 1)   return 'только что'
      if (diffMin < 60)  return diffMin + ' мин назад'
      if (diffHrs < 24)  return diffHrs + ' ч назад'
      if (diffDays < 7)  return diffDays + ' дн назад'
      return date.toLocaleDateString('ru-RU')
    }

    Типичные ошибки

    1. Забыли +1 к getMonth():

    // Сломано — декабрь показывается как месяц 11:
    const d = new Date(2024, 11, 25)
    console.log(d.getMonth())        // 11 — а должно быть 12!
    console.log(`${d.getDate()}.${d.getMonth()}.${d.getFullYear()}`)
    // '25.11.2024' — неверно!
    
    // Исправлено:
    console.log(`${d.getDate()}.${d.getMonth() + 1}.${d.getFullYear()}`)
    // '25.12.2024'

    2. Создают дату с числовым месяцем без учёта сдвига:

    // Сломано — хотели июнь (6), получили июль:
    const june = new Date(2024, 6, 1)  // это ИЮЛЬ (индекс 6)!
    
    // Исправлено:
    const june = new Date(2024, 5, 1)  // 5 = июнь

    3. Сравнивают объекты Date оператором ==:

    // Сломано — сравниваются ссылки на объекты, а не значения:
    const d1 = new Date(2024, 0, 1)
    const d2 = new Date(2024, 0, 1)
    console.log(d1 == d2)   // false — разные объекты!
    
    // Исправлено:
    console.log(d1.getTime() === d2.getTime())  // true
    // или: d1 < d2, d1 > d2 — работают через автоматический вызов getTime()

    В реальных проектах

  • Форматирование: Intl.DateTimeFormat — более мощная альтернатива toLocaleDateString
  • date-fns, dayjs — популярные библиотеки для удобной работы с датами
  • Временные зоны: всегда храни даты в UTC, показывай в локальном времени пользователя
  • ISO строки: для передачи дат через API используй date.toISOString() — '2024-06-20T15:30:00.000Z'
  • Примеры

    Форматирование дат заказов, timeAgo и расчёт сроков доставки

    // Форматирование даты заказа
    function formatOrderDate(date) {
      return date.toLocaleDateString('ru-RU', {
        day: '2-digit',
        month: 'long',
        year: 'numeric',
        hour: '2-digit',
        minute: '2-digit',
      })
    }
    
    // Сколько времени прошло с события
    function timeAgo(date) {
      const diffMs = Date.now() - date.getTime()
      const diffMin = Math.floor(diffMs / 60000)
      const diffHrs = Math.floor(diffMin / 60)
      const diffDays = Math.floor(diffHrs / 24)
    
      if (diffMin < 1)   return 'только что'
      if (diffMin < 60)  return `${diffMin} мин назад`
      if (diffHrs < 24)  return `${diffHrs} ч назад`
      if (diffDays < 30) return `${diffDays} дн назад`
      return date.toLocaleDateString('ru-RU')
    }
    
    // Дата доставки — через N рабочих дней (пропускаем выходные)
    function addWorkingDays(date, days) {
      const result = new Date(date)
      let added = 0
      while (added < days) {
        result.setDate(result.getDate() + 1)
        const dayOfWeek = result.getDay()
        if (dayOfWeek !== 0 && dayOfWeek !== 6) added++  // не воскресенье и не суббота
      }
      return result
    }
    
    // Сколько дней до события
    function daysUntil(targetDate) {
      const today = new Date()
      today.setHours(0, 0, 0, 0)
      const target = new Date(targetDate)
      target.setHours(0, 0, 0, 0)
      return Math.round((target - today) / (1000 * 60 * 60 * 24))
    }
    
    // Демонстрация
    const orderDate = new Date(2024, 5, 15, 14, 32)  // 15 июня 2024, 14:32
    console.log('Заказ:', formatOrderDate(orderDate))
    // '15 июня 2024 г., 14:32'
    
    const delivery = addWorkingDays(orderDate, 3)
    console.log('Доставка:', delivery.toLocaleDateString('ru-RU'))
    
    // timeAgo с искусственными датами
    const justNow = new Date(Date.now() - 30 * 1000)     // 30 секунд назад
    const hourAgo = new Date(Date.now() - 2 * 3600000)   // 2 часа назад
    const daysAgo = new Date(Date.now() - 5 * 86400000)  // 5 дней назад
    
    console.log(timeAgo(justNow))  // 'только что'
    console.log(timeAgo(hourAgo))  // '2 ч назад'
    console.log(timeAgo(daysAgo))  // '5 дн назад'
    
    // Новый год
    const newYear = new Date(new Date().getFullYear() + 1, 0, 1)
    console.log('Дней до НГ:', daysUntil(newYear))

    Задание

    Ты разрабатываешь систему управления подписками для SaaS-сервиса. Реализуй: 1. `formatDate(date)` — форматирует дату как строку "ДД.ММ.ГГГГ" 2. `daysUntilExpiry(expiryDate)` — сколько дней до истечения подписки (отрицательное — если уже истекла) 3. `subscriptionStatus(expiryDate)` — возвращает строку: "активна" (>7 дней), "истекает скоро" (≤7 дней), "истекла" (≤0 дней)

    Подсказка

    formatDate: date.getMonth() + 1 (месяцы с 0). daysUntilExpiry: Math.round(diffMs / (1000 * 60 * 60 * 24)). subscriptionStatus: days <= 0 → "истекла", days <= 7 → "истекает скоро", иначе → "активна".

    Загружаем среду выполнения...
    Загружаем AI-помощника...