Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
194 changes: 190 additions & 4 deletions robbery.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,12 @@
* Сделано задание на звездочку
* Реализовано оба метода и tryLater
*/
exports.isStar = true;
exports.isStar = false;

var MINUTES_IN_HOUR = 60;
var MINUTES_IN_DAY = MINUTES_IN_HOUR * 24;
var WEEKDAYS = ['ПН', 'ВТ', 'СР', 'ЧТ', 'ПТ', 'СБ', 'ВС'];
var ROBBERY_DAYS_COUNT = 3;

/**
* @param {Object} schedule – Расписание Банды
Expand All @@ -14,8 +19,14 @@ exports.isStar = true;
* @param {String} workingHours.to – Время закрытия, например, "18:00+5"
* @returns {Object}
*/


exports.getAppropriateMoment = function (schedule, duration, workingHours) {
console.info(schedule, duration, workingHours);
var bankSchedule = getScheduleInMinutesForBank(workingHours);
var commonSchedule = getCommonSchedule(schedule, bankSchedule[0].timeZone);
var freeTimeIntervals = findFreeTime(commonSchedule);
var robberyIntervals = intersect(freeTimeIntervals, bankSchedule);
var moment = getRobberyMomentTime(robberyIntervals, duration);

return {

Expand All @@ -24,7 +35,7 @@ exports.getAppropriateMoment = function (schedule, duration, workingHours) {
* @returns {Boolean}
*/
exists: function () {
return false;
return moment !== null;
},

/**
Expand All @@ -35,7 +46,15 @@ exports.getAppropriateMoment = function (schedule, duration, workingHours) {
* @returns {String}
*/
format: function (template) {
return template;
if (!this.exists()) {
return '';
}

var formattedTime = getDateFromMinutes(moment.from);

return template.replace('%HH', formattedTime.hours)
.replace('%MM', formattedTime.minutes)
.replace('%DD', formattedTime.day);
},

/**
Expand All @@ -48,3 +67,170 @@ exports.getAppropriateMoment = function (schedule, duration, workingHours) {
}
};
};

function getIntervalInMinutes(interval, timeZone) {
return {
from: getMinutes(interval.from, timeZone),
to: getMinutes(interval.to, timeZone)
};
}

function getMinutes(str, timeZone) {
var separators = /[ :+]/;
var data = str.split(separators);
var timeInMinutes = parseInt(data[1], 10) * MINUTES_IN_HOUR + parseInt(data[2], 10) +
(parseInt(timeZone, 10) - parseInt(data[3], 10)) * MINUTES_IN_HOUR;

return MINUTES_IN_DAY * WEEKDAYS.indexOf(data[0]) + timeInMinutes;
}

function getScheduleInMinutesForBank(workingHours) {
var from = workingHours.from.split(/[:+]/);
var to = workingHours.to.split(/[:+]/);

var schedule = [];
var minutesFrom = parseInt(from[0], 10) * MINUTES_IN_HOUR + parseInt(from[1], 10);
var minutesTo = parseInt(to[0], 10) * MINUTES_IN_HOUR + parseInt(to[1], 10);
for (var i = 0; i < 3; i++) {
schedule.push({
from: minutesFrom + MINUTES_IN_DAY * i,
to: minutesTo + MINUTES_IN_DAY * i,
timeZone: from[2]
});
}

return schedule;
}

function splitSchedule(schedule) {
var firstInterval = {
from: 0,
to: schedule[0].from
};

var secondInterval = {
from: schedule[0].to,
to: MINUTES_IN_DAY * ROBBERY_DAYS_COUNT
};

return [firstInterval, secondInterval];
}

function findFreeTime(schedule) {
var freeTimeIntervals = splitSchedule(schedule);

schedule.forEach(function (interval) {
var last = freeTimeIntervals.length - 1;
var elem = freeTimeIntervals[last];

if (interval.to <= elem.from) {

return;
}

// Интервалы пересекаются
if ((interval.from <= elem.from) && (elem.from < interval.to)) {
freeTimeIntervals[last] = {
from: interval.to,
to: MINUTES_IN_DAY * ROBBERY_DAYS_COUNT
};

return;
}

// Один внутри другого
if (interval.from >= elem.from) {
freeTimeIntervals[last] = {
from: elem.from,
to: interval.from
};
freeTimeIntervals.push({
from: interval.to,
to: MINUTES_IN_DAY * ROBBERY_DAYS_COUNT
});
}
});

return freeTimeIntervals;
}

function getCommonSchedule(gangSchedule, timeZone) {
var commonSchedule = [];

Object.keys(gangSchedule).forEach(function (name) {
commonSchedule = commonSchedule.concat(gangSchedule[name]
.map(function (interval) {
return getIntervalInMinutes(interval, timeZone);
}));
});
commonSchedule.sort(compare);

return commonSchedule;
}

function intersectIntervals(bankTime, freeTime) {
var result = {};
var isBankStartAfter = freeTime.to < bankTime.from;
var isBankEndBefore = bankTime.to < freeTime.from;

// если есть пересечение
if (!(isBankStartAfter || isBankEndBefore)) {
result = {
from: Math.max(bankTime.from, freeTime.from),
to: Math.min(bankTime.to, freeTime.to)
};
}

return result;
}

function intersect(freeTime, bankTime) {
var result = [];

bankTime.forEach(function (bankInterval) {
var intervals = freeTime.map(function (freeTimeInterval) {
return intersectIntervals(bankInterval, freeTimeInterval);
});
result = result.concat(intervals);
});

return result;
}

function isEnoughForRobbery(interval, duration) {
return interval.to - interval.from >= duration;
}

function getRobberyMomentTime(availableIntervals, duration) {
for (var i = 0; i < availableIntervals.length; i++) {

if (isEnoughForRobbery(availableIntervals[i], duration)) {
return availableIntervals[i];
}
}

return null;
}

function compare(first, second) {
return Math.sign(first.from - second.from);
}

function getDateFromMinutes(minutes) {
var dayNumber = Math.floor(minutes / MINUTES_IN_DAY);
var day = WEEKDAYS[dayNumber];
minutes = minutes - dayNumber * MINUTES_IN_DAY;

var hours = Math.floor(minutes / MINUTES_IN_HOUR);
minutes = minutes - hours * MINUTES_IN_HOUR;

return {
day: day,
hours: formatTime(hours),
minutes: formatTime(minutes)
};
}

function formatTime(time) {
return time >= 10 ? time : '0' + time;
}