From c8725ee46909574e0fdd296ffd9b9484fd613cf0 Mon Sep 17 00:00:00 2001 From: qmzik Date: Thu, 19 Oct 2017 00:48:48 +0500 Subject: [PATCH 1/2] done --- package-lock.json | 16 +-- robbery.js | 315 ++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 315 insertions(+), 16 deletions(-) diff --git a/package-lock.json b/package-lock.json index 2b4e704..6087ec1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1029,6 +1029,14 @@ "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=" }, + "string_decoder": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz", + "integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==", + "requires": { + "safe-buffer": "5.1.1" + } + }, "string-width": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", @@ -1038,14 +1046,6 @@ "strip-ansi": "4.0.0" } }, - "string_decoder": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz", - "integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==", - "requires": { - "safe-buffer": "5.1.1" - } - }, "strip-ansi": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", diff --git a/robbery.js b/robbery.js index 4a8309d..29db301 100644 --- a/robbery.js +++ b/robbery.js @@ -1,10 +1,267 @@ 'use strict'; +exports.isStar = true; + +const CURRENT_YEAR = 2017; +const CURRENT_MONTH = 10; +const HOUR_IN_MILLISEC = 1000 * 60 * 60; +const MINUTE_IN_MILLISEC = 1000 * 60; +const HALF_HOUR = 30; +const HALF_HOUR_IN_MILLISEC = HALF_HOUR * MINUTE_IN_MILLISEC; +const ROBBERY_DAYS = ['ПН', 'ВТ', 'СР']; + + /** - * Сделано задание на звездочку - * Реализовано оба метода и tryLater + * @param {String} string + * @returns {String} */ -exports.isStar = true; +function getDay(string) { + return string.substr(0, 2); +} + +/** + * @param {String} day + * @returns {Number} + */ +function getNumberOfDay(day) { + return ROBBERY_DAYS.indexOf(getDay(day)) + 1; +} + +/** + * @param {String} string + * @returns {Number} + */ +function getHours(string) { + return string.substring(3, 5); +} + +/** + * @param {String} string + * @returns {Number} + */ +function getMinutes(string) { + return string.substring(6, 8); +} + + +/** + * @param {String} time + * @returns {Number} + */ +function getTimeZone(time) { + return time.substr(-1); +} + +/** + * @param {String} string + * @param {Number} day + * @param {Number} hours + * @param {Number} minutes + * @returns {String} + */ +function getCorrectFormat(string, day, hours, minutes) { + return string + .replace('%DD', day) + .replace('%HH', hours) + .replace('%MM', minutes); +} + +/** + * @param {Array} schedule + * @returns {Array} + */ +function crossElements(schedule) { + let connectedSchedule = []; + let start = 0; + let finish = 0; + schedule.forEach((time, i) => { + start = time.from; + finish = time.to; + schedule.forEach((time2, j) => { + if (i !== j && + start <= time2.to && finish >= time2.from) { + start = Math.max(start, time2.from); + finish = Math.min(finish, time2.to); + } + }); + connectedSchedule.push({ from: start, to: finish }); + }); + + return connectedSchedule; +} + +/** + * @param {String} time + * @param {Number} day + * @param {Number} timezone + * @returns {Object} + */ +function bankUTCTime(time, day, timezone) { + return Date.UTC( + CURRENT_YEAR, + CURRENT_MONTH, + day + 1, + time.substring(0, 2), + time.substring(3, 5)) - timezone; +} + +/** + * @param {Object} time + * @returns {Array} + */ +function toBankUTCTime(time) { + let scheduleBank = []; + let timezone = getTimeZone(time.from) * HOUR_IN_MILLISEC; + for (let i = 0; i <= 2; i++) { + let from = bankUTCTime(time.from, i, timezone); + let to = bankUTCTime(time.to, i, timezone); + scheduleBank.push({ from, to }); + } + + return scheduleBank; +} + +/** + * @param {Object} time + * @param {Number} timezone + * @returns {Object} + */ +function utcTimeForRobber(time, timezone) { + return Date.UTC( + CURRENT_YEAR, + CURRENT_MONTH, + getNumberOfDay(time), + getHours(time), + getMinutes(time)) - timezone; +} + +/** + * @param {Object} time + * @returns {Object} + */ +function toUTC(time) { + let timezone = getTimeZone(time.from) * HOUR_IN_MILLISEC; + time.from = utcTimeForRobber(time.from, timezone); + time.to = utcTimeForRobber(time.to, timezone); + + return time; +} + +/** + * @param {Object} schedule + * @returns {Array} + */ +function mergeTime(schedule) { + let merged = []; + Object.keys(schedule).forEach(robber => { + schedule[robber].forEach(time => { + merged.push({ from: time.from, to: time.to }); + }); + }); + + return merged; +} + +/** + * @param {Array} schedule + * @returns {Array} + */ +function mixSchedule(schedule) { + let connectedSchedule = []; + let start = 0; + let finish = 0; + schedule.forEach((time, i) => { + start = time.from; + finish = time.to; + schedule.forEach((time2, j) => { + if (i !== j && + start <= time2.to && finish >= time2.from) { + start = Math.min(start, time2.from); + finish = Math.max(finish, time2.to); + } + }); + connectedSchedule.push({ from: start, to: finish }); + }); + + return connectedSchedule; +} + +/** + * @param {Array} schedule + * @returns {Array} + */ +function deleteCopies(schedule) { + if (schedule.length !== 0) { + schedule.reduceRight((previousValue, currentValue, index) => { + if (currentValue.from === previousValue.from && currentValue.to === previousValue.to) { + schedule.splice(index, 1); + } + + return currentValue; + }); + } + + return schedule; +} + +/** + * @param {Array} commonSchedule + * @param {Array} bankSchedule + * @param {Number} timezone + * @param {Number} duration + * @returns {Array} + */ +function reverseSchedule(commonSchedule, bankSchedule, timezone, duration) { + if ((bankSchedule[0].to - bankSchedule[0].from) - duration * MINUTE_IN_MILLISEC < 0) { + return []; + } + let freeTimes = []; + let range = { + from: Date.UTC(CURRENT_YEAR, CURRENT_MONTH, 1, 0, 0) - timezone, + to: Date.UTC(CURRENT_YEAR, CURRENT_MONTH, 3, 23, 59) - timezone }; + bankSchedule.forEach(day => { + commonSchedule.push({ from: range.from, to: day.from }); + commonSchedule.push({ from: day.to, to: range.to }); + }); + commonSchedule.forEach(badTime => { + if (Math.max(range.from, badTime.from) === Math.min(range.to, badTime.from) && + badTime.from !== range.from) { + freeTimes.push({ from: range.from, to: badTime.from }); + } + if (Math.max(range.from, badTime.to) === Math.min(range.to, badTime.to) && + badTime.to !== range.to) { + freeTimes.push({ from: badTime.to, to: range.to }); + } + }); + + return freeTimes; +} + +/** + * @param {Array} freeTime + * @param {Number} duration + * @returns {Array} + */ +function findFreeTimeForRobbers(freeTime, duration) { + duration *= MINUTE_IN_MILLISEC; + let robberyTimes = []; + freeTime.forEach(time => { + if (time.to - time.from >= duration) { + robberyTimes.push(time); + } + }); + + return robberyTimes; +} + +/** + * @param {Object} element1 + * @param {Object} element2 + * @returns {Object} + */ +function forSort(element1, element2) { + return element1.from - element2.from; +} /** * @param {Object} schedule – Расписание Банды @@ -14,17 +271,33 @@ exports.isStar = true; * @param {String} workingHours.to – Время закрытия, например, "18:00+5" * @returns {Object} */ -exports.getAppropriateMoment = function (schedule, duration, workingHours) { - console.info(schedule, duration, workingHours); + +exports.getAppropriateMoment = (schedule, duration, workingHours) => { + let timezone = getTimeZone(workingHours.from); + let bankSchedule = toBankUTCTime(workingHours); + let mergedTime = mergeTime(schedule).map(time => toUTC(time)); + let commonTime = mixSchedule(mergedTime); + commonTime.sort(forSort); + let reversedSchedule = reverseSchedule( + deleteCopies(commonTime), bankSchedule, timezone * HOUR_IN_MILLISEC, duration); + let freeTime = crossElements(reversedSchedule); + freeTime.sort(forSort); + let timeForRobbery = findFreeTimeForRobbers(deleteCopies(freeTime), duration); + let start = 0; + if (timeForRobbery.length > 0) { + start = timeForRobbery[0].from; + } return { + interval: 0, + /** * Найдено ли время * @returns {Boolean} */ - exists: function () { - return false; + exists: () => { + return timeForRobbery.length !== 0; }, /** @@ -35,7 +308,16 @@ exports.getAppropriateMoment = function (schedule, duration, workingHours) { * @returns {String} */ format: function (template) { - return template; + if (!this.exists()) { + return ''; + } + let time = new Date(start + timezone * HOUR_IN_MILLISEC); + + return getCorrectFormat( + template, + ROBBERY_DAYS[time.getUTCDate() - 1], + ('0' + time.getUTCHours()).slice(-2), + ('0' + time.getUTCMinutes()).slice(-2)); }, /** @@ -44,6 +326,23 @@ exports.getAppropriateMoment = function (schedule, duration, workingHours) { * @returns {Boolean} */ tryLater: function () { + if (!this.exists()) { + return false; + } + if (this.interval < timeForRobbery.length) { + if (start + HALF_HOUR_IN_MILLISEC + duration * MINUTE_IN_MILLISEC <= + timeForRobbery[this.interval].to) { + start += HALF_HOUR_IN_MILLISEC; + + return true; + } else if (this.interval < timeForRobbery.length - 1) { + this.interval++; + start = timeForRobbery[this.interval].from; + + return true; + } + } + return false; } }; From 28d58e37cd53307d5723710c8adb349e5c5bb4f9 Mon Sep 17 00:00:00 2001 From: qmzik Date: Thu, 19 Oct 2017 00:54:45 +0500 Subject: [PATCH 2/2] small changes --- robbery.js | 33 ++++++++++++++++----------------- 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/robbery.js b/robbery.js index 29db301..c7d57ff 100644 --- a/robbery.js +++ b/robbery.js @@ -72,19 +72,18 @@ function getCorrectFormat(string, day, hours, minutes) { */ function crossElements(schedule) { let connectedSchedule = []; - let start = 0; - let finish = 0; + let from = 0; + let to = 0; schedule.forEach((time, i) => { - start = time.from; - finish = time.to; + from = time.from; + to = time.to; schedule.forEach((time2, j) => { - if (i !== j && - start <= time2.to && finish >= time2.from) { - start = Math.max(start, time2.from); - finish = Math.min(finish, time2.to); + if (i !== j && from <= time2.to && to >= time2.from) { + from = Math.max(from, time2.from); + to = Math.min(to, time2.to); } }); - connectedSchedule.push({ from: start, to: finish }); + connectedSchedule.push({ from, to }); }); return connectedSchedule; @@ -168,19 +167,19 @@ function mergeTime(schedule) { */ function mixSchedule(schedule) { let connectedSchedule = []; - let start = 0; - let finish = 0; + let from = 0; + let to = 0; schedule.forEach((time, i) => { - start = time.from; - finish = time.to; + from = time.from; + to = time.to; schedule.forEach((time2, j) => { if (i !== j && - start <= time2.to && finish >= time2.from) { - start = Math.min(start, time2.from); - finish = Math.max(finish, time2.to); + from <= time2.to && to >= time2.from) { + from = Math.min(from, time2.from); + to = Math.max(to, time2.to); } }); - connectedSchedule.push({ from: start, to: finish }); + connectedSchedule.push({ from, to }); }); return connectedSchedule;