diff --git a/robbery.js b/robbery.js index 4a8309d..fe3b4bb 100644 --- a/robbery.js +++ b/robbery.js @@ -1,11 +1,163 @@ 'use strict'; -/** - * Сделано задание на звездочку - * Реализовано оба метода и tryLater - */ exports.isStar = true; +var ROBBERY_DAYS = ['ПН', 'ВТ', 'СР']; +var MS_IN_MINUTE = 60 * 1000; +// сдвиг на 30 минут +var TIME_SHIFT = 30; + +function getBankSchedule(bankHours, bankTimezone) { + + return ROBBERY_DAYS.map(function (day) { + var from = day + ' ' + bankHours.from; + var to = day + ' ' + bankHours.to; + + return getNewInterval(from, to, bankTimezone); + }); +} + +function getNewDate(date, bankTimezone) { + var timeRegExp = /([А-Я]{2}) (\d{2}):(\d{2})\+(\d+)/g; + var parsedTime = timeRegExp.exec(date); + var day = ROBBERY_DAYS.indexOf(parsedTime[1]) + 1; + var hour = parsedTime[2] - (parseInt(parsedTime[4], 10) - bankTimezone); + + return new Date(2016, 7, day, hour, parsedTime[3]); +} + +function parseSchedule(schedule, bankTimezone) { + var parsedSchedule = []; + Object.keys(schedule).forEach(function (robberName) { + schedule[robberName].forEach(function (interval) { + var from = interval.from; + var to = interval.to; + parsedSchedule.push(getNewInterval(from, to, bankTimezone)); + }); + }); + + return parsedSchedule; +} + + +function getNewInterval(from, to, timezone) { + + return { + from: getNewDate(from, timezone), + to: getNewDate(to, timezone) + }; +} + + +function getSortedIntervals(schedule, bankTimezone) { + var sortedSchedule = parseSchedule(schedule, bankTimezone); + sortedSchedule.sort(function (a, b) { + return a.from - b.from; + }); + var joinedSchedule = joinSchedule(sortedSchedule); + + return getFreeSchedule(joinedSchedule, bankTimezone); +} + +function isIncluded(time, interval) { + + return interval.from <= time && time <= interval.to; +} + +function getLaterTime(firstTime, secondTime) { + var greater = Math.max(firstTime, secondTime); + + return new Date(greater); +} + + +function joinSchedule(schedule) { + var busyIntervals = []; + var totalBusyInterval = schedule[0]; + + for (var i = 0; i < schedule.length; i++) { + var isIncludedTime = isIncluded(schedule[i].from, totalBusyInterval); + + if (isIncludedTime) { + totalBusyInterval.to = getLaterTime(schedule[i].to, totalBusyInterval.to); + } else { + busyIntervals.push(totalBusyInterval); + totalBusyInterval = schedule[i]; + } + } + busyIntervals.push(totalBusyInterval); + + return busyIntervals; +} + + +function isCrossedIntervals(bankInterval, freeInterval) { + var isIncludedTimeFrom = isIncluded(bankInterval.from, freeInterval); + var isIncludedTimeTo = isIncluded(bankInterval.to, freeInterval); + var isRobberyTimes = freeInterval.from > bankInterval.from && + freeInterval.to < bankInterval.to; + + return isIncludedTimeFrom || isIncludedTimeTo || isRobberyTimes; +} + +function getBankTimezone(time) { + var bankTimezone = /\+(\d+)/.exec(time)[1]; + + return parseInt(bankTimezone, 10); +} + +function getMomentsForRobbery(freeSchedule, bankSchedule, duration) { + var robberyTimes = []; + + bankSchedule.forEach(function (bankInterval) { + freeSchedule.forEach(function (freeInterval) { + if (isCrossedIntervals(bankInterval, freeInterval)) { + var crossInterval = { + from: new Date(Math.max(bankInterval.from, freeInterval.from)), + to: new Date(Math.min(bankInterval.to, freeInterval.to)) + }; + + var laterTime = new Date(crossInterval.from.getTime() + (duration * MS_IN_MINUTE)); + if (isIncluded(laterTime, crossInterval)) { + robberyTimes.push(crossInterval); + } + } + }); + }); + + return robberyTimes; +} + +function getFreeSchedule(busyTime, bankTimezone) { + var freeIntervals = []; + var currentFreeInterval = {}; + + currentFreeInterval.from = getNewDate('ПН 00:00+' + bankTimezone, bankTimezone); + busyTime.forEach(function (currentBusyInterval) { + if (currentBusyInterval.from === currentBusyInterval.to) { + return; + } + currentFreeInterval.to = currentBusyInterval.from; + freeIntervals.push(currentFreeInterval); + currentFreeInterval = {}; + currentFreeInterval.from = currentBusyInterval.to; + }); + currentFreeInterval.to = getNewDate('СР 23:59+' + bankTimezone, bankTimezone); + freeIntervals.push(currentFreeInterval); + + return freeIntervals; +} + +function formatTime(time) { + + return (time < 10 ? '0' : '') + time; +} + +function createLaterTime(date, shift) { + + return new Date(date.getTime() + (shift * MS_IN_MINUTE)); +} + /** * @param {Object} schedule – Расписание Банды * @param {Number} duration - Время на ограбление в минутах @@ -15,7 +167,10 @@ exports.isStar = true; * @returns {Object} */ exports.getAppropriateMoment = function (schedule, duration, workingHours) { - console.info(schedule, duration, workingHours); + var bankTimezone = getBankTimezone(workingHours.from); + var freeTimeIntervals = getSortedIntervals(schedule, bankTimezone); + var bankSchedule = getBankSchedule(workingHours, bankTimezone); + var robberyTimes = getMomentsForRobbery(freeTimeIntervals, bankSchedule, duration); return { @@ -24,7 +179,7 @@ exports.getAppropriateMoment = function (schedule, duration, workingHours) { * @returns {Boolean} */ exists: function () { - return false; + return robberyTimes.length > 0; }, /** @@ -35,7 +190,16 @@ exports.getAppropriateMoment = function (schedule, duration, workingHours) { * @returns {String} */ format: function (template) { - return template; + if (!this.exists()) { + return ''; + } + + var time = robberyTimes[0].from; + + return template + .replace('%HH', formatTime(time.getHours())) + .replace('%DD', ROBBERY_DAYS[time.getDay() - 1]) + .replace('%MM', formatTime(time.getMinutes())); }, /** @@ -44,6 +208,21 @@ exports.getAppropriateMoment = function (schedule, duration, workingHours) { * @returns {Boolean} */ tryLater: function () { + if (this.exists()) { + var laterTime = createLaterTime(robberyTimes[0].from, TIME_SHIFT); + var newEndTime = createLaterTime(laterTime, duration); + if (isIncluded(newEndTime, robberyTimes[0])) { + robberyTimes[0].from = laterTime; + + return true; + // из тестов "не должен сдвигать момент, если более позднего нет" + } else if (robberyTimes.length > 1) { + robberyTimes.shift(); + + return true; + } + } + return false; } };