Or: how what was supposed to be just a simple string parsing task escalated into computing planetary movements.

Opening Hours

While working on the train station maps for KDE Itinerary I came across OSM’s opening_hours tags on restaurants/shops/amenities/etc. Those contain a description for which time intervals a given thing is available/open, in a machine-readable format. Being able to evaluate this would be nice, as KDE Itinerary knows the time you are going to be at a train station, so we can make the map more useful by focusing on elements that are actually available while you are there.

OSM’s format is extensively documented, and also used by other systems that we use already (such as OpenTripPlanner) or that might become relevant for us in the future (such as ParkAPI). Another place where we encounter a similar (but simpler) format is schema.org annotations in websites that Plasma Browser Integration evaluates.

So, obviously we need this in a library we can use in all those cases.

Details

Let’s look at a few examples of what OSM’s opening hours expressions can do, starting with the simple (and by far most common) case:

Mo-Fr 10:00-18:00; Sa 12:00-16:00

Localization aside this is still largely human-readable, and easy to evaluate with a bit of string parsing and QDateTime math.

Another common case is this slight variation:

Mo-Fr 09:00-15:00; PH off

PH stands for public holiday. That might look like a minor addition, but it moves from a bit of off-the-shelf date/time math to knowing the public holidays at the location this expression applies to. Luckily there’s a framework for that already, KF5::Holidays. We now however also have to track additional context information for the expression, as they evaluate differently in different places.

Looking at the more rarely used features, there’s means to select the n-th weekday in a month for example (Nov Tue[-1] - last Tuesday in November), or periodicity on weeks (week1-53/2 - any odd week), and many more such cases to express complex conditions. If this reminds of the recurrence handling in iCal/KF5::CalendarCore, it is very much like this.

To top it off, and to get to astronomical body movements, there’s also selectors for sunrise or twilight events. Those are computed based on the angular sun position at a given geographic coordinate at which the opening hours expression is evaluated. KF5::Holidays fortunately had most of this already, the missing bits have been added as part of this work.

So overall another nice reminder of what we all know to be the answer to “How hard can it be?” ;-)

Limits

Most of the above is implemented, some of the more rare details are still missing but at least can be added when needed. There’s more however, namely the SH (school holiday) selector. We currently have no comprehensive and world-wide collection of school holiday data to implement this. Therefore expression containing this feature will raise an error.

Show me the code!

The code consists of a C++ library, QML bindings and a small QML demo application and can be found here.

Screenshot of the KOpeningHours demo application showing parsing and evaluation of an OSM opening hours expression visualized in an interval table.
KOpeningHours demo application showing parsing and evaluation of an OSM opening hours expression.

To build and use this you also needs the very latest KF5::Holidays, all necessary extensions and improvements of that should be part of the upcoming KF 5.77 release though.