diff --git a/robbery.js b/robbery.js index 4a8309d..fc485e8 100644 --- a/robbery.js +++ b/robbery.js @@ -1,48 +1,235 @@ 'use strict'; -/** - * Сделано задание на звездочку - * Реализовано оба метода и tryLater - */ -exports.isStar = true; - -/** - * @param {Object} schedule – Расписание Банды - * @param {Number} duration - Время на ограбление в минутах - * @param {Object} workingHours – Время работы банка - * @param {String} workingHours.from – Время открытия, например, "10:00+5" - * @param {String} workingHours.to – Время закрытия, например, "18:00+5" - * @returns {Object} - */ +exports.isStar = false; + +var DAY_NUM = { + 'ПН': '01', + 'ВТ': '02', + 'СР': '03', + 'ЧТ': '04', + 'ПТ': '05', + 'СБ': '06', + 'ВС': '07' +}; + +var DAYS = ['ПН', 'ВТ', 'СР']; + +var toValidDateTimeFormat = function(dateTime) { + var parts = dateTime.split(' '); + + var dayName = parts[0]; + var time = parts[1]; + + var date = '2001/01/' + DAY_NUM[dayName]; + + return date + ' ' + time; +}; + +var convertToBankTimezone = function (dateTimeString, bankTimeZone) { + var parts = dateTimeString.split(' '); + var time = parts[1].split(':'); + var minAndTz = time[1].split('+'); + var timeZone = parseInt(minAndTz[1]); + + var zoneDiff = timeZone - bankTimeZone; + + var day = parts[0]; + var hours = parseInt(time[0]) - zoneDiff; + var minutes = minAndTz[0]; + var timeZone = bankTimeZone; + + return day + ' ' + hours + ':' + minutes + '+' + timeZone; +}; + +var parseDateTimeInterval = function(dateTimeInterval, bankTimeZone) { + var convertedFrom = convertToBankTimezone(dateTimeInterval.from, bankTimeZone); + var convertedTo = convertToBankTimezone(dateTimeInterval.to, bankTimeZone); + + var validFrom = toValidDateTimeFormat(convertedFrom); + var fromDate = new Date(validFrom); + fromDate.setHours(fromDate.getHours()); + + var validTo = toValidDateTimeFormat(convertedTo); + var toDate = new Date(validTo); + toDate.setHours(toDate.getHours()); + + var interval = { + from: fromDate, + to: toDate + }; + + return interval; +}; + +var putInterval = function (intervals, dateTimeInterval) { + for (var i = 0; i < intervals.length; i++) { + if (intervals[i].from <= dateTimeInterval.from && + intervals[i].to >= dateTimeInterval.from) { + + dateTimeInterval.from = intervals[i].from; + } + + if (intervals[i].to >= dateTimeInterval.to && + intervals[i].from <= dateTimeInterval.to) { + + dateTimeInterval.to = intervals[i].to; + } + } + + var newIntervals = []; + + for (var i = 0; i < intervals.length; i++) { + if (intervals[i].from > dateTimeInterval.to || + intervals[i].to < dateTimeInterval.from) { + newIntervals.push(intervals[i]); + } + } + + newIntervals.push(dateTimeInterval); + + return newIntervals; +}; + +var divideByDate = function (dateTimeInterval) { + var intervals = []; + + while (dateTimeInterval.from.getDate() !== dateTimeInterval.to.getDate()) { + + var endPrevDate = new Date( + dateTimeInterval.to.getUTCFullYear(), + dateTimeInterval.to.getUTCMonth(), + dateTimeInterval.to.getUTCDate() - 1, + 23, + 59 + ); + + var beginNextDate = new Date( + dateTimeInterval.to.getUTCFullYear(), + dateTimeInterval.to.getUTCMonth(), + dateTimeInterval.to.getUTCDate(), + 0, + 0 + ); + + var nextDay = Object.assign({}, dateTimeInterval); + + dateTimeInterval.to = endPrevDate; + nextDay.from = beginNextDate; + + intervals.push(nextDay); + } + + intervals.push(dateTimeInterval); + + return intervals; +}; + +var divide = function (busyIntervals) { + var newBusyIntervals = []; + + for (var i = 0; i < busyIntervals.length; i++) { + var divided = divideByDate(busyIntervals[i]); + for (var j = 0; j < divided.length; j++) { + newBusyIntervals.push(divided[j]); + } + } + + return newBusyIntervals; +}; + +var bankAcceptable = function (busyIntervals, workingHours) { + var newBusyIntervals = []; + + for (var i = 0; i < busyIntervals.length; i++) { + if (busyIntervals[i].from.toTimeString() < workingHours.from.toTimeString()) { + busyIntervals[i].from.setHours(workingHours.from.getHours(), workingHours.from.getMinutes()); + } + + if (busyIntervals[i].to.toTimeString() > workingHours.to.toTimeString()) { + busyIntervals[i].to.setHours(workingHours.to.getHours(), workingHours.to.getMinutes()); + } + + if (busyIntervals[i].from < busyIntervals[i].to) { + newBusyIntervals.push(busyIntervals[i]); + } + } + + return newBusyIntervals; +}; + +var getAppropriateForDay = function (dayNumber, busyIntervals, duration) { + var mondays = busyIntervals + .filter(function (interval) { + return interval.from.getDate() === dayNumber; + }); + + var thatMoment; + + for (var i = 0; i < mondays.length - 1; i++) { + if ((mondays[i + 1].from.getTime() - mondays[i].to.getTime()) / 1000 / 60 >= duration) { + thatMoment = mondays[0].from; + break; + } + } + + return thatMoment; +}; + exports.getAppropriateMoment = function (schedule, duration, workingHours) { - console.info(schedule, duration, workingHours); + var bankTimeZone = parseInt(workingHours.from.slice(6, 7)); - return { + var workingHours = { + from: 'ПН ' + workingHours.from, + to: 'ПН ' + workingHours.to + } + + workingHours = parseDateTimeInterval(workingHours, bankTimeZone); - /** - * Найдено ли время - * @returns {Boolean} - */ + var busyIntervals = Object.keys(schedule) + .map(function (characterName) { + return schedule[characterName]; + }) + .reduce(function (prevIntervals, characterInfo) { + return characterInfo + .map(function (dateTimeInterval) { + return parseDateTimeInterval(dateTimeInterval, bankTimeZone); + }) + .reduce(putInterval, prevIntervals); + }, []); + + busyIntervals = divide(busyIntervals); + busyIntervals = bankAcceptable(busyIntervals, workingHours); + + busyIntervals = busyIntervals.sort(function (interval, another) { + return interval.from > another.from; + }); + + var thatMoment = getAppropriateForDay(1, busyIntervals, duration); + + if (!thatMoment) { + thatMoment = getAppropriateForDay(2, busyIntervals, duration); + } + if (!thatMoment) { + thatMoment = getAppropriateForDay(3, busyIntervals, duration); + } + + return { exists: function () { - return false; + return thatMoment !== undefined; }, - /** - * Возвращает отформатированную строку с часами для ограбления - * Например, - * "Начинаем в %HH:%MM (%DD)" -> "Начинаем в 14:59 (СР)" - * @param {String} template - * @returns {String} - */ format: function (template) { + if (thatMoment === undefined) { + return ''; + } + + template = template.replace('%HH', ('0' + thatMoment.getHours()).slice(-2)); + template = template.replace('%MM', ('0' + thatMoment.getMinutes()).slice(-2)); + template = template.replace('%DD', DAYS[thatMoment.getDate() - 1]); + return template; }, - /** - * Попробовать найти часы для ограбления позже [*] - * @star - * @returns {Boolean} - */ tryLater: function () { return false; }