utils/units.js

/**
 * Утилиты для нормализации и проверки единиц времени.
 *
 * @module utils/units
 */

/**
 * Объект соотношений разных названий единиц времени к каноническим названиям.
 *
 * @type {Object.<string, string>}
 */
const UNIT_ALIASES = {
    millisecond: 'millisecond',
    milliseconds: 'millisecond',
    ms: 'millisecond',

    second: 'second',
    seconds: 'second',
    s: 'second',

    minute: 'minute',
    minutes: 'minute',
    m: 'minute',

    hour: 'hour',
    hours: 'hour',
    h: 'hour',

    day: 'day',
    days: 'day',
    d: 'day',

    month: 'month',
    months: 'month',

    year: 'year',
    years: 'year',
    y: 'year',
};

/**
 * Точное количество миллисекунд для фиксированных единиц времени.
 *
 * @type {Object.<string, number>}
 */
export const FIXED_UNIT_TO_MS = {
    millisecond: 1,
    second: 1000,
    minute: 60 * 1000,
    hour: 60 * 60 * 1000,
    day: 24 * 60 * 60 * 1000,
};

/**
 * Массив поддерживаемых календарных единиц времени.
 *
 * @type {string[]}
 */
export const CALENDAR_UNITS = ['month', 'year'];

/**
 * Нормализует единицу времени к каноническому виду.
 *
 * @param {string} unit - Единица времени.
 * @throws {TypeError} Выбрасывается, если unit пустой или не является строкой.
 * @throws {Error} Выбрасывается, если unit не поддерживается.
 * @returns {string} Каноническое название единицы времени.
 * @example
 * import { normalizeUnit } from './utils/units.js';
 *
 * console.log(normalizeUnit('hours')); // ожидаемый результат: hour
 */
export function normalizeUnit(unit) {
    if (typeof unit !== 'string' || unit.trim() === '') {
        throw new TypeError('normalizeUnit: unit must be a non-empty string');
    }

    const normalized = unit.toLowerCase().trim();
    const canonical = UNIT_ALIASES[normalized];

    if (!canonical) {
        throw new Error(`Unsupported unit: ${unit}`);
    }

    return canonical;
}

/**
 * Проверяет, является ли единица фиксированной.
 *
 * @param {string} unit - Единица времени.
 * @returns {boolean} `true`, если единица является фиксированной.
 * @example
 * import { isFixedUnit } from './utils/units.js';
 *
 * console.log(isFixedUnit('minute')); // ожидаемый результат: true
 */
export function isFixedUnit(unit) {
    return normalizeUnit(unit) in FIXED_UNIT_TO_MS;
}

/**
 * Проверяет, является ли единица календарной.
 *
 * @param {string} unit - Единица времени.
 * @returns {boolean} `true`, если единица является календарной.
 * @example
 * import { isCalendarUnit } from './utils/units.js';
 *
 * console.log(isCalendarUnit('month')); // ожидаемый результат: true
 */
export function isCalendarUnit(unit) {
    return CALENDAR_UNITS.includes(normalizeUnit(unit));
}

/**
 * Возвращает точное количество миллисекунд в одной фиксированной единице времени.
 *
 * @param {string} unit - Фиксированная единица времени.
 * @throws {Error} Выбрасывается, если unit нельзя точно перевести в миллисекунды.
 * @returns {number} Количество миллисекунд в одной единице времени.
 * @example
 * import { unitToMilliseconds } from './utils/units.js';
 *
 * console.log(unitToMilliseconds('hour')); // ожидаемый результат: 3600000
 */
export function unitToMilliseconds(unit) {
    const normalizedUnit = normalizeUnit(unit);

    if (!(normalizedUnit in FIXED_UNIT_TO_MS)) {
        throw new Error(`Unit cannot be converted to milliseconds exactly: ${unit}`);
    }

    return FIXED_UNIT_TO_MS[normalizedUnit];
}