UNPKG

1-line-opening-hours

Version:

Simple parser for OpenStreetMap `opening_hours`

410 lines 13.4 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.OpeningHours = void 0; const SIMPLE_DAYS_NAMES = ['su', 'mo', 'tu', 'we', 'th', 'fr', 'sa']; class OpeningHours { constructor(stringOpeningHours) { this.MAX_CLOSE_TIME = '24:00'; this.MIN_OPEN_TIME = '00:00'; this.openingHours = { su: [], mo: [], tu: [], we: [], th: [], fr: [], sa: [], ph: [], }; this.getFullStatusOfDay = (date) => { const isOpenNow = this.isOpenOn(date); const allHours = this.getTable(); const dayIndex = date.getDay(); const daySimpleIndex = SIMPLE_DAYS_NAMES[dayIndex]; const todayHours = allHours[daySimpleIndex]; if (!todayHours || todayHours.length === 0) { return { open: false, openUntil: null, nextReopening: this.getNextReopening(allHours, date), }; } const formatHours = this.serializeHoursOfDay(todayHours); if (!isOpenNow) { const hour = this.opensAt(formatHours, date, undefined); if (hour === null) { return { open: false, openUntil: null, nextReopening: this.getNextReopening(allHours, date), }; } return { open: false, openUntil: null, nextReopening: { hour, day: SIMPLE_DAYS_NAMES[date.getDay()], opensInDay: 0, }, }; } return { open: true, openUntil: this.openUntil(formatHours, date), nextReopening: null, }; }; this.getFullStatusOfToday = (utcOffset = 0) => { return this.getFullStatusOfDay(this.getTodayDateWithOffset(utcOffset)); }; this.openUntil = (formatHours, date) => { if (formatHours.length > 1) { const now = this.constructDateFromTime(`${date.getUTCHours()}:${date.getUTCMinutes()}`); for (let i = 1; i < formatHours.length; i = i + 2) { if (now < formatHours[i].date || i === formatHours.length - 1) { return formatHours[i].hour; } } } return '24/7'; }; this.opensAt = (formatHours, date, nextDay) => { if (formatHours.length > 1) { const now = this.constructDateFromTime(`${date.getUTCHours()}:${date.getUTCMinutes()}`); if (nextDay) { return formatHours[0].hour; } for (let i = 0; i < formatHours.length - 1; i = i + 2) { if (now < formatHours[i].date) { return formatHours[i].hour; } } } return null; }; this.getNextReopening = (allHours, date) => { const nextDay = this.getNextOpeningDay(date); const nextDayHours = allHours[nextDay.day]; if (!nextDayHours || nextDayHours.length === 0) { return null; } const formatNextDayHours = this.serializeHoursOfDay(nextDayHours); const hour = this.opensAt(formatNextDayHours, date, nextDay.day); return { hour, day: nextDay.day, opensInDay: nextDay.opensInDay, }; }; this.serializeHoursOfDay = (dayHours) => { const formatHours = []; dayHours.map(todayHour => todayHour .split('-') .map(hour => formatHours.push({ date: this.constructDateFromTime(hour), hour }))); return formatHours; }; this.parse(stringOpeningHours); } getTable() { return this.openingHours; } getTodayDateWithOffset(utcOffset = 0) { if (utcOffset === 0) { return new Date(); } const now = new Date(); now.setTime(now.getTime() + utcOffset * 60 * 1000); return now; } isOpenOn(date) { let today = date.getDay(); let todayTime = this.formatTime(date); let times = this.getTimesOfDay(today); for (let i = 0; i < times.length; i++) { let timeSlots = this.geTimeSlots(times[i]); if (this.compareTime(todayTime, timeSlots[0]) != -1 && this.compareTime(timeSlots[1], todayTime) != -1) { return true; } } if (this.isCurrentlyOnNightServiceOfYesterday(date)) { return true; } return false; } isOpenNow(utcOffset = 0) { return this.isOpenOn(this.getTodayDateWithOffset(utcOffset)); } geTimeSlots(time) { if (time.includes('+')) { return [time.split('+')[0], this.MAX_CLOSE_TIME]; } const timeSlots = time.split('-'); if (this.isNightlyService(timeSlots[0], timeSlots[1])) { return [timeSlots[0], this.MAX_CLOSE_TIME]; } return [timeSlots[0], timeSlots[1]]; } isCurrentlyOnNightServiceOfYesterday(date) { const yesterdayTimes = this.getTimesOfDay(date.getDay() - 1); if (yesterdayTimes.length === 0) { return false; } let yesterdayLastTimeRange = yesterdayTimes[yesterdayTimes.length - 1].split('-'); if (this.isNightlyService(yesterdayLastTimeRange[0], yesterdayLastTimeRange[1]) && this.compareTime(yesterdayLastTimeRange[1], this.formatTime(date)) !== -1 && this.compareTime(this.formatTime(date), this.MIN_OPEN_TIME) !== -1) { return true; } return false; } getNextOpeningDay(date) { const today = date.getDay(); let times = []; let i = today; for (let key in this.openingHours) { i = i === 7 ? 0 : i + 1; times = this.getTimesOfDay(i); if (times.length > 0) { times = this.openingHours[key]; break; } } return { day: SIMPLE_DAYS_NAMES[i], opensInDay: i - today, }; } parse(inp) { const parts = this.splitHard(this.simplify(inp)); parts.forEach(part => { this.parseHardPart(part); }); } simplify(input) { if (input === '24/7') { input = 'mo-su 00:00-24:00; ph 00:00-24:00'; } input = input.toLocaleLowerCase(); input = input.trim(); input = input.replace(/ +(?= )/g, ''); input = input.replace(' -', '-'); input = input.replace('- ', '-'); input = input.replace(' :', ':'); input = input.replace(': ', ':'); input = input.replace(' ,', ','); input = input.replace(', ', ','); input = input.replace(' ;', ';'); input = input.replace('; ', ';'); return input; } formatTime(date) { return date.getUTCHours() + ':' + date.getUTCMinutes(); } splitHard(inp) { return inp.split(';'); } parseHardPart(part) { if (part == '24/7') { part = 'mo-su 00:00-24:00'; } let segments = part.split(/\ |\,/); let tempData = {}; let days = []; let times = []; segments.forEach(segment => { if (this.checkDay(segment)) { if (times.length == 0) { days = days.concat(this.parseDays(segment)); } else { days.forEach(day => { if (tempData[day]) { tempData[day] = tempData[day].concat(times); } else { tempData[day] = times; } }); days = this.parseDays(segment); times = []; } } if (this.checkTime(segment)) { if (segment == 'off') { times = []; } else { times.push(segment); } } }); days.forEach(day => { if (tempData[day]) { tempData[day] = tempData[day].concat(times); } else { tempData[day] = times; } }); for (let key in tempData) { this.openingHours[key] = tempData[key]; } } parseDays(part) { part = part.toLowerCase(); let days = []; let softParts = part.split(','); softParts.forEach(part => { let rangeCount = (part.match(/\-/g) || []).length; if (rangeCount == 0) { days.push(part); } else { days = days.concat(this.calcDayRange(part)); } }); return days; } calcDayRange(range) { let def = { su: 0, mo: 1, tu: 2, we: 3, th: 4, fr: 5, sa: 6, }; let rangeElements = range.split('-'); let dayStart = def[rangeElements[0]]; let dayEnd = def[rangeElements[1]]; let numberRange = this.calcRange(dayStart, dayEnd, 6); let outRange = []; numberRange.forEach(n => { for (let key in def) { if (def[key] == n) { outRange.push(key); } } }); return outRange; } calcRange(min, max, maxVal) { if (min == max) { return [min]; } let range = [min]; let rangePoint = min; while (rangePoint < (min < max ? max : maxVal)) { rangePoint++; range.push(rangePoint); } if (min > max) { range = range.concat(this.calcRange(0, max, maxVal)); } return range; } checkTime(inp) { if (inp.match(/[0-9]{1,2}:[0-9]{2}\+/)) { return true; } if (inp.match(/[0-9]{1,2}:[0-9]{2}\-[0-9]{1,2}:[0-9]{2}/)) { return true; } if (inp.match(/off/)) { return true; } return false; } checkDay(inp) { let days = ['mo', 'tu', 'we', 'th', 'fr', 'sa', 'su', 'ph']; if (inp.match(/\-/g)) { let ranges = inp.split('-'); if (days.indexOf(ranges[0]) !== -1 && days.indexOf(ranges[1]) !== -1) { return true; } } else { if (days.indexOf(inp) !== -1) { return true; } } return false; } compareTime(time1, time2) { const date1 = this.constructDateFromTime(time1); const date2 = this.constructDateFromTime(time2); if (date1 > date2) { return 1; } if (date1 < date2) { return -1; } return 0; } isNightlyService(openingTime, closingTime) { const date1 = this.constructDateFromTime(openingTime); const date2 = this.constructDateFromTime(closingTime); if (date1 > date2) { return true; } return false; } constructDateFromTime(time) { const constructedDate = new Date('2016-01-01'); const parsedOpeningTime = this.getHoursAndMinutes(time); constructedDate.setHours(parsedOpeningTime[0], parsedOpeningTime[1]); return constructedDate; } getHoursAndMinutes(time) { if (!time) { return [23, 59]; } const hoursAndMinutes = time.split(':'); if (hoursAndMinutes.length !== 2 || Number.isNaN(hoursAndMinutes[0]) || Number.isNaN(hoursAndMinutes[1])) { return [23, 59]; } return [parseInt(hoursAndMinutes[0], 10), parseInt(hoursAndMinutes[1], 10)]; } getTimesOfDay(day) { if (day === -1) { day = 6; } let i = 0; let times = []; for (let key in this.openingHours) { if (i == day) { times = this.openingHours[key]; } i++; } return times; } toReadableString() { const weekDays = { mo: 'Monday', tu: 'Tuesday', we: 'Wednesday', th: 'Thursday', fr: 'Friday', sa: 'Saturday', su: 'Sunday', }; return Object.keys(weekDays) .map(dayKey => { if (this.openingHours[dayKey] && this.openingHours[dayKey].length) { return weekDays[dayKey] + ' ' + this.openingHours[dayKey].join('\t'); } else { return weekDays[dayKey] + ' Closed'; } }) .join('\n'); } } exports.OpeningHours = OpeningHours; //# sourceMappingURL=openingHours.js.map