forked from MapComplete/MapComplete
		
	
		
			
				
	
	
		
			668 lines
		
	
	
	
		
			22 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
			
		
		
	
	
			668 lines
		
	
	
	
		
			22 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
| import { Utils } from "../../Utils"
 | |
| import opening_hours from "opening_hours"
 | |
| 
 | |
| export interface OpeningHour {
 | |
|     weekday: number // 0 is monday, 1 is tuesday, ...
 | |
|     startHour: number
 | |
|     startMinutes: number
 | |
|     endHour: number
 | |
|     endMinutes: number
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Various utilities manipulating opening hours
 | |
|  */
 | |
| export class OH {
 | |
|     private static readonly days = ["Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"]
 | |
|     private static readonly daysIndexed = {
 | |
|         mo: 0,
 | |
|         tu: 1,
 | |
|         we: 2,
 | |
|         th: 3,
 | |
|         fr: 4,
 | |
|         sa: 5,
 | |
|         su: 6,
 | |
|     }
 | |
| 
 | |
|     public static hhmm(h: number, m: number): string {
 | |
|         if (h == 24) {
 | |
|             return "00:00"
 | |
|         }
 | |
|         return Utils.TwoDigits(h) + ":" + Utils.TwoDigits(m)
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * const rules = [{weekday: 6,endHour: 17,endMinutes: 0,startHour: 13,startMinutes: 0},
 | |
|      *                {weekday: 1,endHour: 12,endMinutes: 0,startHour: 10,startMinutes: 0}]
 | |
|      * OH.ToString(rules) // =>  "Tu 10:00-12:00; Su 13:00-17:00"
 | |
|      *
 | |
|      * const rules = [{weekday: 3,endHour: 17,endMinutes: 0,startHour: 13,startMinutes: 0}, {weekday: 1,endHour: 12,endMinutes: 0,startHour: 10,startMinutes: 0}]
 | |
|      * OH.ToString(rules) // => "Tu 10:00-12:00; Th 13:00-17:00"
 | |
|      *
 | |
|      * const rules = [ { weekday: 1, endHour: 17, endMinutes: 0, startHour: 13, startMinutes: 0 }, { weekday: 1, endHour: 12, endMinutes: 0, startHour: 10, startMinutes: 0 }]);
 | |
|      * OH.ToString(rules) // => "Tu 10:00-12:00, 13:00-17:00"
 | |
|      *
 | |
|      * const rules = [ { weekday: 0, endHour: 12, endMinutes: 0, startHour: 10, startMinutes: 0 }, { weekday: 0, endHour: 17, endMinutes: 0, startHour: 13, startMinutes: 0}, { weekday: 1, endHour: 17, endMinutes: 0, startHour: 13, startMinutes: 0 }, { weekday: 1, endHour: 12, endMinutes: 0, startHour: 10, startMinutes: 0 }];
 | |
|      * OH.ToString(rules) // => "Mo-Tu 10:00-12:00, 13:00-17:00"
 | |
|      *
 | |
|      * // should merge overlapping opening hours
 | |
|      * const timerange0 = {weekday: 1, endHour: 23, endMinutes: 30, startHour: 23, startMinutes: 0 }
 | |
|      * const touchingTimeRange = {  weekday: 1, endHour: 0, endMinutes: 0, startHour: 23, startMinutes: 30 }
 | |
|      * OH.ToString(OH.MergeTimes([timerange0, touchingTimeRange])) // => "Tu 23:00-00:00"
 | |
|      *
 | |
|      * // should merge touching opening hours
 | |
|      * const timerange0 = {weekday: 1, endHour: 23, endMinutes: 30, startHour: 23, startMinutes: 0 }
 | |
|      * const overlappingTimeRange =  { weekday: 1, endHour: 24, endMinutes: 0, startHour: 23, startMinutes: 30 }
 | |
|      * OH.ToString(OH.MergeTimes([timerange0, overlappingTimeRange])) // => "Tu 23:00-00:00"
 | |
|      *
 | |
|      */
 | |
| 
 | |
|     public static ToString(ohs: OpeningHour[]) {
 | |
|         if (ohs.length == 0) {
 | |
|             return ""
 | |
|         }
 | |
|         const partsPerWeekday: string[][] = [[], [], [], [], [], [], []]
 | |
| 
 | |
|         for (const oh of ohs) {
 | |
|             partsPerWeekday[oh.weekday].push(
 | |
|                 OH.hhmm(oh.startHour, oh.startMinutes) + "-" + OH.hhmm(oh.endHour, oh.endMinutes)
 | |
|             )
 | |
|         }
 | |
| 
 | |
|         const stringPerWeekday = partsPerWeekday.map((parts) => parts.sort().join(", "))
 | |
| 
 | |
|         const rules = []
 | |
| 
 | |
|         let rangeStart = 0
 | |
|         let rangeEnd = 0
 | |
| 
 | |
|         function pushRule() {
 | |
|             const rule = stringPerWeekday[rangeStart]
 | |
|             if (rule === "") {
 | |
|                 return
 | |
|             }
 | |
|             if (rangeStart == rangeEnd - 1) {
 | |
|                 rules.push(`${OH.days[rangeStart]} ${rule}`)
 | |
|             } else {
 | |
|                 rules.push(`${OH.days[rangeStart]}-${OH.days[rangeEnd - 1]} ${rule}`)
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         for (; rangeEnd < 7; rangeEnd++) {
 | |
|             if (stringPerWeekday[rangeStart] != stringPerWeekday[rangeEnd]) {
 | |
|                 pushRule()
 | |
|                 rangeStart = rangeEnd
 | |
|             }
 | |
|         }
 | |
|         pushRule()
 | |
| 
 | |
|         const oh = rules.join("; ")
 | |
|         if (oh === "Mo-Su 00:00-00:00") {
 | |
|             return "24/7"
 | |
|         }
 | |
|         return oh
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Merge duplicate opening-hour element in place.
 | |
|      * Returns true if something changed
 | |
|      *
 | |
|      * // should merge overlapping opening hours
 | |
|      * const oh1: OpeningHour = { weekday: 0, startHour: 10, startMinutes: 0, endHour: 11, endMinutes: 0 };
 | |
|      * const oh0: OpeningHour = { weekday: 0, startHour: 10, startMinutes: 30, endHour: 12, endMinutes: 0 };
 | |
|      * OH.MergeTimes([oh0, oh1]) // => [{ weekday: 0, startHour: 10, startMinutes: 0, endHour: 12, endMinutes: 0 }]
 | |
|      *
 | |
|      * // should merge touching opening hours
 | |
|      * const oh1: OpeningHour = { weekday: 0, startHour: 10, startMinutes: 0, endHour: 11, endMinutes: 0 };
 | |
|      * const oh0: OpeningHour = { weekday: 0, startHour: 11, startMinutes: 0, endHour: 12, endMinutes: 0 };
 | |
|      * OH.MergeTimes([oh0, oh1]) // => [{ weekday: 0, startHour: 10, startMinutes: 0, endHour: 12, endMinutes: 0 }]
 | |
|      */
 | |
|     public static MergeTimes(ohs: OpeningHour[]): OpeningHour[] {
 | |
|         const queue = ohs.map((oh) => {
 | |
|             if (oh.endHour === 0 && oh.endMinutes === 0) {
 | |
|                 const newOh = {
 | |
|                     ...oh,
 | |
|                 }
 | |
|                 newOh.endHour = 24
 | |
|                 return newOh
 | |
|             }
 | |
|             return oh
 | |
|         })
 | |
|         const newList = []
 | |
|         while (queue.length > 0) {
 | |
|             let maybeAdd = queue.pop()
 | |
| 
 | |
|             let doAddEntry = true
 | |
|             if (maybeAdd.weekday == undefined) {
 | |
|                 doAddEntry = false
 | |
|             }
 | |
| 
 | |
|             for (let i = newList.length - 1; i >= 0 && doAddEntry; i--) {
 | |
|                 let guard = newList[i]
 | |
|                 if (maybeAdd.weekday != guard.weekday) {
 | |
|                     // Not the same day
 | |
|                     continue
 | |
|                 }
 | |
| 
 | |
|                 if (
 | |
|                     OH.startTimeLiesInRange(maybeAdd, guard) &&
 | |
|                     OH.endTimeLiesInRange(maybeAdd, guard)
 | |
|                 ) {
 | |
|                     // Guard fully covers 'maybeAdd': we can safely ignore maybeAdd
 | |
|                     doAddEntry = false
 | |
|                     break
 | |
|                 }
 | |
| 
 | |
|                 if (
 | |
|                     OH.startTimeLiesInRange(guard, maybeAdd) &&
 | |
|                     OH.endTimeLiesInRange(guard, maybeAdd)
 | |
|                 ) {
 | |
|                     // 'maybeAdd'  fully covers Guard - the guard is killed
 | |
|                     newList.splice(i, 1)
 | |
|                     break
 | |
|                 }
 | |
| 
 | |
|                 if (
 | |
|                     OH.startTimeLiesInRange(maybeAdd, guard) ||
 | |
|                     OH.endTimeLiesInRange(maybeAdd, guard) ||
 | |
|                     OH.startTimeLiesInRange(guard, maybeAdd) ||
 | |
|                     OH.endTimeLiesInRange(guard, maybeAdd)
 | |
|                 ) {
 | |
|                     // At this point, the maybeAdd overlaps the guard: we should extend the guard and retest it
 | |
|                     newList.splice(i, 1)
 | |
|                     let startHour = guard.startHour
 | |
|                     let startMinutes = guard.startMinutes
 | |
|                     if (OH.startTime(maybeAdd) < OH.startTime(guard)) {
 | |
|                         startHour = maybeAdd.startHour
 | |
|                         startMinutes = maybeAdd.startMinutes
 | |
|                     }
 | |
| 
 | |
|                     let endHour = guard.endHour
 | |
|                     let endMinutes = guard.endMinutes
 | |
|                     if (OH.endTime(maybeAdd) > OH.endTime(guard)) {
 | |
|                         endHour = maybeAdd.endHour
 | |
|                         endMinutes = maybeAdd.endMinutes
 | |
|                     }
 | |
| 
 | |
|                     queue.push({
 | |
|                         startHour: startHour,
 | |
|                         startMinutes: startMinutes,
 | |
|                         endHour: endHour,
 | |
|                         endMinutes: endMinutes,
 | |
|                         weekday: guard.weekday,
 | |
|                     })
 | |
| 
 | |
|                     doAddEntry = false
 | |
|                     break
 | |
|                 }
 | |
|             }
 | |
|             if (doAddEntry) {
 | |
|                 newList.push(maybeAdd)
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         // New list can only differ from the old list by merging entries
 | |
|         // This means that the list is changed only if the lengths are different.
 | |
|         // If the lengths are the same, we might just as well return the old list and be a bit more stable
 | |
|         if (newList.length !== ohs.length) {
 | |
|             return newList
 | |
|         } else {
 | |
|             return ohs
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Gives the number of hours since the start of day.
 | |
|      * E.g.
 | |
|      * startTime({startHour: 9, startMinuts: 15}) == 9.25
 | |
|      * @param oh
 | |
|      */
 | |
|     public static startTime(oh: OpeningHour): number {
 | |
|         return oh.startHour + oh.startMinutes / 60
 | |
|     }
 | |
| 
 | |
|     public static endTime(oh: OpeningHour): number {
 | |
|         return oh.endHour + oh.endMinutes / 60
 | |
|     }
 | |
| 
 | |
|     public static startTimeLiesInRange(checked: OpeningHour, mightLieIn: OpeningHour) {
 | |
|         return (
 | |
|             OH.startTime(mightLieIn) <= OH.startTime(checked) &&
 | |
|             OH.startTime(checked) <= OH.endTime(mightLieIn)
 | |
|         )
 | |
|     }
 | |
| 
 | |
|     public static endTimeLiesInRange(checked: OpeningHour, mightLieIn: OpeningHour) {
 | |
|         return (
 | |
|             OH.startTime(mightLieIn) <= OH.endTime(checked) &&
 | |
|             OH.endTime(checked) <= OH.endTime(mightLieIn)
 | |
|         )
 | |
|     }
 | |
| 
 | |
|     public static parseHHMMRange(hhmmhhmm: string): {
 | |
|         startHour: number
 | |
|         startMinutes: number
 | |
|         endHour: number
 | |
|         endMinutes: number
 | |
|     } {
 | |
|         if (hhmmhhmm == "off") {
 | |
|             return null
 | |
|         }
 | |
| 
 | |
|         const timings = hhmmhhmm.split("-")
 | |
|         const start = OH.parseHHMM(timings[0])
 | |
|         const end = OH.parseHHMM(timings[1])
 | |
|         return {
 | |
|             startHour: start.hours,
 | |
|             startMinutes: start.minutes,
 | |
|             endHour: end.hours,
 | |
|             endMinutes: end.minutes,
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Converts an OH-syntax rule into an object
 | |
|      *
 | |
|      *
 | |
|      * const rules = OH.ParsePHRule("PH 12:00-17:00")
 | |
|      * rules.mode // => " "
 | |
|      * rules.start // => "12:00"
 | |
|      * rules.end // => "17:00"
 | |
|      *
 | |
|      * OH.ParseRule("PH 12:00-17:00") // => null
 | |
|      * OH.ParseRule("Th[-1] off") // => null
 | |
|      *
 | |
|      * const rules = OH.Parse("24/7");
 | |
|      * rules.length // => 7
 | |
|      * rules[0].startHour // => 0
 | |
|      * OH.ToString(rules) // => "24/7"
 | |
|      *
 | |
|      * const rules = OH.ParseRule("11:00-19:00");
 | |
|      * rules.length // => 7
 | |
|      * rules[0].weekday // => 0
 | |
|      * rules[0].startHour // => 11
 | |
|      * rules[3].endHour // => 19
 | |
|      *
 | |
|      * const rules = OH.ParseRule("Mo-Th 11:00-19:00");
 | |
|      * rules.length // => 4
 | |
|      * rules[0].weekday // => 0
 | |
|      * rules[0].startHour // => 11
 | |
|      * rules[3].endHour // => 19
 | |
|      *
 | |
|      */
 | |
|     public static ParseRule(rule: string): OpeningHour[] {
 | |
|         try {
 | |
|             if (rule.trim() == "24/7") {
 | |
|                 return OH.multiply(
 | |
|                     [0, 1, 2, 3, 4, 5, 6],
 | |
|                     [
 | |
|                         {
 | |
|                             startHour: 0,
 | |
|                             startMinutes: 0,
 | |
|                             endHour: 24,
 | |
|                             endMinutes: 0,
 | |
|                         },
 | |
|                     ]
 | |
|                 )
 | |
|             }
 | |
| 
 | |
|             const split = rule.trim().replace(/, */g, ",").split(" ")
 | |
|             if (split.length == 1) {
 | |
|                 // First, try to parse this rule as a rule without weekdays
 | |
|                 let timeranges = OH.ParseHhmmRanges(rule)
 | |
|                 let weekdays = [0, 1, 2, 3, 4, 5, 6]
 | |
|                 return OH.multiply(weekdays, timeranges)
 | |
|             }
 | |
| 
 | |
|             if (split.length == 2) {
 | |
|                 const weekdays = OH.ParseWeekdayRanges(split[0])
 | |
|                 const timeranges = OH.ParseHhmmRanges(split[1])
 | |
|                 return OH.multiply(weekdays, timeranges)
 | |
|             }
 | |
|             return null
 | |
|         } catch (e) {
 | |
|             console.log("Could not parse weekday rule ", rule)
 | |
|             return null
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      *
 | |
|      * OH.ParsePHRule("PH Off") // => {mode: "off"}
 | |
|      * OH.ParsePHRule("PH OPEN") // => {mode: "open"}
 | |
|      * OH.ParsePHRule("PH 10:00-12:00") // => {mode: " ", start: "10:00", end: "12:00"}
 | |
|      * OH.ParsePHRule(undefined) // => null
 | |
|      * OH.ParsePHRule(null) // => null
 | |
|      * OH.ParsePHRule("some random string") // => null
 | |
|      */
 | |
|     public static ParsePHRule(str: string): {
 | |
|         mode: string
 | |
|         start?: string
 | |
|         end?: string
 | |
|     } {
 | |
|         if (str === undefined || str === null) {
 | |
|             return null
 | |
|         }
 | |
|         str = str.trim()
 | |
|         if (!str.startsWith("PH")) {
 | |
|             return null
 | |
|         }
 | |
| 
 | |
|         str = str.trim()
 | |
|         if (str.toLowerCase() === "ph off") {
 | |
|             return {
 | |
|                 mode: "off",
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         if (str.toLowerCase() === "ph open") {
 | |
|             return {
 | |
|                 mode: "open",
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         if (!str.startsWith("PH ")) {
 | |
|             return null
 | |
|         }
 | |
|         try {
 | |
|             const timerange = OH.parseHHMMRange(str.substring(2))
 | |
|             if (timerange === null) {
 | |
|                 return null
 | |
|             }
 | |
| 
 | |
|             return {
 | |
|                 mode: " ",
 | |
|                 start: OH.hhmm(timerange.startHour, timerange.startMinutes),
 | |
|                 end: OH.hhmm(timerange.endHour, timerange.endMinutes),
 | |
|             }
 | |
|         } catch (e) {
 | |
|             return null
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     public static simplify(str: string): string {
 | |
|         return OH.ToString(OH.MergeTimes(OH.Parse(str)))
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Parses a string into Opening Hours
 | |
|      */
 | |
|     public static Parse(rules: string): OpeningHour[] {
 | |
|         if (rules === undefined || rules === "") {
 | |
|             return []
 | |
|         }
 | |
| 
 | |
|         const ohs: OpeningHour[] = []
 | |
| 
 | |
|         const split = rules.split(";")
 | |
| 
 | |
|         for (const rule of split) {
 | |
|             if (rule === "") {
 | |
|                 continue
 | |
|             }
 | |
|             try {
 | |
|                 const parsed = OH.ParseRule(rule)
 | |
|                 if (parsed !== null) {
 | |
|                     ohs.push(...parsed)
 | |
|                 }
 | |
|             } catch (e) {
 | |
|                 console.error("Could not parse ", rule, ": ", e)
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         return ohs
 | |
|     }
 | |
| 
 | |
|     /*
 | |
|  This function converts a number of ranges (generated by OpeningHours.js) into all the hours of day that a change occurs.
 | |
|  E.g.
 | |
|  Monday, some business is opended from 9:00 till 17:00
 | |
|  Tuesday from 9:30 till 18:00
 | |
|  Wednesday from 9:30 till 12:30
 | |
|  This function will extract all those moments of change and will return 9:00, 9:30, 12:30, 17:00 and 18:00
 | |
|  This list will be sorted
 | |
|  */
 | |
|     public static allChangeMoments(
 | |
|         ranges: {
 | |
|             isOpen: boolean
 | |
|             isSpecial: boolean
 | |
|             comment: string
 | |
|             startDate: Date
 | |
|             endDate: Date
 | |
|         }[][]
 | |
|     ): [number[], string[]] {
 | |
|         const changeHours: number[] = []
 | |
|         const changeHourText: string[] = []
 | |
| 
 | |
|         const extrachangeHours: number[] = []
 | |
|         const extrachangeHourText: string[] = []
 | |
| 
 | |
|         for (const weekday of ranges) {
 | |
|             for (const range of weekday) {
 | |
|                 if (!range.isOpen && !range.isSpecial) {
 | |
|                     continue
 | |
|                 }
 | |
|                 const startOfDay: Date = new Date(range.startDate)
 | |
|                 startOfDay.setHours(0, 0, 0, 0)
 | |
| 
 | |
|                 // The number of seconds since the start of the day
 | |
|                 // @ts-ignore
 | |
|                 const changeMoment: number = (range.startDate - startOfDay) / 1000
 | |
|                 if (changeHours.indexOf(changeMoment) < 0) {
 | |
|                     changeHours.push(changeMoment)
 | |
|                     changeHourText.push(
 | |
|                         OH.hhmm(range.startDate.getHours(), range.startDate.getMinutes())
 | |
|                     )
 | |
|                 }
 | |
| 
 | |
|                 // The number of seconds till between the start of the day and closing
 | |
|                 // @ts-ignore
 | |
|                 let changeMomentEnd: number = (range.endDate - startOfDay) / 1000
 | |
|                 if (changeMomentEnd >= 24 * 60 * 60) {
 | |
|                     if (extrachangeHours.indexOf(changeMomentEnd) < 0) {
 | |
|                         extrachangeHours.push(changeMomentEnd)
 | |
|                         extrachangeHourText.push(
 | |
|                             OH.hhmm(range.endDate.getHours(), range.endDate.getMinutes())
 | |
|                         )
 | |
|                     }
 | |
|                 } else if (changeHours.indexOf(changeMomentEnd) < 0) {
 | |
|                     changeHours.push(changeMomentEnd)
 | |
|                     changeHourText.push(
 | |
|                         OH.hhmm(range.endDate.getHours(), range.endDate.getMinutes())
 | |
|                     )
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         // Note that 'changeHours' and 'changeHourText' will be more or less in sync - one is in numbers, the other in 'HH:MM' format.
 | |
|         // But both can be sorted without problem; they'll stay in sync
 | |
|         changeHourText.sort()
 | |
|         changeHours.sort()
 | |
|         extrachangeHourText.sort()
 | |
|         extrachangeHours.sort()
 | |
| 
 | |
|         changeHourText.push(...extrachangeHourText)
 | |
|         changeHours.push(...extrachangeHours)
 | |
| 
 | |
|         return [changeHours, changeHourText]
 | |
|     }
 | |
| 
 | |
|     public static CreateOhObject(
 | |
|         tags: object & { _lat: number; _lon: number; _country?: string },
 | |
|         textToParse: string
 | |
|     ) {
 | |
|         // noinspection JSPotentiallyInvalidConstructorUsage
 | |
|         return new opening_hours(
 | |
|             textToParse,
 | |
|             {
 | |
|                 lat: tags._lat,
 | |
|                 lon: tags._lon,
 | |
|                 address: {
 | |
|                     country_code: tags._country.toLowerCase(),
 | |
|                     state: undefined,
 | |
|                 },
 | |
|             },
 | |
|             <any>{ tag_key: "opening_hours" }
 | |
|         )
 | |
|     }
 | |
| 
 | |
|     /*
 | |
|  Calculates when the business is opened (or on holiday) between two dates.
 | |
|  Returns a matrix of ranges, where [0] is a list of ranges when it is opened on monday, [1] is a list of ranges for tuesday, ...
 | |
|   */
 | |
|     public static GetRanges(
 | |
|         oh: any,
 | |
|         from: Date,
 | |
|         to: Date
 | |
|     ): {
 | |
|         isOpen: boolean
 | |
|         isSpecial: boolean
 | |
|         comment: string
 | |
|         startDate: Date
 | |
|         endDate: Date
 | |
|     }[][] {
 | |
|         const values = [[], [], [], [], [], [], []]
 | |
| 
 | |
|         const start = new Date(from)
 | |
|         // We go one day more into the past, in order to force rendering of holidays in the start of the period
 | |
|         start.setDate(from.getDate() - 1)
 | |
| 
 | |
|         const iterator = oh.getIterator(start)
 | |
| 
 | |
|         let prevValue = undefined
 | |
|         while (iterator.advance(to)) {
 | |
|             if (prevValue) {
 | |
|                 prevValue.endDate = iterator.getDate() as Date
 | |
|             }
 | |
|             const endDate = new Date(iterator.getDate()) as Date
 | |
|             endDate.setHours(0, 0, 0, 0)
 | |
|             endDate.setDate(endDate.getDate() + 1)
 | |
|             const value = {
 | |
|                 isSpecial: iterator.getUnknown(),
 | |
|                 isOpen: iterator.getState(),
 | |
|                 comment: iterator.getComment(),
 | |
|                 startDate: iterator.getDate() as Date,
 | |
|                 endDate: endDate, // Should be overwritten by the next iteration
 | |
|             }
 | |
|             prevValue = value
 | |
| 
 | |
|             if (value.comment === undefined && !value.isOpen && !value.isSpecial) {
 | |
|                 // simply closed, nothing special here
 | |
|                 continue
 | |
|             }
 | |
| 
 | |
|             if (value.startDate < from) {
 | |
|                 continue
 | |
|             }
 | |
|             // Get day: sunday is 0, monday is 1. We move everything so that monday == 0
 | |
|             values[(value.startDate.getDay() + 6) % 7].push(value)
 | |
|         }
 | |
|         return values
 | |
|     }
 | |
| 
 | |
|     private static parseHHMM(hhmm: string): { hours: number; minutes: number } {
 | |
|         if (hhmm === undefined || hhmm == null) {
 | |
|             return null
 | |
|         }
 | |
|         const spl = hhmm.trim().split(":")
 | |
|         if (spl.length != 2) {
 | |
|             return null
 | |
|         }
 | |
|         const hm = { hours: Number(spl[0].trim()), minutes: Number(spl[1].trim()) }
 | |
|         if (isNaN(hm.hours) || isNaN(hm.minutes)) {
 | |
|             return null
 | |
|         }
 | |
|         return hm
 | |
|     }
 | |
| 
 | |
|     private static ParseHhmmRanges(hhmms: string): {
 | |
|         startHour: number
 | |
|         startMinutes: number
 | |
|         endHour: number
 | |
|         endMinutes: number
 | |
|     }[] {
 | |
|         if (hhmms === "off") {
 | |
|             return []
 | |
|         }
 | |
|         return hhmms
 | |
|             .split(",")
 | |
|             .map((s) => s.trim())
 | |
|             .filter((str) => str !== "")
 | |
|             .map(OH.parseHHMMRange)
 | |
|             .filter((v) => v != null)
 | |
|     }
 | |
| 
 | |
|     private static ParseWeekday(weekday: string): number {
 | |
|         return OH.daysIndexed[weekday.trim().toLowerCase()]
 | |
|     }
 | |
| 
 | |
|     private static ParseWeekdayRange(weekdays: string): number[] {
 | |
|         const split = weekdays.split("-")
 | |
|         if (split.length == 1) {
 | |
|             const parsed = OH.ParseWeekday(weekdays)
 | |
|             if (parsed == null) {
 | |
|                 return null
 | |
|             }
 | |
|             return [parsed]
 | |
|         } else if (split.length == 2) {
 | |
|             let start = OH.ParseWeekday(split[0])
 | |
|             let end = OH.ParseWeekday(split[1])
 | |
|             if ((start ?? null) === null || (end ?? null) === null) {
 | |
|                 return null
 | |
|             }
 | |
|             let range = []
 | |
|             for (let i = start; i <= end; i++) {
 | |
|                 range.push(i)
 | |
|             }
 | |
|             return range
 | |
|         } else {
 | |
|             return null
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     private static ParseWeekdayRanges(weekdays: string): number[] {
 | |
|         let ranges = []
 | |
|         let split = weekdays.split(",")
 | |
|         for (const weekday of split) {
 | |
|             const parsed = OH.ParseWeekdayRange(weekday)
 | |
|             if (parsed === undefined || parsed === null) {
 | |
|                 return null
 | |
|             }
 | |
|             ranges.push(...parsed)
 | |
|         }
 | |
|         return ranges
 | |
|     }
 | |
| 
 | |
|     private static multiply(
 | |
|         weekdays: number[],
 | |
|         timeranges: {
 | |
|             startHour: number
 | |
|             startMinutes: number
 | |
|             endHour: number
 | |
|             endMinutes: number
 | |
|         }[]
 | |
|     ) {
 | |
|         if ((weekdays ?? null) == null || (timeranges ?? null) == null) {
 | |
|             return null
 | |
|         }
 | |
|         const ohs: OpeningHour[] = []
 | |
|         for (const timerange of timeranges) {
 | |
|             for (const weekday of weekdays) {
 | |
|                 ohs.push({
 | |
|                     weekday: weekday,
 | |
|                     startHour: timerange.startHour,
 | |
|                     startMinutes: timerange.startMinutes,
 | |
|                     endHour: timerange.endHour,
 | |
|                     endMinutes: timerange.endMinutes,
 | |
|                 })
 | |
|             }
 | |
|         }
 | |
|         return ohs
 | |
|     }
 | |
|     public static getMondayBefore(d) {
 | |
|         d = new Date(d)
 | |
|         const day = d.getDay()
 | |
|         const diff = d.getDate() - day + (day == 0 ? -6 : 1) // adjust when day is sunday
 | |
|         return new Date(d.setDate(diff))
 | |
|     }
 | |
| }
 |