From 88f88959e8d2bc5b4384b9cde3029e69e7959dce Mon Sep 17 00:00:00 2001 From: jellymary Date: Tue, 30 Oct 2018 17:55:45 +0500 Subject: [PATCH 1/6] init --- robbery.js | 223 ++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 221 insertions(+), 2 deletions(-) diff --git a/robbery.js b/robbery.js index ed84b5d..b55d395 100755 --- a/robbery.js +++ b/robbery.js @@ -4,7 +4,19 @@ * Сделано задание на звездочку * Реализовано оба метода и tryLater */ -const isStar = true; +const isStar = false; + +const DAYS = Object.freeze({ + 'ПН': 0, 'ВТ': 1, 'СР': 2, 'ЧТ': 3, 'ПТ': 4, 'СБ': 5, 'ВС': 6, + parseDay: function (dayNumber) { + const days = ['ПН', 'ВТ', 'СР', 'ЧТ', 'ПТ', 'СБ', 'ВС']; + + return days[parseInt(dayNumber)]; + }, + addDays: function (day, daysCount) { + return this.parseDay(this[day] + daysCount); + } +}); /** * @param {Object} schedule – Расписание Банды @@ -17,6 +29,15 @@ const isStar = true; function getAppropriateMoment(schedule, duration, workingHours) { console.info(schedule, duration, workingHours); + const bankWorkingHours = changeTimeSpan(parseTimestamp, workingHours); + const bankTimeZone = bankWorkingHours.from.timeZone; + const robbersSchedule = copyAndParseTimeSpan(schedule); + forEachRobber(transferScheduleToTimeZone, robbersSchedule, bankTimeZone); + forEachRobber(trimByDays, robbersSchedule); + forEachRobber(trimByHours, robbersSchedule, bankWorkingHours); + const appropriateTimes = getAppropriateTimes(robbersSchedule); + const appropriateMoments = checkDuration(appropriateTimes, duration); + return { /** @@ -24,7 +45,7 @@ function getAppropriateMoment(schedule, duration, workingHours) { * @returns {Boolean} */ exists: function () { - return false; + return appropriateMoments.length !== 0; }, /** @@ -34,6 +55,15 @@ function getAppropriateMoment(schedule, duration, workingHours) { * @returns {String} */ format: function (template) { + if (!this.exists()) { + return ''; + } + const start = appropriateMoments[0].from; + + template = template.replace(/%HH/, start.hours.toString().padStart(2, '0')) + .replace(/%MM/, start.minutes.toString().padStart(2, '0')) + .replace(/%DD/, start.weekDay); + return template; }, @@ -48,6 +78,195 @@ function getAppropriateMoment(schedule, duration, workingHours) { }; } +function changeTimeSpan(changer, timeSpan) { + const args = []; + for (let i = 2; i < arguments.length; i++) { + args.push(arguments[i]); + } + + return { + from: changer(timeSpan.from, args), + to: changer(timeSpan.to, args) + }; +} + +function forEachRobber(func, schedule) { + const robbers = Object.keys(schedule); + for (let i = 0; i < robbers.length; i++) { + const robber = robbers[i]; + schedule[robber] = func(schedule[robber], arguments[2]); + } +} + +function parseTimestamp(timestamp) { + const splitTimestamp = timestamp.split(/[ +:]/).reverse(); + + return getTimestamp( + splitTimestamp[3], + parseInt(splitTimestamp[2]), + parseInt(splitTimestamp[1]), + parseInt(splitTimestamp[0]) + ); +} + +function getTimestamp(weekDay, hours, minutes, timeZone) { + return { + weekDay, + hours, + minutes, + timeZone + }; +} + +function copyAndParseTimeSpan(schedule) { + const robbers = Object.keys(schedule); + const newSchedule = {}; + for (let i = 0; i < robbers.length; i++) { + const robber = robbers[i]; + Object.defineProperty(newSchedule, robber, { + value: [], + configurable: false, + writable: true, + enumerable: true + }); + for (let j = 0; j < schedule[robber].length; j++) { + newSchedule[robber].push(changeTimeSpan(parseTimestamp, schedule[robber][j])); + } + } + + return newSchedule; +} + +function transferScheduleToTimeZone(robberSchedule, targetTimeZone) { + targetTimeZone = parseInt(targetTimeZone); + const timeZone = robberSchedule[0].from.timeZone; + if (timeZone !== targetTimeZone) { + const timeShift = targetTimeZone - timeZone; + + return robberSchedule.map( + timeSpan => changeTimeSpan(shiftTimestamp, timeSpan, timeShift)); + } + + return robberSchedule; +} + +function shiftTimestamp(timestamp, timeShift) { + timeShift = parseInt(timeShift); + timestamp.timeZone += timeShift; + timestamp.hours += timeShift; + if (timestamp.hours >= 24 || timestamp.hours < 0) { + timestamp.weekDay = DAYS.addDays(timestamp.weekDay, timestamp.hours < 0 ? -1 : 1); + timestamp.hours %= 24; + } + + return timestamp; +} + +function trimByDays(robberSchedule) { + const newSchedule = []; + for (let j = 0; j < robberSchedule.length; j++) { + const timeSpan = robberSchedule[j]; + if (timeSpan.from.weekDay === timeSpan.to.weekDay) { + newSchedule.push(timeSpan); + } else { + newSchedule.push({ + from: timeSpan.from, + to: getTimestamp( + timeSpan.from.weekDay, + 23, 59, + timeSpan.from.timeZone + ) + }); + newSchedule.push({ + from: getTimestamp( + timeSpan.to.weekDay, + 0, 0, + timeSpan.to.timeZone + ), + to: timeSpan.to + }); + } + } + + return newSchedule; +} + +function trimByHours(robberSchedule, trimmerHours) { + const newSchedule = []; + for (let i = 0; i < robberSchedule.length; i++) { + const timeSpan = robberSchedule[i]; + if (trimmerHours.from.weekDay && trimmerHours.from.weekDay !== timeSpan.from.weekDay) { + newSchedule.push(timeSpan); + continue; + } + const trimTimeSpan = getTrimTimeSpan(timeSpan, trimmerHours); + if (compareTime(trimTimeSpan.from, trimTimeSpan.to) !== 1) { + newSchedule.push(trimTimeSpan); + } + } + + return newSchedule; +} + +function compareTime(time1, time2) { + if (time1.hours < time2.hours) { + return -1; + } else if (time1.hours === time2.hours && time1.minutes === time2.minutes) { + return 0; + } + + return 1; +} + +function getTrimTimeSpan(timeSpan, trimmerHours) { + return { + from: getTimestamp( + timeSpan.from.weekDay, + Math.max(timeSpan.from.hours, trimmerHours.from.hours), + timeSpan.from.hours < trimmerHours.from.hours + ? trimmerHours.from.minutes + : timeSpan.from.minutes, + timeSpan.from.timeZone + ), + to: getTimestamp( + timeSpan.to.weekDay, + Math.min(timeSpan.to.hours, trimmerHours.to.hours), + timeSpan.to.hours < trimmerHours.to.hours + ? timeSpan.to.minutes + : trimmerHours.to.minutes, + timeSpan.to.timeZone + ) + }; +} + +function getAppropriateTimes(schedule) { + const robbers = Object.keys(schedule); + let appropriateTimes = schedule[robbers[0]]; + for (let i = 1; i < robbers.length; i++) { + const robber = robbers[i]; + for (let j = 0; j < schedule[robber].length; j++) { + const trimmerTimeSpan = schedule[robber][j]; + appropriateTimes = trimByHours(appropriateTimes, trimmerTimeSpan); + } + } + + return appropriateTimes; +} + +function checkDuration(moments, targetDuration) { + const appropriateMoments = []; + for (let i = 0; i < moments.length; i++) { + const moment = moments[i]; + const duration = moment.to.hours * 60 + moment.to.minutes - + (moment.from.hours * 60 + moment.from.minutes); + if (duration >= targetDuration) { + appropriateMoments.push(moment); + } + } + + return appropriateMoments; +} + module.exports = { getAppropriateMoment, From 5c053f66c137ba23c8cfe11b4ccdb697a51ff400 Mon Sep 17 00:00:00 2001 From: jellymary Date: Sat, 17 Nov 2018 21:01:06 +0500 Subject: [PATCH 2/6] solved --- robbery.js | 389 +++++++++++++++++++++++++++-------------------------- 1 file changed, 198 insertions(+), 191 deletions(-) diff --git a/robbery.js b/robbery.js index b55d395..0f710e1 100755 --- a/robbery.js +++ b/robbery.js @@ -6,17 +6,135 @@ */ const isStar = false; -const DAYS = Object.freeze({ - 'ПН': 0, 'ВТ': 1, 'СР': 2, 'ЧТ': 3, 'ПТ': 4, 'СБ': 5, 'ВС': 6, - parseDay: function (dayNumber) { - const days = ['ПН', 'ВТ', 'СР', 'ЧТ', 'ПТ', 'СБ', 'ВС']; - - return days[parseInt(dayNumber)]; - }, - addDays: function (day, daysCount) { - return this.parseDay(this[day] + daysCount); +const WEEK = ['ПН', 'ВТ', 'СР', 'ЧТ', 'ПТ', 'СБ', 'ВС']; + +class WeekDay { + constructor(weekDay) { + this.day = weekDay; + this.number = WEEK.indexOf(this.day); + } + + addDays(daysCount) { + this.number += daysCount; + this.number %= 7; + this.day = WEEK.indexOf(this.day); + } +} + +class TimeStamp { + constructor(weekDay, hours, minutes, timeZone) { + this.weekDay = weekDay ? new WeekDay(weekDay) : weekDay; + this.hours = hours; + this.minutes = minutes; + this.timeZone = timeZone; + } + + shift(timeShift) { + this.timeZone += timeShift; + this.hours += timeShift; + if (this.hours >= 24 || this.hours < 0) { + this.weekDay.addDays(this.hours < 0 ? -1 : 1); + this.hours %= 24; + } + + return this; + } + + static compare(time1, time2) { + let comparator = time1.weekDay.number - time2.weekDay.number; + comparator = comparator !== 0 ? comparator : time1.hours - time2.hours; + comparator = comparator !== 0 ? comparator : time1.minutes - time2.minutes; + + return comparator; + } + + static max(time1, time2) { + if (TimeStamp.compare(time1, time2) >= 0) { + return time1; + } + + return time2; + } + + static min(time1, time2) { + if (TimeStamp.compare(time1, time2) <= 0) { + return time1; + } + + return time2; + } + + static parse(timeStamp) { + const splitTimeStamp = timeStamp.split(/[ +:]/).reverse(); + + return new TimeStamp( + splitTimeStamp[3], + parseInt(splitTimeStamp[2]), + parseInt(splitTimeStamp[1]), + parseInt(splitTimeStamp[0]) + ); } -}); +} + +class TimeInterval { + constructor(from, to) { + if (!(from instanceof TimeStamp && to instanceof TimeStamp)) { + throw new TypeError(); + } + this.from = from; + this.to = to; + } + + shift(timeShift) { + this.from.shift(timeShift); + this.to.shift(timeShift); + + return this; + } + + static areIntersected(time1, time2) { + return (TimeStamp.compare(time1.from, time2.from) >= 0 && + TimeStamp.compare(time1.from, time2.to) < 0) || + (TimeStamp.compare(time1.to, time2.from) > 0 && + TimeStamp.compare(time1.to, time2.to) <= 0); + } + + static parse(timeInterval) { + return new TimeInterval( + TimeStamp.parse(timeInterval.from), + TimeStamp.parse(timeInterval.to) + ); + } +} + +class GangSchedule { + constructor(schedule) { + for (let robber in schedule) { + if (schedule.hasOwnProperty(robber)) { + this[robber] = schedule[robber]; + } + } + } + + forEachRobber(func, ...args) { + for (let robber in this) { + if (this.hasOwnProperty(robber)) { + this[robber] = func(this[robber], ...args); + } + } + } + + static parse(schedule) { + const gangSchedule = {}; + for (const robber in schedule) { + if (schedule.hasOwnProperty(robber)) { + gangSchedule[robber] = schedule[robber].map(time => TimeInterval.parse(time)); + } + } + + return new GangSchedule(gangSchedule); + } +} /** * @param {Object} schedule – Расписание Банды @@ -27,16 +145,26 @@ const DAYS = Object.freeze({ * @returns {Object} */ function getAppropriateMoment(schedule, duration, workingHours) { - console.info(schedule, duration, workingHours); - - const bankWorkingHours = changeTimeSpan(parseTimestamp, workingHours); - const bankTimeZone = bankWorkingHours.from.timeZone; - const robbersSchedule = copyAndParseTimeSpan(schedule); - forEachRobber(transferScheduleToTimeZone, robbersSchedule, bankTimeZone); - forEachRobber(trimByDays, robbersSchedule); - forEachRobber(trimByHours, robbersSchedule, bankWorkingHours); - const appropriateTimes = getAppropriateTimes(robbersSchedule); - const appropriateMoments = checkDuration(appropriateTimes, duration); + const bankSchedule = ['ПН', 'ВТ', 'СР'].map(day => new TimeInterval( + TimeStamp.parse(day + ' ' + workingHours.from), + TimeStamp.parse(day + ' ' + workingHours.to) + )); + const bankTimeZone = bankSchedule[0].from.timeZone; + + const gangSchedule = GangSchedule.parse(schedule); + gangSchedule.forEachRobber(translateScheduleToTimeZone, bankTimeZone); + gangSchedule.forEachRobber(findFreeTimeInPeriod, new TimeInterval( + new TimeStamp('ПН', 0, 0, bankTimeZone), + new TimeStamp('СР', 23, 59, bankTimeZone) + )); + + const appropriateMoments = getAppropriateMoments(mergeSchedules(gangSchedule, bankSchedule)) + .filter(moment => { + const currentDuration = moment.to.hours * 60 + moment.to.minutes - + (moment.from.hours * 60 + moment.from.minutes); + + return currentDuration >= duration; + }); return { @@ -62,7 +190,7 @@ function getAppropriateMoment(schedule, duration, workingHours) { template = template.replace(/%HH/, start.hours.toString().padStart(2, '0')) .replace(/%MM/, start.minutes.toString().padStart(2, '0')) - .replace(/%DD/, start.weekDay); + .replace(/%DD/, start.weekDay.day); return template; }, @@ -78,193 +206,72 @@ function getAppropriateMoment(schedule, duration, workingHours) { }; } -function changeTimeSpan(changer, timeSpan) { - const args = []; - for (let i = 2; i < arguments.length; i++) { - args.push(arguments[i]); - } - - return { - from: changer(timeSpan.from, args), - to: changer(timeSpan.to, args) - }; -} - -function forEachRobber(func, schedule) { - const robbers = Object.keys(schedule); - for (let i = 0; i < robbers.length; i++) { - const robber = robbers[i]; - schedule[robber] = func(schedule[robber], arguments[2]); - } -} - -function parseTimestamp(timestamp) { - const splitTimestamp = timestamp.split(/[ +:]/).reverse(); - - return getTimestamp( - splitTimestamp[3], - parseInt(splitTimestamp[2]), - parseInt(splitTimestamp[1]), - parseInt(splitTimestamp[0]) - ); -} - -function getTimestamp(weekDay, hours, minutes, timeZone) { - return { - weekDay, - hours, - minutes, - timeZone - }; -} - -function copyAndParseTimeSpan(schedule) { - const robbers = Object.keys(schedule); - const newSchedule = {}; - for (let i = 0; i < robbers.length; i++) { - const robber = robbers[i]; - Object.defineProperty(newSchedule, robber, { - value: [], - configurable: false, - writable: true, - enumerable: true - }); - for (let j = 0; j < schedule[robber].length; j++) { - newSchedule[robber].push(changeTimeSpan(parseTimestamp, schedule[robber][j])); - } - } - - return newSchedule; -} - -function transferScheduleToTimeZone(robberSchedule, targetTimeZone) { - targetTimeZone = parseInt(targetTimeZone); - const timeZone = robberSchedule[0].from.timeZone; +function translateScheduleToTimeZone(schedule, targetTimeZone) { + const timeZone = schedule[0].from.timeZone; if (timeZone !== targetTimeZone) { const timeShift = targetTimeZone - timeZone; - return robberSchedule.map( - timeSpan => changeTimeSpan(shiftTimestamp, timeSpan, timeShift)); + return schedule.map(timeInterval => timeInterval.shift(timeShift)); } - return robberSchedule; + return schedule; } -function shiftTimestamp(timestamp, timeShift) { - timeShift = parseInt(timeShift); - timestamp.timeZone += timeShift; - timestamp.hours += timeShift; - if (timestamp.hours >= 24 || timestamp.hours < 0) { - timestamp.weekDay = DAYS.addDays(timestamp.weekDay, timestamp.hours < 0 ? -1 : 1); - timestamp.hours %= 24; - } - - return timestamp; +function findFreeTimeInPeriod(schedule, timePeriod) { + schedule.sort((a, b) => TimeStamp.compare(a.from, b.from)); + const freeTimes = []; + freeTimes.push({ from: timePeriod.from }); + schedule.forEach(timeInterval => { + freeTimes[freeTimes.length - 1].to = timeInterval.from; + freeTimes.push({ from: timeInterval.to }); + }); + freeTimes[freeTimes.length - 1].to = timePeriod.to; + + return freeTimes.map(interval => new TimeInterval(interval.from, interval.to)); } -function trimByDays(robberSchedule) { - const newSchedule = []; - for (let j = 0; j < robberSchedule.length; j++) { - const timeSpan = robberSchedule[j]; - if (timeSpan.from.weekDay === timeSpan.to.weekDay) { - newSchedule.push(timeSpan); - } else { - newSchedule.push({ - from: timeSpan.from, - to: getTimestamp( - timeSpan.from.weekDay, - 23, 59, - timeSpan.from.timeZone - ) - }); - newSchedule.push({ - from: getTimestamp( - timeSpan.to.weekDay, - 0, 0, - timeSpan.to.timeZone - ), - to: timeSpan.to - }); - } - } - - return newSchedule; +function mergeSchedules(gangSchedule, bankSchedule) { + return Object.keys(gangSchedule) + .map(robber => gangSchedule[robber]) + .concat([bankSchedule]); } -function trimByHours(robberSchedule, trimmerHours) { - const newSchedule = []; - for (let i = 0; i < robberSchedule.length; i++) { - const timeSpan = robberSchedule[i]; - if (trimmerHours.from.weekDay && trimmerHours.from.weekDay !== timeSpan.from.weekDay) { - newSchedule.push(timeSpan); - continue; - } - const trimTimeSpan = getTrimTimeSpan(timeSpan, trimmerHours); - if (compareTime(trimTimeSpan.from, trimTimeSpan.to) !== 1) { - newSchedule.push(trimTimeSpan); +function getAppropriateMoments(schedule) { + let moments = schedule[0]; + for (let i = 1; i < schedule.length; i++) { + const currentMoments = []; + const currentSchedule = schedule[i]; + for (let j = 0; j < currentSchedule.length; j++) { + const timeInterval = currentSchedule[j]; + currentMoments.push(...trimByHours(moments, timeInterval)); } + moments = currentMoments; } - - return newSchedule; + // let moments = bankSchedule; + // for (const robber in gangSchedule) { + // if (gangSchedule.hasOwnProperty(robber)) { + // const currentMoments = []; + // gangSchedule[robber].forEach(timeInterval => + // currentMoments.push(...trimByHours(moments, timeInterval))); + // moments = currentMoments; + // } + // } + + return moments; } -function compareTime(time1, time2) { - if (time1.hours < time2.hours) { - return -1; - } else if (time1.hours === time2.hours && time1.minutes === time2.minutes) { - return 0; - } - - return 1; -} - -function getTrimTimeSpan(timeSpan, trimmerHours) { - return { - from: getTimestamp( - timeSpan.from.weekDay, - Math.max(timeSpan.from.hours, trimmerHours.from.hours), - timeSpan.from.hours < trimmerHours.from.hours - ? trimmerHours.from.minutes - : timeSpan.from.minutes, - timeSpan.from.timeZone - ), - to: getTimestamp( - timeSpan.to.weekDay, - Math.min(timeSpan.to.hours, trimmerHours.to.hours), - timeSpan.to.hours < trimmerHours.to.hours - ? timeSpan.to.minutes - : trimmerHours.to.minutes, - timeSpan.to.timeZone - ) - }; -} - -function getAppropriateTimes(schedule) { - const robbers = Object.keys(schedule); - let appropriateTimes = schedule[robbers[0]]; - for (let i = 1; i < robbers.length; i++) { - const robber = robbers[i]; - for (let j = 0; j < schedule[robber].length; j++) { - const trimmerTimeSpan = schedule[robber][j]; - appropriateTimes = trimByHours(appropriateTimes, trimmerTimeSpan); +function trimByHours(schedule, hours) { + const trimmedSchedule = []; + schedule.forEach(timeInterval => { + if (TimeInterval.areIntersected(timeInterval, hours)) { + trimmedSchedule.push(new TimeInterval( + TimeStamp.max(timeInterval.from, hours.from), + TimeStamp.min(timeInterval.to, hours.to) + )); } - } - - return appropriateTimes; -} - -function checkDuration(moments, targetDuration) { - const appropriateMoments = []; - for (let i = 0; i < moments.length; i++) { - const moment = moments[i]; - const duration = moment.to.hours * 60 + moment.to.minutes - - (moment.from.hours * 60 + moment.from.minutes); - if (duration >= targetDuration) { - appropriateMoments.push(moment); - } - } + }); - return appropriateMoments; + return trimmedSchedule; } module.exports = { From 25712cefda1cddc1e57c79f7e04ac5960c767a4d Mon Sep 17 00:00:00 2001 From: jellymary Date: Sat, 17 Nov 2018 23:26:53 +0500 Subject: [PATCH 3/6] =?UTF-8?q?=D0=BE=D0=B4=D0=BD=D0=B0=20=D0=BE=D1=88?= =?UTF-8?q?=D0=B8=D0=B1=D0=BA=D0=B0=20=D0=BD=D0=B0=D0=B9=D0=B4=D0=B5=D0=BD?= =?UTF-8?q?=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- robbery.js | 19 ++++++------------- robbery.spec.js | 20 ++++++++++++++++++++ 2 files changed, 26 insertions(+), 13 deletions(-) diff --git a/robbery.js b/robbery.js index 0f710e1..0b12c64 100755 --- a/robbery.js +++ b/robbery.js @@ -146,8 +146,8 @@ class GangSchedule { */ function getAppropriateMoment(schedule, duration, workingHours) { const bankSchedule = ['ПН', 'ВТ', 'СР'].map(day => new TimeInterval( - TimeStamp.parse(day + ' ' + workingHours.from), - TimeStamp.parse(day + ' ' + workingHours.to) + TimeStamp.parse(`${day} ${workingHours.from}`), + TimeStamp.parse(`${day} ${workingHours.to}`) )); const bankTimeZone = bankSchedule[0].from.timeZone; @@ -158,7 +158,8 @@ function getAppropriateMoment(schedule, duration, workingHours) { new TimeStamp('СР', 23, 59, bankTimeZone) )); - const appropriateMoments = getAppropriateMoments(mergeSchedules(gangSchedule, bankSchedule)) + let appropriateMoments = getAppropriateMoments(mergeSchedules(gangSchedule, bankSchedule)); + appropriateMoments = appropriateMoments .filter(moment => { const currentDuration = moment.to.hours * 60 + moment.to.minutes - (moment.from.hours * 60 + moment.from.minutes); @@ -247,15 +248,6 @@ function getAppropriateMoments(schedule) { } moments = currentMoments; } - // let moments = bankSchedule; - // for (const robber in gangSchedule) { - // if (gangSchedule.hasOwnProperty(robber)) { - // const currentMoments = []; - // gangSchedule[robber].forEach(timeInterval => - // currentMoments.push(...trimByHours(moments, timeInterval))); - // moments = currentMoments; - // } - // } return moments; } @@ -263,7 +255,8 @@ function getAppropriateMoments(schedule) { function trimByHours(schedule, hours) { const trimmedSchedule = []; schedule.forEach(timeInterval => { - if (TimeInterval.areIntersected(timeInterval, hours)) { + if (TimeInterval.areIntersected(timeInterval, hours) || + TimeInterval.areIntersected(hours, timeInterval)) { trimmedSchedule.push(new TimeInterval( TimeStamp.max(timeInterval.from, hours.from), TimeStamp.min(timeInterval.to, hours.to) diff --git a/robbery.spec.js b/robbery.spec.js index 298de05..ee9269a 100755 --- a/robbery.spec.js +++ b/robbery.spec.js @@ -48,6 +48,26 @@ describe('robbery.getAppropriateMoment()', () => { ); }); + it('момента не существует вне зависимости от продрлжительности', () => { + const moment = robbery.getAppropriateMoment({ + Danny: [ + { from: 'ПН 10:00+5', to: 'ПН 17:00+5' } + ], + Rusty: [ + { from: 'ПН 10:00+5', to: 'ПН 17:00+5' } + ], + Linus: [ + { from: 'СР 10:00+5', to: 'СР 17:00+5' } + ] + }, 10, { from: '12:00+5', to: '14:00+5' }); + + assert.ok(moment.exists()); + assert.strictEqual( + moment.format('Метим на %DD, старт в %HH:%MM!'), + 'Метим на ВТ, старт в 12:00!' + ); + }); + if (robbery.isStar) { it('должен перемещаться на более поздний момент [*]', () => { const moment = getMomentFor(90); From 7131522221e6f69bd6efd471cd82b8fd7ec63b23 Mon Sep 17 00:00:00 2001 From: jellymary Date: Sat, 17 Nov 2018 23:51:47 +0500 Subject: [PATCH 4/6] mb --- robbery.js | 13 +++++++------ robbery.spec.js | 4 ++-- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/robbery.js b/robbery.js index 0b12c64..eaeddff 100755 --- a/robbery.js +++ b/robbery.js @@ -40,6 +40,10 @@ class TimeStamp { return this; } + between(time1, time2) { + return TimeStamp.compare(this, time1) >= 0 && TimeStamp.compare(this, time2) <= 0; + } + static compare(time1, time2) { let comparator = time1.weekDay.number - time2.weekDay.number; comparator = comparator !== 0 ? comparator : time1.hours - time2.hours; @@ -93,10 +97,8 @@ class TimeInterval { } static areIntersected(time1, time2) { - return (TimeStamp.compare(time1.from, time2.from) >= 0 && - TimeStamp.compare(time1.from, time2.to) < 0) || - (TimeStamp.compare(time1.to, time2.from) > 0 && - TimeStamp.compare(time1.to, time2.to) <= 0); + return time1.from.between(time2.from, time2.to) || time1.to.between(time2.from, time2.to) || + time2.from.between(time1.from, time1.to) || time2.to.between(time1.from, time1.to); } static parse(timeInterval) { @@ -255,8 +257,7 @@ function getAppropriateMoments(schedule) { function trimByHours(schedule, hours) { const trimmedSchedule = []; schedule.forEach(timeInterval => { - if (TimeInterval.areIntersected(timeInterval, hours) || - TimeInterval.areIntersected(hours, timeInterval)) { + if (TimeInterval.areIntersected(timeInterval, hours)) { trimmedSchedule.push(new TimeInterval( TimeStamp.max(timeInterval.from, hours.from), TimeStamp.min(timeInterval.to, hours.to) diff --git a/robbery.spec.js b/robbery.spec.js index ee9269a..19dd0a3 100755 --- a/robbery.spec.js +++ b/robbery.spec.js @@ -48,7 +48,7 @@ describe('robbery.getAppropriateMoment()', () => { ); }); - it('момента не существует вне зависимости от продрлжительности', () => { + it('ВТ у всех свободен', () => { const moment = robbery.getAppropriateMoment({ Danny: [ { from: 'ПН 10:00+5', to: 'ПН 17:00+5' } @@ -59,7 +59,7 @@ describe('robbery.getAppropriateMoment()', () => { Linus: [ { from: 'СР 10:00+5', to: 'СР 17:00+5' } ] - }, 10, { from: '12:00+5', to: '14:00+5' }); + }, 120, { from: '12:00+5', to: '14:00+5' }); assert.ok(moment.exists()); assert.strictEqual( From 838fbbfed03d96f4e9bf243d41bbdda6e867a1a0 Mon Sep 17 00:00:00 2001 From: jellymary Date: Sun, 18 Nov 2018 00:23:36 +0500 Subject: [PATCH 5/6] =?UTF-8?q?=D0=B1=D0=B0=D0=B3=D0=B0=20=D0=B2=20weekday?= =?UTF-8?q?=20=D0=BD=D0=B0=D0=B9=D0=B4=D0=B5=D0=BD=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- robbery.js | 2 +- robbery.spec.js | 50 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 51 insertions(+), 1 deletion(-) diff --git a/robbery.js b/robbery.js index eaeddff..8842dba 100755 --- a/robbery.js +++ b/robbery.js @@ -17,7 +17,7 @@ class WeekDay { addDays(daysCount) { this.number += daysCount; this.number %= 7; - this.day = WEEK.indexOf(this.day); + this.day = WEEK[this.number]; } } diff --git a/robbery.spec.js b/robbery.spec.js index 19dd0a3..727145e 100755 --- a/robbery.spec.js +++ b/robbery.spec.js @@ -68,6 +68,56 @@ describe('robbery.getAppropriateMoment()', () => { ); }); + it('вычитаем время для перевода в часовой пояс банка', () => { + const moment = robbery.getAppropriateMoment({ + Danny: [ + { from: 'ПН 14:00+7', to: 'ПН 19:00+7' }, + { from: 'ВТ 15:00+7', to: 'ВТ 18:00+7' } + ], + Rusty: [ + { from: 'ПН 11:30+5', to: 'ПН 16:30+5' }, + { from: 'ВТ 13:00+5', to: 'ВТ 16:00+5' } + ], + Linus: [ + { from: 'ПН 09:00+3', to: 'ПН 14:00+3' }, + { from: 'ПН 21:00+3', to: 'ВТ 09:30+3' }, + { from: 'СР 09:30+3', to: 'СР 15:00+3' } + ] + }, + 90, + { from: '10:00+5', to: '18:00+5' }); + + assert.ok(moment.exists()); + assert.strictEqual( + moment.format('Метим на %DD, старт в %HH:%MM!'), + 'Метим на ВТ, старт в 11:30!' + ); + }); + + it('перевод времени изменяет день недели', () => { + const moment = robbery.getAppropriateMoment( + { + Danny: [ + { from: 'ПН 12:00+5', to: 'ПН 17:00+5' }, + { from: 'ВТ 13:00+5', to: 'ВТ 16:00+5' } + ], + Rusty: [ + { from: 'ПН 11:30+5', to: 'ПН 16:30+5' }, + { from: 'ВТ 13:00+5', to: 'ВТ 16:00+5' } + ], + Linus: [ + { from: 'ПН 09:00+3', to: 'ПН 14:00+3' }, + { from: 'ПН 23:00+3', to: 'ВТ 09:30+3' }, + { from: 'СР 09:30+3', to: 'СР 15:00+3' } + ] + }, + 90, + { from: '10:00+5', to: '18:00+5' } + ); + + assert.ok(moment.exists()); + }); + if (robbery.isStar) { it('должен перемещаться на более поздний момент [*]', () => { const moment = getMomentFor(90); From c99c02b7336acb312795c0a1497719d48114dd73 Mon Sep 17 00:00:00 2001 From: jellymary Date: Sun, 18 Nov 2018 01:56:56 +0500 Subject: [PATCH 6/6] =?UTF-8?q?=D1=87=D1=82=D0=BE=D0=B1=D1=8B=20=D1=83?= =?UTF-8?q?=D0=B1=D0=B5=D0=B4=D0=B8=D1=82=D1=8C=D1=81=D1=8F,=20=D1=87?= =?UTF-8?q?=D1=82=D0=BE=20=D0=BD=D0=B8=D1=87=D0=B5=D0=B3=D0=BE=20=D0=BD?= =?UTF-8?q?=D0=B5=20=D0=B8=D0=B7=D0=BC=D0=B5=D0=BD=D0=B8=D0=BB=D0=BE=D1=81?= =?UTF-8?q?=D1=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- robbery.js | 45 ++++++++++++++++++++++++++++++++------------- robbery.spec.js | 40 ++++++++++++++++++++++++++++++++++------ 2 files changed, 66 insertions(+), 19 deletions(-) diff --git a/robbery.js b/robbery.js index 8842dba..7293189 100755 --- a/robbery.js +++ b/robbery.js @@ -41,10 +41,17 @@ class TimeStamp { } between(time1, time2) { + if (!(time1 instanceof TimeStamp && time2 instanceof TimeStamp)) { + throw new TypeError(); + } + return TimeStamp.compare(this, time1) >= 0 && TimeStamp.compare(this, time2) <= 0; } static compare(time1, time2) { + if (!(time1 instanceof TimeStamp && time2 instanceof TimeStamp)) { + throw new TypeError(); + } let comparator = time1.weekDay.number - time2.weekDay.number; comparator = comparator !== 0 ? comparator : time1.hours - time2.hours; comparator = comparator !== 0 ? comparator : time1.minutes - time2.minutes; @@ -53,6 +60,9 @@ class TimeStamp { } static max(time1, time2) { + if (!(time1 instanceof TimeStamp && time2 instanceof TimeStamp)) { + throw new TypeError(); + } if (TimeStamp.compare(time1, time2) >= 0) { return time1; } @@ -61,6 +71,9 @@ class TimeStamp { } static min(time1, time2) { + if (!(time1 instanceof TimeStamp && time2 instanceof TimeStamp)) { + throw new TypeError(); + } if (TimeStamp.compare(time1, time2) <= 0) { return time1; } @@ -96,7 +109,16 @@ class TimeInterval { return this; } + get duration() { + return ((this.to.weekDay.number * 24 + this.to.hours) * 60 + this.to.minutes) - + ((this.from.weekDay.number * 24 + this.from.hours) * 60 + this.from.minutes); + } + static areIntersected(time1, time2) { + if (!(time1 instanceof TimeInterval && time2 instanceof TimeInterval)) { + throw new TypeError(); + } + return time1.from.between(time2.from, time2.to) || time1.to.between(time2.from, time2.to) || time2.from.between(time1.from, time1.to) || time2.to.between(time1.from, time1.to); } @@ -160,23 +182,18 @@ function getAppropriateMoment(schedule, duration, workingHours) { new TimeStamp('СР', 23, 59, bankTimeZone) )); - let appropriateMoments = getAppropriateMoments(mergeSchedules(gangSchedule, bankSchedule)); - appropriateMoments = appropriateMoments - .filter(moment => { - const currentDuration = moment.to.hours * 60 + moment.to.minutes - - (moment.from.hours * 60 + moment.from.minutes); - - return currentDuration >= duration; - }); + const appropriateMoments = getAppropriateMoments(mergeSchedules(gangSchedule, bankSchedule)) + .filter(moment => moment.duration >= duration); return { + moments: appropriateMoments, /** * Найдено ли время * @returns {Boolean} */ exists: function () { - return appropriateMoments.length !== 0; + return this.moments.length !== 0; }, /** @@ -189,13 +206,12 @@ function getAppropriateMoment(schedule, duration, workingHours) { if (!this.exists()) { return ''; } - const start = appropriateMoments[0].from; + const start = this.moments[0].from; - template = template.replace(/%HH/, start.hours.toString().padStart(2, '0')) + return template + .replace(/%HH/, start.hours.toString().padStart(2, '0')) .replace(/%MM/, start.minutes.toString().padStart(2, '0')) .replace(/%DD/, start.weekDay.day); - - return template; }, /** @@ -205,6 +221,9 @@ function getAppropriateMoment(schedule, duration, workingHours) { */ tryLater: function () { return false; + // if (!this.exists()) { + // return false; + // } } }; } diff --git a/robbery.spec.js b/robbery.spec.js index 727145e..91f6fe9 100755 --- a/robbery.spec.js +++ b/robbery.spec.js @@ -63,8 +63,8 @@ describe('robbery.getAppropriateMoment()', () => { assert.ok(moment.exists()); assert.strictEqual( - moment.format('Метим на %DD, старт в %HH:%MM!'), - 'Метим на ВТ, старт в 12:00!' + moment.format('%DD %HH:%MM'), + 'ВТ 12:00' ); }); @@ -89,12 +89,36 @@ describe('robbery.getAppropriateMoment()', () => { assert.ok(moment.exists()); assert.strictEqual( - moment.format('Метим на %DD, старт в %HH:%MM!'), - 'Метим на ВТ, старт в 11:30!' + moment.format('%DD %HH:%MM'), + 'ВТ 11:30' ); }); it('перевод времени изменяет день недели', () => { + const moment = robbery.getAppropriateMoment( + { + Timmy: [ + { from: 'ПН 12:00+5', to: 'ПН 17:00+5' }, + { from: 'ВТ 13:00+5', to: 'ВТ 16:00+5' } + ], + Ben: [ + { from: 'ПН 11:30+5', to: 'ПН 16:30+5' }, + { from: 'ВТ 13:00+5', to: 'ВТ 16:00+5' } + ], + Loki: [ + { from: 'ПН 09:00+3', to: 'ПН 14:00+3' }, + { from: 'ПН 23:00+3', to: 'ВТ 09:30+3' }, + { from: 'СР 09:30+3', to: 'СР 15:00+3' } + ] + }, + 90, + { from: '10:00+5', to: '18:00+5' } + ); + + assert.ok(moment.exists()); + }); + + it('адекватное написание времени', () => { const moment = robbery.getAppropriateMoment( { Danny: [ @@ -107,15 +131,19 @@ describe('robbery.getAppropriateMoment()', () => { ], Linus: [ { from: 'ПН 09:00+3', to: 'ПН 14:00+3' }, - { from: 'ПН 23:00+3', to: 'ВТ 09:30+3' }, + { from: 'ПН 21:00+3', to: 'ВТ 09:30+3' }, { from: 'СР 09:30+3', to: 'СР 15:00+3' } ] }, 90, - { from: '10:00+5', to: '18:00+5' } + { from: '09:00+5', to: '18:00+5' } ); assert.ok(moment.exists()); + assert.strictEqual( + moment.format('%DD %HH:%MM'), + 'ПН 09:00' + ); }); if (robbery.isStar) {