forked from MapComplete/MapComplete
		
	Fix: opening hours now correctly shows "closes at <point far in the future>"
This commit is contained in:
		
							parent
							
								
									8584a4ba68
								
							
						
					
					
						commit
						d5b0976fb0
					
				
					 3 changed files with 53 additions and 46 deletions
				
			
		| 
						 | 
					@ -326,6 +326,7 @@
 | 
				
			||||||
            "openTill": "till",
 | 
					            "openTill": "till",
 | 
				
			||||||
            "open_24_7": "Open around the clock",
 | 
					            "open_24_7": "Open around the clock",
 | 
				
			||||||
            "open_during_ph": "During a public holiday, it is",
 | 
					            "open_during_ph": "During a public holiday, it is",
 | 
				
			||||||
 | 
					            "open_until": "Closes at {date}",
 | 
				
			||||||
            "opensAt": "from",
 | 
					            "opensAt": "from",
 | 
				
			||||||
            "ph_closed": "closed",
 | 
					            "ph_closed": "closed",
 | 
				
			||||||
            "ph_not_known": " ",
 | 
					            "ph_not_known": " ",
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -160,7 +160,7 @@ export class OH {
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            for (let i = newList.length - 1; i >= 0 && doAddEntry; i--) {
 | 
					            for (let i = newList.length - 1; i >= 0 && doAddEntry; i--) {
 | 
				
			||||||
                let guard = newList[i]
 | 
					                const guard = newList[i]
 | 
				
			||||||
                if (maybeAdd.weekday != guard.weekday) {
 | 
					                if (maybeAdd.weekday != guard.weekday) {
 | 
				
			||||||
                    // Not the same day
 | 
					                    // Not the same day
 | 
				
			||||||
                    continue
 | 
					                    continue
 | 
				
			||||||
| 
						 | 
					@ -236,9 +236,8 @@ export class OH {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Gives the number of hours since the start of day.
 | 
					     * Gives the number of hours since the start of day.
 | 
				
			||||||
     * E.g.
 | 
					     *
 | 
				
			||||||
     * startTime({startHour: 9, startMinuts: 15}) == 9.25
 | 
					     * // OH.startTime({startHour: 9, startMinutes: 15}) // => 9.25
 | 
				
			||||||
     * @param oh
 | 
					 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public static startTime(oh: OpeningHour): number {
 | 
					    public static startTime(oh: OpeningHour): number {
 | 
				
			||||||
        return oh.startHour + oh.startMinutes / 60
 | 
					        return oh.startHour + oh.startMinutes / 60
 | 
				
			||||||
| 
						 | 
					@ -346,8 +345,8 @@ export class OH {
 | 
				
			||||||
            const split = rule.trim().replace(/, */g, ",").split(" ")
 | 
					            const split = rule.trim().replace(/, */g, ",").split(" ")
 | 
				
			||||||
            if (split.length == 1) {
 | 
					            if (split.length == 1) {
 | 
				
			||||||
                // First, try to parse this rule as a rule without weekdays
 | 
					                // First, try to parse this rule as a rule without weekdays
 | 
				
			||||||
                let timeranges = OH.ParseHhmmRanges(rule)
 | 
					                const timeranges = OH.ParseHhmmRanges(rule)
 | 
				
			||||||
                let weekdays = [0, 1, 2, 3, 4, 5, 6]
 | 
					                const weekdays = [0, 1, 2, 3, 4, 5, 6]
 | 
				
			||||||
                return OH.multiply(weekdays, timeranges)
 | 
					                return OH.multiply(weekdays, timeranges)
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -450,7 +449,7 @@ export class OH {
 | 
				
			||||||
        return ohs
 | 
					        return ohs
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /*
 | 
					    /**
 | 
				
			||||||
This function converts a number of ranges (generated by OpeningHours.js) into all the hours of day that a change occurs.
 | 
					This function converts a number of ranges (generated by OpeningHours.js) into all the hours of day that a change occurs.
 | 
				
			||||||
E.g.
 | 
					E.g.
 | 
				
			||||||
Monday, some business is opened from 9:00 till 17:00
 | 
					Monday, some business is opened from 9:00 till 17:00
 | 
				
			||||||
| 
						 | 
					@ -458,6 +457,11 @@ Tuesday from 9:30 till 18:00
 | 
				
			||||||
Wednesday from 9:30 till 12:30
 | 
					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 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
 | 
					This list will be sorted
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const startDate = new Date(2025,4,20,10,0,0)
 | 
				
			||||||
 | 
					const endDate = new Date(2025,4,20,17,0,0)
 | 
				
			||||||
 | 
					const changes = OH.allChangeMoments([[{isOpen: true, isSpecial: false, comment: "", startDate, endDate}]])
 | 
				
			||||||
 | 
					changes // => [[36000,61200], ["10:00", "17:00"]]
 | 
				
			||||||
*/
 | 
					*/
 | 
				
			||||||
    public static allChangeMoments(
 | 
					    public static allChangeMoments(
 | 
				
			||||||
        ranges: {
 | 
					        ranges: {
 | 
				
			||||||
| 
						 | 
					@ -483,8 +487,7 @@ This list will be sorted
 | 
				
			||||||
                startOfDay.setHours(0, 0, 0, 0)
 | 
					                startOfDay.setHours(0, 0, 0, 0)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                // The number of seconds since the start of the day
 | 
					                // The number of seconds since the start of the day
 | 
				
			||||||
                // @ts-ignore
 | 
					                const changeMoment: number = (range.startDate.getTime() - startOfDay.getTime()) / 1000
 | 
				
			||||||
                const changeMoment: number = (range.startDate - startOfDay) / 1000
 | 
					 | 
				
			||||||
                if (changeHours.indexOf(changeMoment) < 0) {
 | 
					                if (changeHours.indexOf(changeMoment) < 0) {
 | 
				
			||||||
                    changeHours.push(changeMoment)
 | 
					                    changeHours.push(changeMoment)
 | 
				
			||||||
                    changeHourText.push(
 | 
					                    changeHourText.push(
 | 
				
			||||||
| 
						 | 
					@ -493,8 +496,7 @@ This list will be sorted
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                // The number of seconds till between the start of the day and closing
 | 
					                // The number of seconds till between the start of the day and closing
 | 
				
			||||||
                // @ts-ignore
 | 
					                const changeMomentEnd: number = (range.endDate.getTime() - startOfDay.getTime()) / 1000
 | 
				
			||||||
                let changeMomentEnd: number = (range.endDate - startOfDay) / 1000
 | 
					 | 
				
			||||||
                if (changeMomentEnd >= 24 * 60 * 60) {
 | 
					                if (changeMomentEnd >= 24 * 60 * 60) {
 | 
				
			||||||
                    if (extrachangeHours.indexOf(changeMomentEnd) < 0) {
 | 
					                    if (extrachangeHours.indexOf(changeMomentEnd) < 0) {
 | 
				
			||||||
                        extrachangeHours.push(changeMomentEnd)
 | 
					                        extrachangeHours.push(changeMomentEnd)
 | 
				
			||||||
| 
						 | 
					@ -665,13 +667,18 @@ This list will be sorted
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Constructs the opening-ranges for either this week, or for next week if there are no more openings this week
 | 
					     * Constructs the opening-ranges for either this week, or for next week if there are no more openings this week.
 | 
				
			||||||
 | 
					     * Note: 'today' is mostly used for testing
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * const oh = new opening_hours("mar 15 - oct 15")
 | 
				
			||||||
 | 
					     * const ranges = OH.createRangesForApplicableWeek(oh, new Date(2025,4,20,10,0,0))
 | 
				
			||||||
 | 
					     * ranges // => {ranges: [[],[],[],[],[],[]], startingMonday: new Date(2025,4,18,10,0,0)}
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public static createRangesForApplicableWeek(oh: opening_hours): {
 | 
					    public static createRangesForApplicableWeek(oh: opening_hours, today?: Date): {
 | 
				
			||||||
        ranges: OpeningRange[][]
 | 
					        ranges: OpeningRange[][]
 | 
				
			||||||
        startingMonday: Date
 | 
					        startingMonday: Date
 | 
				
			||||||
    } {
 | 
					    } {
 | 
				
			||||||
        const today = new Date()
 | 
					        today ??= new Date()
 | 
				
			||||||
        today.setHours(0, 0, 0, 0)
 | 
					        today.setHours(0, 0, 0, 0)
 | 
				
			||||||
        const lastMonday = OH.getMondayBefore(today)
 | 
					        const lastMonday = OH.getMondayBefore(today)
 | 
				
			||||||
        const nextSunday = new Date(lastMonday)
 | 
					        const nextSunday = new Date(lastMonday)
 | 
				
			||||||
| 
						 | 
					@ -699,7 +706,7 @@ This list will be sorted
 | 
				
			||||||
    public static weekdaysIdentical(openingRanges: OpeningRange[][], startday = 0, endday = 4) {
 | 
					    public static weekdaysIdentical(openingRanges: OpeningRange[][], startday = 0, endday = 4) {
 | 
				
			||||||
        const monday = openingRanges[startday]
 | 
					        const monday = openingRanges[startday]
 | 
				
			||||||
        for (let i = startday + 1; i <= endday; i++) {
 | 
					        for (let i = startday + 1; i <= endday; i++) {
 | 
				
			||||||
            let weekday = openingRanges[i]
 | 
					            const weekday = openingRanges[i]
 | 
				
			||||||
            if (weekday.length !== monday.length) {
 | 
					            if (weekday.length !== monday.length) {
 | 
				
			||||||
                return false
 | 
					                return false
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
| 
						 | 
					@ -831,12 +838,12 @@ This list will be sorted
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            return [parsed]
 | 
					            return [parsed]
 | 
				
			||||||
        } else if (split.length == 2) {
 | 
					        } else if (split.length == 2) {
 | 
				
			||||||
            let start = OH.ParseWeekday(split[0])
 | 
					            const start = OH.ParseWeekday(split[0])
 | 
				
			||||||
            let end = OH.ParseWeekday(split[1])
 | 
					            const end = OH.ParseWeekday(split[1])
 | 
				
			||||||
            if ((start ?? null) === null || (end ?? null) === null) {
 | 
					            if ((start ?? null) === null || (end ?? null) === null) {
 | 
				
			||||||
                return null
 | 
					                return null
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            let range = []
 | 
					            const range = []
 | 
				
			||||||
            for (let i = start; i <= end; i++) {
 | 
					            for (let i = start; i <= end; i++) {
 | 
				
			||||||
                range.push(i)
 | 
					                range.push(i)
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
| 
						 | 
					@ -847,8 +854,8 @@ This list will be sorted
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private static ParseWeekdayRanges(weekdays: string): number[] {
 | 
					    private static ParseWeekdayRanges(weekdays: string): number[] {
 | 
				
			||||||
        let ranges = []
 | 
					        const ranges = []
 | 
				
			||||||
        let split = weekdays.split(",")
 | 
					        const split = weekdays.split(",")
 | 
				
			||||||
        for (const weekday of split) {
 | 
					        for (const weekday of split) {
 | 
				
			||||||
            const parsed = OH.ParseWeekdayRange(weekday)
 | 
					            const parsed = OH.ParseWeekdayRange(weekday)
 | 
				
			||||||
            if (parsed === undefined || parsed === null) {
 | 
					            if (parsed === undefined || parsed === null) {
 | 
				
			||||||
| 
						 | 
					@ -1054,7 +1061,7 @@ export class ToTextualDescription {
 | 
				
			||||||
                languages[supportedLanguage] = "{a}. {b}"
 | 
					                languages[supportedLanguage] = "{a}. {b}"
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        let chainer = new TypedTranslation<{ a; b }>(languages)
 | 
					        const chainer = new TypedTranslation<{ a; b }>(languages)
 | 
				
			||||||
        let tr = trs[0]
 | 
					        let tr = trs[0]
 | 
				
			||||||
        for (let i = 1; i < trs.length; i++) {
 | 
					        for (let i = 1; i < trs.length; i++) {
 | 
				
			||||||
            tr = chainer.PartialSubsTr("a", tr).PartialSubsTr("b", trs[i])
 | 
					            tr = chainer.PartialSubsTr("a", tr).PartialSubsTr("b", trs[i])
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -7,7 +7,7 @@ import BaseUIElement from "../BaseUIElement"
 | 
				
			||||||
import Toggle from "../Input/Toggle"
 | 
					import Toggle from "../Input/Toggle"
 | 
				
			||||||
import { VariableUiElement } from "../Base/VariableUIElement"
 | 
					import { VariableUiElement } from "../Base/VariableUIElement"
 | 
				
			||||||
import Table from "../Base/Table"
 | 
					import Table from "../Base/Table"
 | 
				
			||||||
import { Translation } from "../i18n/Translation"
 | 
					import { Translation, TypedTranslation } from "../i18n/Translation"
 | 
				
			||||||
import Loading from "../Base/Loading"
 | 
					import Loading from "../Base/Loading"
 | 
				
			||||||
import opening_hours from "opening_hours"
 | 
					import opening_hours from "opening_hours"
 | 
				
			||||||
import Locale from "../i18n/Locale"
 | 
					import Locale from "../i18n/Locale"
 | 
				
			||||||
| 
						 | 
					@ -73,7 +73,7 @@ export default class OpeningHoursVisualization extends Toggle {
 | 
				
			||||||
        ranges: OpeningRange[][],
 | 
					        ranges: OpeningRange[][],
 | 
				
			||||||
        lastMonday: Date
 | 
					        lastMonday: Date
 | 
				
			||||||
    ): BaseUIElement {
 | 
					    ): BaseUIElement {
 | 
				
			||||||
        // First, a small sanity check. The business might be permanently closed, 24/7 opened or something other special
 | 
					        // First, a small sanity check. The business might be permanently closed, 24/7 opened or be another special case
 | 
				
			||||||
        if (ranges.some((range) => range.length > 0)) {
 | 
					        if (ranges.some((range) => range.length > 0)) {
 | 
				
			||||||
            // The normal case: we have items for the coming days
 | 
					            // The normal case: we have items for the coming days
 | 
				
			||||||
            return OpeningHoursVisualization.ConstructVizTable(oh, ranges, lastMonday)
 | 
					            return OpeningHoursVisualization.ConstructVizTable(oh, ranges, lastMonday)
 | 
				
			||||||
| 
						 | 
					@ -98,8 +98,7 @@ export default class OpeningHoursVisualization extends Toggle {
 | 
				
			||||||
        const today = new Date()
 | 
					        const today = new Date()
 | 
				
			||||||
        today.setHours(0, 0, 0, 0)
 | 
					        today.setHours(0, 0, 0, 0)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // @ts-ignore
 | 
					        const todayIndex = Math.ceil((today.getTime() - rangeStart.getTime()) / (1000 * 60 * 60 * 24))
 | 
				
			||||||
        const todayIndex = Math.ceil((today - rangeStart) / (1000 * 60 * 60 * 24))
 | 
					 | 
				
			||||||
        // By default, we always show the range between 8 - 19h, in order to give a stable impression
 | 
					        // By default, we always show the range between 8 - 19h, in order to give a stable impression
 | 
				
			||||||
        // Ofc, a bigger range is used if needed
 | 
					        // Ofc, a bigger range is used if needed
 | 
				
			||||||
        const earliestOpen = Math.min(8 * 60 * 60, ...changeHours)
 | 
					        const earliestOpen = Math.min(8 * 60 * 60, ...changeHours)
 | 
				
			||||||
| 
						 | 
					@ -193,11 +192,9 @@ export default class OpeningHoursVisualization extends Toggle {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        const startOfDay: Date = new Date(range.startDate)
 | 
					        const startOfDay: Date = new Date(range.startDate)
 | 
				
			||||||
        startOfDay.setHours(0, 0, 0, 0)
 | 
					        startOfDay.setHours(0, 0, 0, 0)
 | 
				
			||||||
        // @ts-ignore
 | 
					        const startpoint = (range.startDate.getTime() - startOfDay.getTime()) / 1000 - earliestOpen
 | 
				
			||||||
        const startpoint = (range.startDate - startOfDay) / 1000 - earliestOpen
 | 
					 | 
				
			||||||
        // prettier-ignore
 | 
					        // prettier-ignore
 | 
				
			||||||
        // @ts-ignore
 | 
					        const width = (100 * (range.endDate.getTime() - range.startDate.getTime()) / 1000) / (latestclose - earliestOpen)
 | 
				
			||||||
        const width = (100 * (range.endDate - range.startDate) / 1000) / (latestclose - earliestOpen);
 | 
					 | 
				
			||||||
        const startPercentage = (100 * startpoint) / availableArea
 | 
					        const startPercentage = (100 * startpoint) / availableArea
 | 
				
			||||||
        return new FixedUiElement(textToShow)
 | 
					        return new FixedUiElement(textToShow)
 | 
				
			||||||
            .SetStyle(`left:${startPercentage}%; width:${width}%`)
 | 
					            .SetStyle(`left:${startPercentage}%; width:${width}%`)
 | 
				
			||||||
| 
						 | 
					@ -236,7 +233,7 @@ export default class OpeningHoursVisualization extends Toggle {
 | 
				
			||||||
        changeHourText: string[],
 | 
					        changeHourText: string[],
 | 
				
			||||||
        earliestOpen: number
 | 
					        earliestOpen: number
 | 
				
			||||||
    ): [BaseUIElement, string] {
 | 
					    ): [BaseUIElement, string] {
 | 
				
			||||||
        let header: BaseUIElement[] = []
 | 
					        const header: BaseUIElement[] = []
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        header.push(
 | 
					        header.push(
 | 
				
			||||||
            ...OpeningHoursVisualization.CreateLinesAtChangeHours(
 | 
					            ...OpeningHoursVisualization.CreateLinesAtChangeHours(
 | 
				
			||||||
| 
						 | 
					@ -249,7 +246,7 @@ export default class OpeningHoursVisualization extends Toggle {
 | 
				
			||||||
        let showHigher = false
 | 
					        let showHigher = false
 | 
				
			||||||
        let showHigherUsed = false
 | 
					        let showHigherUsed = false
 | 
				
			||||||
        for (let i = 0; i < changeHours.length; i++) {
 | 
					        for (let i = 0; i < changeHours.length; i++) {
 | 
				
			||||||
            let changeMoment = changeHours[i]
 | 
					            const changeMoment = changeHours[i]
 | 
				
			||||||
            const offset = (100 * (changeMoment - earliestOpen)) / availableArea
 | 
					            const offset = (100 * (changeMoment - earliestOpen)) / availableArea
 | 
				
			||||||
            if (offset < 0 || offset > 100) {
 | 
					            if (offset < 0 || offset > 100) {
 | 
				
			||||||
                continue
 | 
					                continue
 | 
				
			||||||
| 
						 | 
					@ -285,12 +282,18 @@ export default class OpeningHoursVisualization extends Toggle {
 | 
				
			||||||
    /*
 | 
					    /*
 | 
				
			||||||
     * Visualizes any special case: e.g. not open for a long time, 24/7 open, ...
 | 
					     * Visualizes any special case: e.g. not open for a long time, 24/7 open, ...
 | 
				
			||||||
     * */
 | 
					     * */
 | 
				
			||||||
    private static ShowSpecialCase(oh: any) {
 | 
					    private static ShowSpecialCase(oh: opening_hours) {
 | 
				
			||||||
        const opensAtDate = oh.getNextChange()
 | 
					        const nextChange = oh.getNextChange()
 | 
				
			||||||
        if (opensAtDate === undefined) {
 | 
					        if (nextChange !== undefined) {
 | 
				
			||||||
            const comm = oh.getComment() ?? oh.getUnknown()
 | 
					            const nowOpen = oh.getState(new Date())
 | 
				
			||||||
            if (!!comm) {
 | 
					            const t = Translations.t.general.opening_hours
 | 
				
			||||||
                return new FixedUiElement(comm)
 | 
					            const tr: TypedTranslation<{ date }> = nowOpen ? t.open_until : t.closed_until
 | 
				
			||||||
 | 
					            const date = nextChange.toLocaleString()
 | 
				
			||||||
 | 
					            return tr.Subs({ date })
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        const comment = oh.getComment() ?? oh.getUnknown()
 | 
				
			||||||
 | 
					        if (typeof comment === "string") {
 | 
				
			||||||
 | 
					            return new FixedUiElement(comment)
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (oh.getState()) {
 | 
					        if (oh.getState()) {
 | 
				
			||||||
| 
						 | 
					@ -298,8 +301,4 @@ export default class OpeningHoursVisualization extends Toggle {
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        return Translations.t.general.opening_hours.closed_permanently.Clone()
 | 
					        return Translations.t.general.opening_hours.closed_permanently.Clone()
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
        return Translations.t.general.opening_hours.closed_until.Subs({
 | 
					 | 
				
			||||||
            date: opensAtDate.toLocaleString(),
 | 
					 | 
				
			||||||
        })
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue