134 lines
5.2 KiB
JavaScript
134 lines
5.2 KiB
JavaScript
// Утилиты для расчета рабочих часов
|
|
|
|
/**
|
|
* Проверяет, является ли день рабочим (понедельник-пятница)
|
|
* @param {Date} date - Дата для проверки
|
|
* @returns {boolean} true если рабочий день
|
|
*/
|
|
function isWorkingDay(date) {
|
|
const day = date.getDay(); // 0 = воскресенье, 1 = понедельник, ..., 6 = суббота
|
|
return day >= 1 && day <= 5; // понедельник-пятница
|
|
}
|
|
|
|
/**
|
|
* Преобразует время в формате HH:MM в минуты от начала дня
|
|
* @param {string} time - Время в формате HH:MM
|
|
* @returns {number} Минуты от начала дня
|
|
*/
|
|
function timeToMinutes(time) {
|
|
if (!time) return 0;
|
|
const [hours, minutes] = time.split(":").map(Number);
|
|
return hours * 60 + minutes;
|
|
}
|
|
|
|
/**
|
|
* Преобразует минуты от начала дня в формат HH:MM
|
|
* @param {number} minutes - Минуты от начала дня
|
|
* @returns {string} Время в формате HH:MM
|
|
*/
|
|
function minutesToTime(minutes) {
|
|
const hours = Math.floor(minutes / 60);
|
|
const mins = minutes % 60;
|
|
return `${hours.toString().padStart(2, "0")}:${mins
|
|
.toString()
|
|
.padStart(2, "0")}`;
|
|
}
|
|
|
|
/**
|
|
* Рассчитывает рабочие часы между двумя датами/временами
|
|
* Рабочий день: 09:00 - 18:00
|
|
* Обед: 12:00 - 13:00 (не учитывается в расчетах)
|
|
* Только рабочие дни (понедельник-пятница)
|
|
*
|
|
* @param {string} startDate - Дата начала (YYYY-MM-DD)
|
|
* @param {string} startTime - Время начала (HH:MM)
|
|
* @param {string} endDate - Дата окончания (YYYY-MM-DD) или null для открытой записи
|
|
* @param {string} endTime - Время окончания (HH:MM) или null для открытой записи
|
|
* @returns {number} Количество рабочих часов
|
|
*/
|
|
function calculateWorkHours(startDate, startTime, endDate, endTime) {
|
|
const startDateTime = new Date(`${startDate}T${startTime || "09:00"}`);
|
|
const endDateTime =
|
|
endDate && endTime ? new Date(`${endDate}T${endTime}`) : null;
|
|
|
|
// Если нет даты окончания - возвращаем 0 (открытая запись)
|
|
if (!endDateTime) {
|
|
return 0;
|
|
}
|
|
|
|
// Если дата окончания раньше даты начала - ошибка
|
|
if (endDateTime < startDateTime) {
|
|
throw new Error("Дата окончания не может быть раньше даты начала");
|
|
}
|
|
|
|
const WORK_START = 9 * 60; // 09:00 в минутах
|
|
const WORK_END = 18 * 60; // 18:00 в минутах
|
|
const LUNCH_START = 12 * 60; // 12:00 в минутах
|
|
const LUNCH_END = 13 * 60; // 13:00 в минутах
|
|
|
|
let totalMinutes = 0;
|
|
let currentDate = new Date(startDate);
|
|
|
|
// Проходим по каждому дню от начала до окончания
|
|
while (
|
|
currentDate <= (endDateTime ? new Date(endDate) : new Date(startDate))
|
|
) {
|
|
if (isWorkingDay(currentDate)) {
|
|
const dayStart = new Date(currentDate);
|
|
dayStart.setHours(0, 0, 0, 0);
|
|
|
|
const dayEnd = new Date(currentDate);
|
|
dayEnd.setHours(23, 59, 59, 999);
|
|
|
|
// Определяем время начала работы в этот день
|
|
let workStartMinutes = WORK_START;
|
|
if (currentDate.toDateString() === new Date(startDate).toDateString()) {
|
|
// Первый день - берем время начала работы
|
|
const startMinutes =
|
|
startDateTime.getHours() * 60 + startDateTime.getMinutes();
|
|
workStartMinutes = Math.max(startMinutes, WORK_START);
|
|
}
|
|
|
|
// Определяем время окончания работы в этот день
|
|
let workEndMinutes = WORK_END;
|
|
if (
|
|
endDateTime &&
|
|
currentDate.toDateString() === new Date(endDate).toDateString()
|
|
) {
|
|
// Последний день - берем время окончания работы
|
|
const endMinutes =
|
|
endDateTime.getHours() * 60 + endDateTime.getMinutes();
|
|
workEndMinutes = Math.min(endMinutes, WORK_END);
|
|
}
|
|
|
|
// Если время окончания раньше времени начала в этот день - пропускаем
|
|
if (workEndMinutes > workStartMinutes) {
|
|
let dayMinutes = workEndMinutes - workStartMinutes;
|
|
|
|
// Вычитаем обед, если он попадает в рабочий интервал
|
|
if (workStartMinutes < LUNCH_END && workEndMinutes > LUNCH_START) {
|
|
const lunchOverlapStart = Math.max(workStartMinutes, LUNCH_START);
|
|
const lunchOverlapEnd = Math.min(workEndMinutes, LUNCH_END);
|
|
if (lunchOverlapEnd > lunchOverlapStart) {
|
|
dayMinutes -= lunchOverlapEnd - lunchOverlapStart;
|
|
}
|
|
}
|
|
|
|
totalMinutes += Math.max(0, dayMinutes);
|
|
}
|
|
}
|
|
|
|
// Переходим к следующему дню
|
|
currentDate.setDate(currentDate.getDate() + 1);
|
|
}
|
|
|
|
return totalMinutes / 60; // Возвращаем часы
|
|
}
|
|
|
|
module.exports = {
|
|
calculateWorkHours,
|
|
isWorkingDay,
|
|
timeToMinutes,
|
|
minutesToTime,
|
|
};
|