Door number (2) is attractive if you can do it. The problem with many applications tracking long-lived facilities (theater halls and other building components, aircraft, cargo vessels, ...) is that they assume those assets are invariant. They are not. As relatively inflexible and expensive capital assets, they are not changed very often--but they definitely do change over their entire life cycles, which span years to decades. Seats are added or removed, rooms are subdivided or combined or radically reassigned, and so on.
If you have a
Cinema_Halls table that can record identity information (e.g. name of the room, its location in the building), lifecycle information (e.g. start and stop timestamp, or in databases like PostgreSQL that have slightly better temporal handling, an interval defining the service lifetime), their capacity and/or other interesting parameters (e.g. total number of seats, number of handicapped seats, usable area in square feet, maximum occupancy as decreed by the Fire Marshal, ...), then your database accurately records the parameters for the hall as a function of time. It exposed a little more complexity/variability in your data model, but in ways that reflect reality; doing utilization and other joins against that facility table will be straightforward.
However, you may not be able to make such a significant schema change. In which case you must put the data externally. Option (1) is such an approach--in effect, a facilities description table that assumes "Hall 1" is itself invariant, but that some of its operational parameters vary over time. There are pros to this approach. Your other tables, for example, will simply reference
hallID = 1 always, and not need to have items 1, 4, 17, 19, and 31 all be different instantiations of the logical "Hall 1" at different points in time. But the queries when you do want hall capacity information will be a little more complex. And if Hall 1 is at some point separated into Hall 1A and Hall 1B, the "halls are invariant" assumption breaks anyway.
If you cannot make Option (1) work as a separate table, alternatives are to encode the lifecycle information somewhere--e.g. stored procedures, or into static data embedded within your analytics functions/methods/objects. That's an ugly, bifurcated, and somewhat fragile approach--but there are many cases where that kind of duct tape and baling wire solution the best you can do, given other practical / organizational constraints. Despite the genuine downsides of the implementation, on the plus side, the code asking for the required values (e.g. "number of seats in hall x at time t") can often do so cleanly (because the
number_of_seats function/method/procedure hides how the value is computed).
Your final suggestion, choice (3), is admirable but unworkable, if only because workaday databases provide such weak support to represent and compute temporal relationships, properties, and realities. PostgreSQL has a pretty decent interval data type. It's rich, precise, and works only in PostgreSQL. If you're using PostgreSQL, have at--but it's non-portable. And an interval type, while a start, is hardly an entire "temporal reasoning" capability. With other databases, you're even worse off. Oracle has two interval types, un-unified, of varying precision. Other databases like DB2 and MySQL support some interval-like computations in their SQL operators and functions for date/time objects, but have no first-class interval types.
So, in order of solution richness and elegance: options 2, 1, 3.