
Parses and validates OpenStreetMap opening hours strings into a type-safe data model and back, offering fast processing and lenient parsing for unambiguous syntax variations.
A Kotlin multiplatform library to parse OpenStreetMap opening hours from a string into a data model and back.
It is pure Kotlin, no dependencies
It mostly follows the OpenStreetMap opening hours specification. For details and remarks, see the section Specification. As of Nov 2025, 96.60% of opening hours strings in the wild are considered valid, 99.13% are understood.
The data model is type-safe, i.e. it is not possible to create an invalid opening hours string from the data model
It is very fast. Expect one order of magnitude faster than other opening hours syntax parsers, e.g. it parses the average opening hours string about 10x as fast as the Java OpeningHoursParser.
See src/jvmTest/kotlin/tasks/print_statistics/PrintStatistics.kt for the script that measures it.
It is currently used in StreetComplete.
© 2024-2025 Tobias Zwick. This library is released under the terms of the MIT License.
Add de.westnordost:osm-opening-hours:0.4.0 as a Maven dependency or download the jar from there.
Usage e.g.
// parse into data model
val hours = "Mo-Fr 08:00-18:00; Sa 10:00-12:00".toOpeningHoursOrNull()
// create from data model
val hoursString = hours?.toString()It mostly follows the OpenStreetMap opening hours specification version 0.7.4, with a few additions/remarks:
Whitespaces: e.g. Jan05Mo-Fr08:00
The specification does not comprehensively define where and how many spaces are allowed
or required in-between the tokens. So, we assume that any number (including none) of them are
allowed and only required in places where two successive tokens use the same set of characters
(e.g. week05␣05:00, Su␣sunset). Other parsers consider the lack of spaces
valid too, though the canonical form always contains spaces in-between for clarity and readability.
More restrictive date ranges within month: e.g. easter+Su-09-We +3 days considered invalid
The specification allows for an unnecessarily complex syntax for dates and syntax that doesn't make sense (and is therefore not used). So dates are parsed in a slightly more strict way according to the rules as described in this comment.
Beyond what is considered valid according to the specification and the mentioned remarks, the following unambiguous syntax variations are understood by the parser if instructed to be lenient:
MO-FR, WEEK 01, Easter…)~, 〜 and to can be used for ranges (e.g. 08:00—12:00, Mo to Fr):, ,, ;, 0, 9,…) wherever a normal one is expected、 enumeration comma and variantsMo-Th, May-Aug Fr-Sa).
This is normally not allowed.
Strings like May-Aug, Mo-Th are interpreted as May-Aug Mo-Th.12:30AM, 08:00 p.m., 12:00 pm, 08:00 ㏂)12 AM, 16, 14h, 13時)h, . and 時 as minutes separators (e.g 12 h 30, 8h15am, 08.00, 8:30, 12時45分)dusk-dawn/2h resolve to dusk-dawn/02:00 (i.e. every 2 hours)8:30) or too many leading zeroes (e.g. 011:030)Fr-Su 24/7)Tuesday, Tue, Di, Mar, 火, 星期二, вт…)Mo.)Mo-Sa,PH,Su)Mo-Sa: 08:00-12:00)Dec 25,31, Dec 25-27,31)December)Dec.)Jan 5)week 1-9)week keyword is repeated for each range (e.g. week 1-9, week 40-52 instead of week 1-9,40-52)Usage e.g.
val hours = "October to Dec.: Sun,PH,Thu: 8 am — 12h30 pm; WEEK1 24/7"
.toOpeningHours(lenient = true)When (re-)creating the string from the data model, of course always a valid opening hours string is returned.
A Kotlin multiplatform library to parse OpenStreetMap opening hours from a string into a data model and back.
It is pure Kotlin, no dependencies
It mostly follows the OpenStreetMap opening hours specification. For details and remarks, see the section Specification. As of Nov 2025, 96.60% of opening hours strings in the wild are considered valid, 99.13% are understood.
The data model is type-safe, i.e. it is not possible to create an invalid opening hours string from the data model
It is very fast. Expect one order of magnitude faster than other opening hours syntax parsers, e.g. it parses the average opening hours string about 10x as fast as the Java OpeningHoursParser.
See src/jvmTest/kotlin/tasks/print_statistics/PrintStatistics.kt for the script that measures it.
It is currently used in StreetComplete.
© 2024-2025 Tobias Zwick. This library is released under the terms of the MIT License.
Add de.westnordost:osm-opening-hours:0.4.0 as a Maven dependency or download the jar from there.
Usage e.g.
// parse into data model
val hours = "Mo-Fr 08:00-18:00; Sa 10:00-12:00".toOpeningHoursOrNull()
// create from data model
val hoursString = hours?.toString()It mostly follows the OpenStreetMap opening hours specification version 0.7.4, with a few additions/remarks:
Whitespaces: e.g. Jan05Mo-Fr08:00
The specification does not comprehensively define where and how many spaces are allowed
or required in-between the tokens. So, we assume that any number (including none) of them are
allowed and only required in places where two successive tokens use the same set of characters
(e.g. week05␣05:00, Su␣sunset). Other parsers consider the lack of spaces
valid too, though the canonical form always contains spaces in-between for clarity and readability.
More restrictive date ranges within month: e.g. easter+Su-09-We +3 days considered invalid
The specification allows for an unnecessarily complex syntax for dates and syntax that doesn't make sense (and is therefore not used). So dates are parsed in a slightly more strict way according to the rules as described in this comment.
Beyond what is considered valid according to the specification and the mentioned remarks, the following unambiguous syntax variations are understood by the parser if instructed to be lenient:
MO-FR, WEEK 01, Easter…)~, 〜 and to can be used for ranges (e.g. 08:00—12:00, Mo to Fr):, ,, ;, 0, 9,…) wherever a normal one is expected、 enumeration comma and variantsMo-Th, May-Aug Fr-Sa).
This is normally not allowed.
Strings like May-Aug, Mo-Th are interpreted as May-Aug Mo-Th.12:30AM, 08:00 p.m., 12:00 pm, 08:00 ㏂)12 AM, 16, 14h, 13時)h, . and 時 as minutes separators (e.g 12 h 30, 8h15am, 08.00, 8:30, 12時45分)dusk-dawn/2h resolve to dusk-dawn/02:00 (i.e. every 2 hours)8:30) or too many leading zeroes (e.g. 011:030)Fr-Su 24/7)Tuesday, Tue, Di, Mar, 火, 星期二, вт…)Mo.)Mo-Sa,PH,Su)Mo-Sa: 08:00-12:00)Dec 25,31, Dec 25-27,31)December)Dec.)Jan 5)week 1-9)week keyword is repeated for each range (e.g. week 1-9, week 40-52 instead of week 1-9,40-52)Usage e.g.
val hours = "October to Dec.: Sun,PH,Thu: 8 am — 12h30 pm; WEEK1 24/7"
.toOpeningHours(lenient = true)When (re-)creating the string from the data model, of course always a valid opening hours string is returned.