Метод shiftTo

В библиотеке Luxon объект Duration используется для представления промежутков времени в абстрактной форме: часы, минуты, секунды, дни и другие единицы могут сосуществовать одновременно. При этом значения внутри Duration не всегда находятся в «человеческом» виде — например, 90 минут могут храниться как { minutes: 90 }, а не как { hours: 1, minutes: 30 }.

Для управления представлением и перераспределением единиц используется метод shiftTo, который позволяет преобразовать длительность к заданному набору единиц с автоматической нормализацией.


Смысл метода shiftTo

Метод shiftTo перераспределяет (перекладывает) значения Duration между единицами времени так, чтобы итоговый объект содержал только указанные единицы.

Ключевая особенность:

  • сохраняется общая длительность
  • изменяется структура представления
  • выполняется автоматическое «перенесение» значений между единицами

Если исходная длительность содержит больше единиц, чем указано в shiftTo, лишние значения «спускаются» в младшие единицы.


Базовая сигнатура

Duration.shiftTo(...units)

Параметры:

  • units — список строковых идентификаторов единиц времени ("hours", "minutes", "seconds" и т.д.)

Возвращаемое значение:

  • новый объект Duration

Простейший пример преобразования

import { Duration } from "luxon";

const d = Duration.fromObject({ hours: 1, minutes: 90 });

const shifted = d.shiftTo("hours", "minutes");

console.log(shifted.toObject());

Результат:

{ hours: 2, minutes: 30 }

Объяснение:

  • 90 минут преобразуются в 1 час 30 минут
  • 1 исходный час + 1 дополнительный = 2 часа
  • остаётся 30 минут

Поведение при исключении единиц

Если единица не указана в shiftTo, она не сохраняется явно, но её вклад перераспределяется в оставшиеся единицы.

const d = Duration.fromObject({ hours: 1, minutes: 90 });

const shifted = d.shiftTo("minutes");

console.log(shifted.toObject());

Результат:

{ minutes: 150 }

Здесь всё выражается только в минутах, без сохранения часов как отдельной сущности.


Работа с большими диапазонами

shiftTo особенно полезен при работе с составными длительностями:

const d = Duration.fromObject({
  days: 1,
  hours: 5,
  minutes: 120
});

const normalized = d.shiftTo("days", "hours", "minutes");

console.log(normalized.toObject());

Результат:

{ days: 1, hours: 7, minutes: 0 }

Пояснение:

  • 120 минут → 2 часа
  • 5 + 2 = 7 часов
  • 0 минут остаётся после перераспределения

Отличие shiftTo от normalize

Внутри Luxon существует также нормализация Duration, но она отличается по смыслу:

  • normalize() — приводит Duration к каноническому виду в рамках всех доступных единиц
  • shiftTo() — фиксирует только указанные единицы и перестраивает значения под них
const d = Duration.fromObject({ minutes: 90 });

console.log(d.normalize().toObject());
console.log(d.shiftTo("hours", "minutes").toObject());

Результаты могут совпадать, но поведение становится различным при более сложных структурах.


Приоритет единиц и порядок аргументов

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

const d = Duration.fromObject({ seconds: 3661 });

console.log(d.shiftTo("hours", "minutes", "seconds").toObject());

Результат:

{ hours: 1, minutes: 1, seconds: 1 }

Если изменить порядок:

console.log(d.shiftTo("seconds", "minutes", "hours").toObject());

Результат будет выражен иначе:

{ seconds: 3661 }

Первая указанная единица становится основной формой хранения.


Потеря и сохранение точности

shiftTo работает с целыми и дробными значениями, но перераспределение может приводить к изменению представления дробных частей.

const d = Duration.fromObject({ seconds: 90.5 });

const shifted = d.shiftTo("minutes", "seconds");

console.log(shifted.toObject());

Результат:

{ minutes: 1, seconds: 30.5 }

Дробная часть сохраняется в младшей единице.


Использование shiftTo для унификации данных

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

const durations = [
  Duration.fromObject({ hours: 1, minutes: 30 }),
  Duration.fromObject({ minutes: 200 }),
  Duration.fromObject({ seconds: 7200 })
];

const normalized = durations.map(d =>
  d.shiftTo("hours", "minutes")
);

normalized.forEach(d => console.log(d.toObject()));

Результат:

{ hours: 1, minutes: 30 }
{ hours: 3, minutes: 20 }
{ hours: 2, minutes: 0 }

Цепочки методов

shiftTo часто используется в цепочке преобразований Duration:

const result = Duration
  .fromObject({ minutes: 125 })
  .shiftTo("hours", "minutes")
  .mapUnits(x => x * 2);

console.log(result.toObject());

После shiftTo структура становится предсказуемой, что упрощает дальнейшие операции.


Особенности поведения при нулевых значениях

shiftTo может возвращать единицы с нулевыми значениями, если они явно указаны:

const d = Duration.fromObject({ seconds: 3600 });

console.log(d.shiftTo("hours", "minutes").toObject());

Результат:

{ hours: 1, minutes: 0 }

Нулевые значения сохраняются, если соответствующая единица присутствует в списке.


Влияние на внутреннюю структуру Duration

После применения shiftTo:

  • изменяется набор активных единиц
  • перераспределяются внутренние коэффициенты
  • пересчитывается представление при сериализации (toObject, toISO, toHuman)
const d = Duration.fromObject({ minutes: 90 });

const shifted = d.shiftTo("hours", "minutes");

console.log(shifted.toISO());

Результат:

PT1H30M

Работа с нестандартными наборами единиц

shiftTo не ограничивается стандартным набором:

  • years
  • months
  • weeks
  • days
  • hours
  • minutes
  • seconds
  • milliseconds
const d = Duration.fromObject({
  days: 400,
  hours: 10
});

console.log(d.shiftTo("years", "months", "days").toObject());

Результат будет зависеть от календарной модели Luxon, где месяцы и годы считаются приблизительно через фиксированные коэффициенты.


Практическая роль shiftTo в архитектуре приложений

shiftTo часто применяется в сценариях:

  • унификация отображения длительности в UI
  • подготовка данных для API
  • агрегация логов времени
  • расчёты таймеров и прогресс-баров
  • конвертация пользовательского ввода

При этом ключевым эффектом является получение стабильного формата данных без ручной переработки единиц времени.


Сравнение поведения при разных входных структурах

const a = Duration.fromObject({ minutes: 60 });
const b = Duration.fromObject({ hours: 1 });

console.log(a.shiftTo("hours", "minutes").toObject());
console.log(b.shiftTo("hours", "minutes").toObject());

Оба результата приводятся к единой структуре:

{ hours: 1, minutes: 0 }
{ hours: 1, minutes: 0 }

Даже при различном исходном представлении итоговая форма совпадает, что делает shiftTo инструментом нормализации данных.