1-line-opening-hours
Version:
Simple parser for OpenStreetMap `opening_hours`
410 lines • 13.4 kB
JavaScript
"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