From 53099efbbd30ddb0ccd77eca2a197cf10f55a5ea Mon Sep 17 00:00:00 2001 From: PavelTrofimov Date: Wed, 26 Oct 2016 12:44:15 +0600 Subject: [PATCH 1/5] init --- robbery.js | 211 +++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 180 insertions(+), 31 deletions(-) diff --git a/robbery.js b/robbery.js index 4a8309d..d623ff5 100644 --- a/robbery.js +++ b/robbery.js @@ -1,49 +1,198 @@ '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} - */ +var days = ['ПН', 'ВТ', 'СР']; + +function getBankSchedule(bankHours, bankTimezone) { + + return days.map(function (day) { + var from = day + ' ' + bankHours.from; + var to = day + ' ' + bankHours.to; + + return getInterval(from, to, bankTimezone); + }); +} + +function getBankDate(date, bankTimezone) { + var timeRegExp = /([А-Я]{2}) (\d{2}):(\d{2})\+(\d+)/g; + var parsedTime = timeRegExp.exec(date); + var day = days.indexOf(parsedTime[1]) + 1; + var hour = parsedTime[2] - (parseInt(parsedTime[4]) - 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(getInterval(from, to, bankTimezone)); + }); + }); + + return parsedSchedule; +} + + +function getInterval(from, to, timezone) { + + return { + from: getBankDate(from, timezone), + to: getBankDate(to, timezone) + }; +} + + +function getSotredIntervals(schedule, bankTimezone) { + var sortedSchedule = parseSchedule(schedule, bankTimezone).sort(function (a, b) { + return a.from - b.from; + }); + var joinedSchedule = joinSchedule(sortedSchedule); + + return getFreeSchedule(joinedSchedule, bankTimezone); +} + +function checkInterval(time, interval) { + + return interval.from <= time && time <= interval.to; +} + +function createDate(currentTime, firstTime) { + var greather = Math.max(currentTime, firstTime); + + return new Date(greather); +} + + +function joinSchedule(schedule) { + var fullSchedule = []; + var firstInterval = schedule[0]; + + for (var i = 0; i < schedule.length; i++) { + var check = checkInterval(schedule[i].from, firstInterval); + + if (check) { + firstInterval.to = createDate(schedule[i].to, firstInterval.to); + } else { + fullSchedule.push(firstInterval); + firstInterval = schedule[i]; + } + } + fullSchedule.push(firstInterval); + + return fullSchedule; +} + + +function isCrossed(bankInterval, freeInterval) { + var checkFrom = checkInterval(bankInterval.from, freeInterval); + var checkTo = checkInterval(bankInterval.to, freeInterval); + var compareIntervals = freeInterval.from > bankInterval.from && + freeInterval.to < bankInterval.to; + + return checkFrom || checkTo || compareIntervals; +} + +function getBankTimezone(time) { + var bankTimezone = /\+(\d+)/.exec(time)[1]; + + return parseInt(bankTimezone); +} + +function getMomentForAttack(freeSchedule, bankSchedule, duration) { + var timesToAttack = []; + + bankSchedule.forEach(function (bankInterval) { + freeSchedule.forEach(function (freeInterval) { + if (isCrossed(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 * 60 * 1000)); + if (checkInterval(laterTime, crossInterval)) { + timesToAttack.push(crossInterval); + } + } + }); + }); + + return timesToAttack; +} + +function getFreeSchedule(busyTime, bankTimezone) { + var freeTime = []; + var interval = {}; + + interval.from = getBankDate('ПН 00:00+' + bankTimezone, bankTimezone); + busyTime.forEach(function (currentInterval) { + if (currentInterval.from === currentInterval.to) { + return; + } + interval.to = currentInterval.from; + freeTime.push(interval); + interval = {}; + interval.from = currentInterval.to; + }); + interval.to = getBankDate('СР 23:59+' + bankTimezone, bankTimezone); + freeTime.push(interval); + + return freeTime; +} + +function formatTime(time) { + + return (time.toString().length === 2 ? '' : '0') + time.toString(); +} + +function createLaterTime(date, shift) { + + return new Date(date.getTime() + (shift * 60 * 1000)); +} + exports.getAppropriateMoment = function (schedule, duration, workingHours) { - console.info(schedule, duration, workingHours); + var bankTimezone = getBankTimezone(workingHours.from); + var freeTimeIntervals = getSotredIntervals(schedule, bankTimezone); + var bankSchedule = getBankSchedule(workingHours, bankTimezone); + var robberyTime = getMomentForAttack(freeTimeIntervals, bankSchedule, duration); return { - /** - * Найдено ли время - * @returns {Boolean} - */ exists: function () { - return false; + return robberyTime.length > 0; }, - /** - * Возвращает отформатированную строку с часами для ограбления - * Например, - * "Начинаем в %HH:%MM (%DD)" -> "Начинаем в 14:59 (СР)" - * @param {String} template - * @returns {String} - */ format: function (template) { - return template; + if (!this.exists()) { + return ''; + } + + var time = robberyTime[0].from; + + return template.replace('%HH', formatTime(time.getHours())) + .replace('%DD', days[time.getDay() - 1]) + .replace('%MM', formatTime(time.getMinutes())); }, - /** - * Попробовать найти часы для ограбления позже [*] - * @star - * @returns {Boolean} - */ tryLater: function () { + if (this.exists()) { + var laterTime = createLaterTime(robberyTime[0].from, 30); + var newEndTime = createLaterTime(laterTime, duration); + if (checkInterval(newEndTime, robberyTime[0])) { + robberyTime[0].from = laterTime; + + return true; + } else if (robberyTime.length > 1) { + robberyTime.shift(); + + return true; + } + } + return false; } }; From 7b242b08d6453637e432c0605204d077bdb4a346 Mon Sep 17 00:00:00 2001 From: PavelTrofimov Date: Fri, 28 Oct 2016 23:59:23 +0600 Subject: [PATCH 2/5] =?UTF-8?q?=D0=B8=D0=B7=D0=BC=D0=B5=D0=BD=D0=B5=D0=BD?= =?UTF-8?q?=D0=B8=D1=8F=20=D0=BF=D0=BE=20=D0=BA=D0=BE=D0=BC=D0=BC=D0=B5?= =?UTF-8?q?=D0=BD=D1=82=D0=B0=D1=80=D0=B8=D1=8F=D0=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- robbery.js | 148 ++++++++++++++++++++++++++++++++--------------------- 1 file changed, 89 insertions(+), 59 deletions(-) diff --git a/robbery.js b/robbery.js index d623ff5..9498caa 100644 --- a/robbery.js +++ b/robbery.js @@ -2,23 +2,26 @@ exports.isStar = true; -var days = ['ПН', 'ВТ', 'СР']; +var ROBBERY_DAYS = ['ПН', 'ВТ', 'СР']; +var MS_IN_MINUTE = 60 * 1000; +// сдвиг на 30 минут +var TIME_SHIFT = 30; function getBankSchedule(bankHours, bankTimezone) { - return days.map(function (day) { + return ROBBERY_DAYS.map(function (day) { var from = day + ' ' + bankHours.from; var to = day + ' ' + bankHours.to; - return getInterval(from, to, bankTimezone); + return getNewInterval(from, to, bankTimezone); }); } -function getBankDate(date, bankTimezone) { +function getNewDate(date, bankTimezone) { var timeRegExp = /([А-Я]{2}) (\d{2}):(\d{2})\+(\d+)/g; var parsedTime = timeRegExp.exec(date); - var day = days.indexOf(parsedTime[1]) + 1; - var hour = parsedTime[2] - (parseInt(parsedTime[4]) - bankTimezone); + 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]); } @@ -29,7 +32,7 @@ function parseSchedule(schedule, bankTimezone) { schedule[robberName].forEach(function (interval) { var from = interval.from; var to = interval.to; - parsedSchedule.push(getInterval(from, to, bankTimezone)); + parsedSchedule.push(getNewInterval(from, to, bankTimezone)); }); }); @@ -37,17 +40,18 @@ function parseSchedule(schedule, bankTimezone) { } -function getInterval(from, to, timezone) { +function getNewInterval(from, to, timezone) { return { - from: getBankDate(from, timezone), - to: getBankDate(to, timezone) + from: getNewDate(from, timezone), + to: getNewDate(to, timezone) }; } -function getSotredIntervals(schedule, bankTimezone) { - var sortedSchedule = parseSchedule(schedule, bankTimezone).sort(function (a, b) { +function getSortedIntervals(schedule, bankTimezone) { + var sortedSchedule = parseSchedule(schedule, bankTimezone); + sortedSchedule.sort(function (a, b) { return a.from - b.from; }); var joinedSchedule = joinSchedule(sortedSchedule); @@ -61,133 +65,159 @@ function checkInterval(time, interval) { } function createDate(currentTime, firstTime) { - var greather = Math.max(currentTime, firstTime); + var greater = Math.max(currentTime, firstTime); - return new Date(greather); + return new Date(greater); } function joinSchedule(schedule) { - var fullSchedule = []; - var firstInterval = schedule[0]; + var busyIntervals = []; + var totalDayInterval = schedule[0]; for (var i = 0; i < schedule.length; i++) { - var check = checkInterval(schedule[i].from, firstInterval); + var check = checkInterval(schedule[i].from, totalDayInterval); if (check) { - firstInterval.to = createDate(schedule[i].to, firstInterval.to); + totalDayInterval.to = createDate(schedule[i].to, totalDayInterval.to); } else { - fullSchedule.push(firstInterval); - firstInterval = schedule[i]; + busyIntervals.push(totalDayInterval); + totalDayInterval = schedule[i]; } } - fullSchedule.push(firstInterval); + busyIntervals.push(totalDayInterval); - return fullSchedule; + return busyIntervals; } -function isCrossed(bankInterval, freeInterval) { - var checkFrom = checkInterval(bankInterval.from, freeInterval); - var checkTo = checkInterval(bankInterval.to, freeInterval); - var compareIntervals = freeInterval.from > bankInterval.from && +function isCrossedIntervals(bankInterval, freeInterval) { + var checkTimeFrom = checkInterval(bankInterval.from, freeInterval); + var checkTimeTo = checkInterval(bankInterval.to, freeInterval); + var compareTimes = freeInterval.from > bankInterval.from && freeInterval.to < bankInterval.to; - return checkFrom || checkTo || compareIntervals; + return checkTimeFrom || checkTimeTo || compareTimes; } function getBankTimezone(time) { var bankTimezone = /\+(\d+)/.exec(time)[1]; - return parseInt(bankTimezone); + return parseInt(bankTimezone, 10); } -function getMomentForAttack(freeSchedule, bankSchedule, duration) { - var timesToAttack = []; +function getMomentsForRobbery(freeSchedule, bankSchedule, duration) { + var timesToRob = []; bankSchedule.forEach(function (bankInterval) { freeSchedule.forEach(function (freeInterval) { - if (isCrossed(bankInterval, 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 * 60 * 1000)); + var laterTime = new Date(crossInterval.from.getTime() + (duration * MS_IN_MINUTE)); if (checkInterval(laterTime, crossInterval)) { - timesToAttack.push(crossInterval); + timesToRob.push(crossInterval); } } }); }); - return timesToAttack; + return timesToRob; } function getFreeSchedule(busyTime, bankTimezone) { - var freeTime = []; - var interval = {}; + var freeIntervals = []; + var currentFreeInterval = {}; - interval.from = getBankDate('ПН 00:00+' + bankTimezone, bankTimezone); - busyTime.forEach(function (currentInterval) { - if (currentInterval.from === currentInterval.to) { + currentFreeInterval.from = getNewDate('ПН 00:00+' + bankTimezone, bankTimezone); + busyTime.forEach(function (currentBusyInterval) { + if (currentBusyInterval.from === currentBusyInterval.to) { return; } - interval.to = currentInterval.from; - freeTime.push(interval); - interval = {}; - interval.from = currentInterval.to; + currentFreeInterval.to = currentBusyInterval.from; + freeIntervals.push(currentFreeInterval); + currentFreeInterval = {}; + currentFreeInterval.from = currentBusyInterval.to; }); - interval.to = getBankDate('СР 23:59+' + bankTimezone, bankTimezone); - freeTime.push(interval); + currentFreeInterval.to = getNewDate('СР 23:59+' + bankTimezone, bankTimezone); + freeIntervals.push(currentFreeInterval); - return freeTime; + return freeIntervals; } function formatTime(time) { - return (time.toString().length === 2 ? '' : '0') + time.toString(); + return (time < 10 ? '0' : '') + time; } function createLaterTime(date, shift) { - return new Date(date.getTime() + (shift * 60 * 1000)); + return new Date(date.getTime() + (shift * MS_IN_MINUTE)); } +/** + * @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.getAppropriateMoment = function (schedule, duration, workingHours) { var bankTimezone = getBankTimezone(workingHours.from); - var freeTimeIntervals = getSotredIntervals(schedule, bankTimezone); + var freeTimeIntervals = getSortedIntervals(schedule, bankTimezone); var bankSchedule = getBankSchedule(workingHours, bankTimezone); - var robberyTime = getMomentForAttack(freeTimeIntervals, bankSchedule, duration); + var robberyTimes = getMomentsForRobbery(freeTimeIntervals, bankSchedule, duration); return { + /** + * Найдено ли время + * @returns {Boolean} + */ exists: function () { - return robberyTime.length > 0; + return robberyTimes.length > 0; }, + /** + * Возвращает отформатированную строку с часами для ограбления + * Например, + * "Начинаем в %HH:%MM (%DD)" -> "Начинаем в 14:59 (СР)" + * @param {String} template + * @returns {String} + */ format: function (template) { if (!this.exists()) { return ''; } - var time = robberyTime[0].from; + var time = robberyTimes[0].from; - return template.replace('%HH', formatTime(time.getHours())) - .replace('%DD', days[time.getDay() - 1]) + return template + .replace('%HH', formatTime(time.getHours())) + .replace('%DD', ROBBERY_DAYS[time.getDay() - 1]) .replace('%MM', formatTime(time.getMinutes())); }, + /** + * Попробовать найти часы для ограбления позже [*] + * @star + * @returns {Boolean} + */ tryLater: function () { if (this.exists()) { - var laterTime = createLaterTime(robberyTime[0].from, 30); + var laterTime = createLaterTime(robberyTimes[0].from, TIME_SHIFT); var newEndTime = createLaterTime(laterTime, duration); - if (checkInterval(newEndTime, robberyTime[0])) { - robberyTime[0].from = laterTime; + if (checkInterval(newEndTime, robberyTimes[0])) { + robberyTimes[0].from = laterTime; return true; - } else if (robberyTime.length > 1) { - robberyTime.shift(); + // из тестов "не должен сдвигать момент, если более позднего нет" + } else if (robberyTimes.length > 1) { + robberyTimes.shift(); return true; } From 81d99a792d198405e4a1b652ae8803c53fdecb04 Mon Sep 17 00:00:00 2001 From: PavelTrofimov Date: Wed, 2 Nov 2016 22:57:12 +0600 Subject: [PATCH 3/5] =?UTF-8?q?=D0=B8=D0=B7=D0=BC=D0=B5=D0=BD=D0=B5=D0=BD?= =?UTF-8?q?=D1=8B=20=D0=BD=D0=B0=D0=B7=D0=B2=D0=B0=D0=BD=D0=B8=D1=8F=20?= =?UTF-8?q?=D0=BF=D0=B5=D1=80=D0=B5=D0=BC=D0=B5=D0=BD=D0=BD=D1=8B=D1=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- robbery.js | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/robbery.js b/robbery.js index 9498caa..3296c7e 100644 --- a/robbery.js +++ b/robbery.js @@ -59,7 +59,7 @@ function getSortedIntervals(schedule, bankTimezone) { return getFreeSchedule(joinedSchedule, bankTimezone); } -function checkInterval(time, interval) { +function isIncluded(time, interval) { return interval.from <= time && time <= interval.to; } @@ -73,31 +73,31 @@ function createDate(currentTime, firstTime) { function joinSchedule(schedule) { var busyIntervals = []; - var totalDayInterval = schedule[0]; + var totalBusyInterval = schedule[0]; for (var i = 0; i < schedule.length; i++) { - var check = checkInterval(schedule[i].from, totalDayInterval); + var isIncluded = isIncluded(schedule[i].from, totalBusyInterval); - if (check) { - totalDayInterval.to = createDate(schedule[i].to, totalDayInterval.to); + if (isIncluded) { + totalBusyInterval.to = createDate(schedule[i].to, totalBusyInterval.to); } else { - busyIntervals.push(totalDayInterval); - totalDayInterval = schedule[i]; + busyIntervals.push(totalBusyInterval); + totalBusyInterval = schedule[i]; } } - busyIntervals.push(totalDayInterval); + busyIntervals.push(totalBusyInterval); return busyIntervals; } function isCrossedIntervals(bankInterval, freeInterval) { - var checkTimeFrom = checkInterval(bankInterval.from, freeInterval); - var checkTimeTo = checkInterval(bankInterval.to, freeInterval); - var compareTimes = freeInterval.from > bankInterval.from && + var isIncludedTimeFrom = isIncluded(bankInterval.from, freeInterval); + var isIncludedTimeTo = isIncluded(bankInterval.to, freeInterval); + var isRobberyTimes = freeInterval.from > bankInterval.from && freeInterval.to < bankInterval.to; - return checkTimeFrom || checkTimeTo || compareTimes; + return isIncludedTimeFrom || isIncludedTimeTo || isRobberyTimes; } function getBankTimezone(time) { @@ -107,7 +107,7 @@ function getBankTimezone(time) { } function getMomentsForRobbery(freeSchedule, bankSchedule, duration) { - var timesToRob = []; + var robberyTimes = []; bankSchedule.forEach(function (bankInterval) { freeSchedule.forEach(function (freeInterval) { @@ -118,14 +118,14 @@ function getMomentsForRobbery(freeSchedule, bankSchedule, duration) { }; var laterTime = new Date(crossInterval.from.getTime() + (duration * MS_IN_MINUTE)); - if (checkInterval(laterTime, crossInterval)) { - timesToRob.push(crossInterval); + if (isIncluded(laterTime, crossInterval)) { + robberyTimes.push(crossInterval); } } }); }); - return timesToRob; + return robberyTimes; } function getFreeSchedule(busyTime, bankTimezone) { @@ -211,7 +211,7 @@ exports.getAppropriateMoment = function (schedule, duration, workingHours) { if (this.exists()) { var laterTime = createLaterTime(robberyTimes[0].from, TIME_SHIFT); var newEndTime = createLaterTime(laterTime, duration); - if (checkInterval(newEndTime, robberyTimes[0])) { + if (isIncluded(newEndTime, robberyTimes[0])) { robberyTimes[0].from = laterTime; return true; From df9c5d05db2a0925344a1ec58d61881458403468 Mon Sep 17 00:00:00 2001 From: PavelTrofimov Date: Wed, 2 Nov 2016 23:02:38 +0600 Subject: [PATCH 4/5] lint --- robbery.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/robbery.js b/robbery.js index 3296c7e..4dd2910 100644 --- a/robbery.js +++ b/robbery.js @@ -76,9 +76,9 @@ function joinSchedule(schedule) { var totalBusyInterval = schedule[0]; for (var i = 0; i < schedule.length; i++) { - var isIncluded = isIncluded(schedule[i].from, totalBusyInterval); + var isIncludedTime = isIncluded(schedule[i].from, totalBusyInterval); - if (isIncluded) { + if (isIncludedTime) { totalBusyInterval.to = createDate(schedule[i].to, totalBusyInterval.to); } else { busyIntervals.push(totalBusyInterval); From e5b889adc9c0ceaa9a796f72abe9638f7645d38c Mon Sep 17 00:00:00 2001 From: PavelTrofimov Date: Wed, 2 Nov 2016 23:29:44 +0600 Subject: [PATCH 5/5] lint --- robbery.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/robbery.js b/robbery.js index 4dd2910..fe3b4bb 100644 --- a/robbery.js +++ b/robbery.js @@ -64,8 +64,8 @@ function isIncluded(time, interval) { return interval.from <= time && time <= interval.to; } -function createDate(currentTime, firstTime) { - var greater = Math.max(currentTime, firstTime); +function getLaterTime(firstTime, secondTime) { + var greater = Math.max(firstTime, secondTime); return new Date(greater); } @@ -79,7 +79,7 @@ function joinSchedule(schedule) { var isIncludedTime = isIncluded(schedule[i].from, totalBusyInterval); if (isIncludedTime) { - totalBusyInterval.to = createDate(schedule[i].to, totalBusyInterval.to); + totalBusyInterval.to = getLaterTime(schedule[i].to, totalBusyInterval.to); } else { busyIntervals.push(totalBusyInterval); totalBusyInterval = schedule[i];