Tagging time restrictions: yes + conditional=no or conditional=yes?

Let’s take a parking lot where you have to pay a parking fee between 8:00 and 18:00 (“during the day”). At other times (“at night”), you can park for free. How would we tag this?

  • fee:conditional = yes @ (08:00-18:00)
  • fee = yes + fee:conditional = no @ (18:00-08:00)
  • Something else
0 voters

For comparison, let’s take this second case, which is typical in my city: parking fees Monday through Friday from 9:00 to 20:00 and Saturdays from 9:00 to 18:00; free at other times. How would we map this?

  • fee:conditional = yes @ (Mo-Fr 09:00-20:00; Sa 09:00-18:00)
  • fee = yes + fee:conditional = no @ (Mo-Fr 00:00-09:00,20:00-24:00; Sa 00:00-09:00,18:00-24:00; Su)
  • Something else
0 voters

When mapping conditional time restrictions, I often come across an issue that I/we encountered a few years ago when revising the street parking schema, where we proposed and documented the variant fee=yes + fee:conditional=no @ (inverted_time). This “inverted” time specification was intended to allow data users who do not interpret conditional restrictions to nevertheless conclude that “parking fees apply here” based solely on fee=yes.

However, this leads to complicated time specifications when the time periods have to be reversed or leads to “artificial” and unintuitive tagging constructs – just to make tagging convenient for data users that don’t care about conditional restrictions.

In an older thread, for example, variants such as fee = yes + fee:conditional = no @ 24/7; yes @ (08:00-18:00) were discussed. But it was also noted that such “tricks” bend the conditional restriction scheme too far just to satisfy inadequate data evaluators. On top of that, the statement fee=yes is not correct in itself if it does not even apply half the time.

Besides fee, this issue also applies to other values, of course. For example, the wiki page on maxstay mentions this situation using the example maxstay:conditional = 1 hour @ (08:00-18:00) vs. maxstay = 1 hour + maxstay:conditional = no @ (18:00-08:00), "depending on whatever it is better for simple data consumers to show sometimes wrong “no maxstay” or sometimes wrong “1 hour maxstay”.

The typical case in my city (and certainly many others) is, by the way, a fee from Monday to Friday during period X, on Saturday during period Y, and otherwise it is free. There are dozens of different parking zones with many different times. If there are also times in between when – e.g., due to a loading zone – no fees are charged for a few hours during the day, the “artificial” variants quickly cause confusion.

Meanwhile, I tend to only use the “simple” fee:conditional = yes @ (...) variant. Evaluators that cannot handle this are simply (still) incomplete. What do you think?

1 Like

Additional question:
If fee:conditional = yes @ (...) is tagged,

  • we don’t need a standalone fee=*.
  • we still need a fee=no.
  • we still need a fee=* value that is different from yes or no.
  • Something else
0 voters

Exactly, which is why I came to the conclusion that I should map the hours per the signage, not the “inverted hours” and that data consumers must evaluate the :conditional and any data consumers that ignore the :conditional can’t be relied upon.

This means the hours that are signed match what is tagged - without needing to do some mental gymnastics to invert the hours.

Data consumers can still parse out the conditional and determine that if general day hours are covered by the :conditional then represent it as such, so it doesn’t mean they always have to communicate the exact hours to the user, it’ll depend on the app/map use case. For example parking with fee:conditional=yes @ 06:00-22:00 + fee=no could be shown on a map as “$” to cover the general case that during the day the parking is paid, even with fee=no being set, or it could be represented as a dollar sign icon with a clock badge to indicate it depends on the time of day.

I’d still tag basic fee=yes if there is any chance that you must pay there. I.e. one should always fail-safe[1].

If Conditional restrictions wiki is up to date, support for fee:conditional=* is very low (if not abysmal). I would also worry that even if some data consumer does support fee:conditional, that they might not be able to parse it fully for whatever reason and might thus fail to use it (see examples for other *:conditional tags in that table).

If one would like to keep from using inverted conditions, but still being actually supported in most data consumers[2], I would still recommend using fee=yes + conditional overrides (;) in fee:conditional=*, e.g. as in your example fee = yes + fee:conditional = no @ 24/7; yes @ (08:00-18:00).

It is not “a trick”; overrides using ; are a valid and documented usage of conditional restrictions, in fact, it’s one the most integral parts of the specification.

It would be like saying that opening_hours=Mo-Fr 12:00-20:00; Mo,Th 13:00-13:30 off is somehow “bending” the opening_hours spec and is a “trick”. It’s not, it is exactly why it was designed that way with overrides[3].

Using ; overrides makes the specification more flexible and thus more useful. Yes, it also makes is (slightly, IMHO) more complex, of course. Given that whole point of fee:conditional existence is that fee was not flexible enough, I would say that it was good design choice.

As for “inadequate data evaluators”, yeah, some might not work with that (or even simpler!) conditional restrictions which follow the documented specification.

While ideally all such inadequate data evaluators would be fixed (so they become adequate), it is not likely to happen overnight, and not without significant efforts all around.

Thus the whole idea of having the base fee=yes to fall back to – it is exactly so we can fail-safe (i.e. saying that you must pay if it fails to fully parse fee:conditional for whatever reason) when data consumer is inadequate.

I agree that fee=yes is not ideologically perfect (some people use fee=conditional to “fix” that, but it brings its own support problems), but it is closest to the truth given limits of documented fee=* values, and is a best solution that actually currently works while still conveying information to an average user (i.e. omitting fee=* is a proverbial “cure worse than an original poison”).

TL;DR: So, until such time when most data consumers fully support *:conditional, I’d recommend staying with fee=yes + fee:conditional=*.


  1. i.e. I’d argue that most people using fee* use it to finding places not requiring them to pay money - and they’d be much annoyed if they had to pay money when it was implied that they don’t have to pay. OTOH, if the app said they have to pay, but in their specific case they actually could get if for free in the end, they would not be nearly as much annoyed, if at all ↩︎

  2. which is actually what it boils down to IMHO - unsupported data is not much better then no data from practical POV ↩︎

  3. you have , if you do not want overrides there but an addition instead, but I digress ↩︎

3 Likes

I chose. fee=yes + fee:conditional=no @ ... in both polls because 8:00 - 18:00 is the part of the day where people are most active and the most parking will be happening. Most people would describe a parking area like this as paid, but with a caveat that it is free at night, or free after 18:00. Describing it as free parking most of the time, but paid during the day would be technically true but would also come across as odd and confusing to most people. So I guess I think about conditional tagging the same way. If you only had to pay between 8:00 - 10:00, then I might go with fee=no + fee:conditional = yes @ ....

Perhaps it’s too late to to change, but it occurs to me that maybe extending the conditional access tagging scheme to the fee tag isn’t the best semantics to represent the hours when a fee is charged. Maybe it should be more like opening_hours where you just put the hours and there is no need to choose betwen yes @ ... or no @ .... It seems like fee_hours=* would complement fee=yes nicely.

1 Like

I hypothesize that the “inverted” use of fee=yes + fee:conditional=no @ (...) tends to hinder software support for fee:conditional, whereas wider use of fee:conditional=yes @ (...) would tend to promote it. Because an application will only be interested in displaying the simple “times when I have to pay” and not inverted times that are difficult to read.

Using fee:conditional=yes @ (...) makes this trivial; basically, you can show the value directly.

Using fee:conditional=no @ (...), that’s 300 lines of crazy code just to interpret simple patterns for days and times (coincidentally, I just wrote that today for a future version of the OSM parking project).

There was already hour_on= replaced by *:conditional= before. This shouldn’t be undone and repeated lightly. Worse, having a *_hours= for every attribute is not scalable.
I do see fee= / toll= differently from access= , where *:conditional= was originated. It can mean whether there’s any pricing at all. Free periods can be compared with discounts, although being more permanent. Therefore fee=yes can be used whenever there’s some fees at anytime.
More technically, charge:conditional=0 USD @ (18:00-08:00) could be used. The problem with this is charge:conditional= can have multiple prices for other periods. Then for convenience, fee:conditional=no @ (18:00-08:00) may be seen as a practical shortcut, and a useful excerpt.
Maybe unsuitable, but I would like to mention @aharvey had tried to propose discount= alongside *:fee= / surcharge= for payments and time. This may easily show free periods, and other variations eg half price. They can be discount:conditional= =100% @ (18:00-08:00) , =50% @ (18:00-08:00) , etc. The added benefit in the latter is you can simply show there’s a period when it’s cheaper without entering the exact price, further reducing the risk of getting outdated.

1 Like

To further complicate this discussion: In fact, I have made a difficult note years ago about limited-time free parkings Conditional restrictions - OpenStreetMap Wiki Talk:Key:charge - OpenStreetMap Wiki
If there’s 1hr free, but you are charged for 2hr after 2hr from time of entry (please ignore round-up/down), is it really fee:conditional=no @ (stay <= 1 hour) ? You are already charged from the 1st hour!
Sure, one can explain the stay is about a condition about total time stayed. But then how should this be distinguished from the case of counting after the free period expires, ie charged for 1hr after 2hr?
Of course, this may be dismissed as a price list, over-complicated, and out-of-scope for OSM. Regardless of the solution of this, I feel it shows why fee= is better treated as the existence of any pricing, to be simple.

Less scalable than having a *:conditional for every attribute? It could be *:hours= if a namespace is considered better.

And this only works for time. *:conditional= is invented for extensibility to handle multiple conditions =no @ ((18:00-08:00) AND *)
Random example “Note: Cambridge residents with Resident Parking Permits do not have to pay for parking after 6 p.m. in this lot.” Park a Car in Cambridge
If your fee*hours= has to be supported, I will need to add fee*hours=08:00-18:00 "For residents" etc further. I don’t think we need an “LoD” for attributes using different tags by complexity, to have LoD1 fee*hours= for time, and LoD2 fee:conditional= for multiple conditions. (Reminds me of EDTF levels)

2 Likes

This was more for tagging times when the fee charge=* is higher or lower than the baseline, eg. paying by cash gives a discount, paying by credit card incurs a surcharge, paying on Sunday or a public holiday incurs a surcharge.

It’s for a different scenario than just tagging when a fee applies ie. fee:conditional.

I would bet that it is overall complexity of implementation that slows the adoption instead.[1]

And lack of FOSS parsing libraries in multiple different programming languages is big (but not only) part of that (i.e. even if one has a already finished library parser, UI for displaying - not to mention adding/editing - such complex fields is several orders of magnitude more complex then just showing a simple checkbox (as one would for bare fee=*).

While I’ve not tried writing one, inverting the condition looks like relatively simple part of writing full-fledged *:conditional parser and UI.

Mere 300 lines? Is the code open source, by any chance? Any caveats on which subset of syntax it supports (and doesn’t support)?

Just the “very simplistic parser” (in words of its author) opening_hours parser is more than 2800 lines of Java code (as counted by cloc(1)), and parsing opening_hours is just one part of parsing fee:conditional=*.


  1. not to mention that even when implemented, it is hard on users, especially in not just display/readonly, but if it for editing that field ↩︎

1 Like

I know that Organic Maps supports opening_hours (and is very good at it, being able to report when an amenity about to close, just like some popular commercial tools out there). Given this, support for fee:conditional in amenities should be trivial. I can’t easily test it, as that tag is rare where I am from (Brazil), but looking at the code, there are no references to :conditional except in highway attributes (for variable access restrictions and variable speed limits), so I assume there is no support. This is a good opportunity for a useful and easy-to-implement feature request.

That ok of course. Without a good smart conditional restriction editor, I often find it more natural/intuitive (perhaps out of habit or partly influenced by outdated documentation) to validate a single condition using OSM.de’s opening_hours evaluation tool.

It would be interesting to:

  1. Poll the reason for these choices: is it because there is a fee at any point at all (would mappers make the same choice if there was a fee only for 1 hour on a Sunday?), or because they are thinking of what’s the most likely fee state when the amenity is requested (e.g. most business hours, or most of the amenity’s opening hours)
    – In my hometown, for example, there is some central street parking charging only on weekdays 16:00-20:00. Would that be a fee=yes because there is a fee at all or a fee=no because it’s not on during most business hours (about 08:00-18:00 here; this is the criterion that I follow) or peak business hours (this varies more but in that particular area would be 09:00-12:00 and 16:00-18:00)?
  2. Poll for the choice in the case of conditional access restrictions, to evaluate if the pattern is universal across different conditional tags
    – Another example in my hometown: there is a major urban street that is closed on weekends for leisure activities. It has been mapped it as motor_vehicle=yes (implicit) + motor_vehicle:conditional=no @ (Sa,Su,PH 07:00-20:00). An user of a simpler navigation app without support for conditional restrictions might be incorrectly routed through that street on weekends, but would be routed through it correctly during the week, arguably when they are most likely to be travelling there. If instead we chose to invert the conditional and use motor_vehicle=no, then only a smart navigation app would use it during the week. In an urban area, this may be just a minor annoyance to the user, but in a remote area with few easy options to get around the restriction, this mapping choice could have far greater implications for the user.

I think this decision is very relevant when viewing street parking restrictions. The image below shows these restrictions in a busy area of ​​my city (this is JOSM’s Parking Lanes style from Klemm et al combined with JOSM’s Mapnik style). Some restrictions are lifted on weekends, and some free parking areas have restrictions for a brief period during business hours on weekdays. This visualization would be somewhat different if one chose the “safest” value for the main tag. As it is, it represents the typical restrictions during business hours, which is what the average user will most likely encounter when visiting the area. While very useful for planning the arrival route, I don’t know any app that displays this, but a navigation app could vary this display according to a selected time or estimated arrival time to help find a parking space, reducing search time and fuel consumption.

The “No parking (not signed)” cases in this image are due to the presence of dedicated contraflow bus lanes.

Edit: I found out that Zlant’s parking lanes viewer supports :conditional, allowing viewing the exact street parking conditions at any given time!

In terms of the tagging I would say that both ways are entirely equivalent to storing the detailed data so either could be used. But in terms of which is preferable, I would generally think in terms of what would make more sense for a data consumer that doesn’t understand the *:conditional tags to see. If it would be a better/safer fall-back to see fee=yes, then that would be what I’d normally tag, and then use the *:conditional tag to add the exceptions to this.

4 Likes