opening_hours tag is used in OpenStreetMap project to describe time ranges when a specific facility (for example, a cafe) is open. As it has pretty complex syntax which require special parsing and additional processing to extract some useful information (e.g. whether a facility is open at specific time, next time it's going to open/close, or a readable set of working hours), this library was written.
Examples of some complex real-life opening_hours values:
Mo,Tu,Th,Fr 12:00-18:00;Sa 12:00-17:00; Th[3] off; Th[-1] off
a library which works from 12:00 to 18:00 on workdays except Wednesday, and from 12:00 to 17:00 on Saturday. It also has breaks on third and last Thursday of each month.
00:00-24:00; Tu-Su 8:30-9:00 off; Tu-Su 14:00-14:30 off; Mo 08:00-13:00 off
around-the-clock shop with some breaks.
```javascript var oh = new opening_hours('We 12:00-14:00');
var from = new Date("01 Jan 2012"); var to = new Date("01 Feb 2012");
// high-level API { var intervals = oh.getOpenIntervals(from, to); for (var i in intervals) console.log('We are ' + (intervals[i][2] ? 'maybe ' : '') + 'open from ' + (intervals[i][3] ? '("' + intervals[i][3] + '") ' : '') + intervals[i][0] + ' till ' + intervals[i][1] + '.');
var duration_hours = oh.getOpenDuration(from, to).map(function(x) { return x / 1000 / 60 / 60 });
if (duration_hours[0])
console.log('For a given range, we are open for ' + duration_hours[0] + ' hours');
if (duration_hours[1])
console.log('For a given range, we are maybe open for ' + duration_hours[1] + ' hours');
}
// helper function function logState(startString, endString, oh, past) { if (past === true) past = 'd'; else past = '';
var output = '';
if (oh.getUnknown()) {
output += ' maybe open'
+ (oh.getComment() ? ' but that depends on: "' + oh.getComment() + '"' : '');
} else {
output += ' ' + (oh.getState() ? 'open' : 'close' + past)
+ (oh.getComment() ? ', comment "' + oh.getComment() + '"' : '');
}
console.log(startString + output + endString + '.');
}
// simple API { var state = oh.getState(); // we use current date var unknown = oh.getUnknown(); var comment = oh.getComment(); var nextchange = oh.getNextChange();
logState('We\'re', '', oh, true);
if (typeof nextchange === 'undefined')
console.log('And we will never ' + (state ? 'close' : 'open'));
else
console.log('And we will '
+ (oh.getUnknown(nextchange) ? 'maybe ' : '')
+ (state ? 'close' : 'open') + ' on ' + nextchange);
}
// iterator API { var iterator = oh.getIterator(from);
logState('Initially, we\'re', '', iterator, true);
while (iterator.advance(to))
logState('Then we', ' at ' + iterator.getDate(), iterator);
logState('And till the end we\'re', '', iterator, true);
} ```
javascript
var oh = new opening_hours('We 12:00-14:00');
Constructs openinghours object, given the openinghours tag value.
Throws an error string if the expression is malformed or unsupported.
javascript
var every_week_is_same = oh.isWeekSame();
Checks whether open intervals are same for every week. Useful for giving a user hint whether time table may change for another week.
Here and below, unless noted otherwise, all arguments are expected to be and all output will be in the form of Date objects.
javascript
var intervals = oh.getOpenIntervals(from, to);
Returns array of open intervals in a given range, in a form of [ [ from1, to1, unknown1, comment1 ], [ from2, to2, unknown2, comment2 ] ]
Intervals are cropped with the input range.
javascript
var duration = oh.getOpenDuration(from, to);
Returns an array with two durations for a given date range, in milliseconds. The first element is the duration for which the facility is open and the second is the duration for which the facility is maybe open (unknown is used).
This API is useful for one-shot checks, but for iteration over intervals you should use the more efficient Iterator API.
javascript
var is_open = oh.getState(date);
Checks whether the facility is open at the given date. You may omit date to use current date.
javascript
var unknown = oh.getUnknown();
Checks whether the opening state is conditional or unknown at the given date. You may omit date to use current date. Conditions can be expressed in comments. If unknown is true then is_open will be false since it is not sure if it is open.
javascript
var comment = oh.getComment();
Returns the comment (if one is specified) for the facility at the given date. You may omit date to use current date. Comments can be specified for any state.
If no comment is specified this function will return undefined.
javascript
var next_change = oh.getNextChange(date, limit);
Returns date of next state change. You may omit date to use current date.
Returns undefined if the next change cannot be found. This may happen if the state won't ever change (e.g. 24/7
) or if search goes beyond limit (which is date + ~5 years if omitted).
javascript
var iterator = oh.getIterator(date);
Constructs an iterator which can go through open/close points, starting at date. You may omit date to use current date.
javascript
var current_date = iterator.getDate();
Returns current iterator position.
javascript
var is_open = iterator.getState();
Returns whether the facility is open at the current iterator position in time.
javascript
var unknown = iterator.getUnknown();
Checks whether the opening state is conditional or unknown at the current iterator position in time.
javascript
var comment = iterator.getComment();
Returns the comment (if one is specified) for the facility at the current iterator position in time.
If no comment is specified this function will return undefined.
javascript
var had_advanced = iterator.advance(limit);
Advances an iterator to the next position, but not further that a limit (which is current position + ~5 years if omitted and is used to prevent infinite loop on non-periodic opening_hours, e.g. 24/7
), returns whether the iterator was moved.
For instance, returns false if the iterator would go beyond limit or if there's no next position (24/7
case).
Almost everything from openinghours definition is supported, as well as some extensions (indicated as EXT: below). For instance, the library is able to process 99.4% of all openinghours tags from Russia OSM data.
Mo 10:00-20:00; Tu 12:00-18:00
)off
keyword to indicate that the facility is closed at that time (Mo-Fr 10:00-20:00; 12:00-14:00 off
)Mo-Fr
, Jan-Feb
, week 2-10
, Jan 10-Feb 10
) and time (12:00-16:00
, 12:00-14:00,16:00-18:00
) conditionsMo-Fr 10:00-16:00; We 12:00-18:00
means that on Wednesday the facility is open from 12:00 till 18:00, not from 10:00 to 18:00.Jan 10-Feb 10: 07:30-12:00
) but this is not required. This was implemented to also parse the syntax proposed by Netzwolf10:00-12:00,14:00-16:00
)
10:00-26:00
, 10:00-02:00
)00:00-24:00
, so Mo-Fr 24/7
(though not really correct) is valid and will be handled correctlyMo-Fr; Tu off
)12.00-16.00
is valid (this is used quite widely)Mo 12:00-14:00,16:00-20:00
and Mo 12:00-14:00 16:00-20:00
are the same thing10:00-sunset
) (sunrise: '06:00', sunset: '18:00')10:00+
)Mo-We,Fr
)Fr-Mo
)Th[1,2-3]
, Fr[-1]
)Jan,Mar-Apr
)Dec-Jan
)Jan 01-Feb 03 10:00-20:00
)Jan 01-26 10:00-20:00
), with periods as well Jan 01-29/7 10:00-20:00
)Jan 23-31/3,Feb 1-12,Mar 1
)week 04-07 10:00-20:00
)week 2-53/2 10:00-20:00
)week 1,3-5,7-30/2 10:00-20:00
)Mo unknown; Th-Fr 09:00-18:00 open
)
Mo unknown "on appointment"; Th-Fr 09:00-18:00 open "female only"; Su closed "really"
)
"on appointment"
) which will result in unknown.Simple node.js based test framework is bundled. You can run it with node test.js
or with make test
.
Simple node.js based benchmark is bundled. You can run it with node benchmark.js
or with make benchmark
.
On author's Intel Core i5-2400 library allows ~20k/sec constructor calls and ~10k/sec openIntervals() calls with one week period. This may further improve in future.