Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/gromnitsky/shopping-hours
A flexible shopping-hours calendar & calculator
https://github.com/gromnitsky/shopping-hours
Last synced: about 6 hours ago
JSON representation
A flexible shopping-hours calendar & calculator
- Host: GitHub
- URL: https://github.com/gromnitsky/shopping-hours
- Owner: gromnitsky
- Created: 2018-02-08T19:34:51.000Z (almost 7 years ago)
- Default Branch: master
- Last Pushed: 2018-02-24T09:52:41.000Z (over 6 years ago)
- Last Synced: 2024-11-13T07:58:20.297Z (6 days ago)
- Language: JavaScript
- Size: 46.9 KB
- Stars: 0
- Watchers: 3
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
Awesome Lists containing this project
README
# shopping-hours
A flexible shopping-hours calendar & calculator.
| `dist/*` | lang | type | minified |
|-------------------------| ----- | ---- | -------- |
| `shopping_hours.min.js` | es5† | UMD | x |
| `shopping_hours.js` | es6 | UMD | |(† may require [babel-polyfill](https://babeljs.io/docs/usage/polyfill/))
* ~8.6KB minified.
* Moveable feasts (Easter & Pentecost).
* User-provided plugins for moveble dates.
* Elastic dates as `fri.4/11` (the 4th Friday of November),
`wed.last/-`, `sat/-`.
* Returns the current status (open/closed) for any date.
* Automatically calculates the next opening/closing datetime.Calendar itself is .txt file that uses a simple line-oriented DSL,
e.g.:~~~
-/- 9:00-13:00,14:00-18:00
1/1 0:0-0:0 o new year
easter_orthodox 0:0-0:0 o
fri.4/11 6:30-23:00 - black friday
sun/- 0:0-0:0
~~~## Why?
http://gromnitsky.blogspot.com/2018/02/a-shopping-hours-calculator.html
## Quick Start
$ npm i shopping-hours
Create a simple calendar:
~~~
$ node
> sh = require('shopping-hours')
[Function: shopping_hours]
> cal = `-/- 9:00-13:00,14:00-18:00
... sat/- 11:00-16:00
... sun/- :-:`
'-/- 9:00-13:00,14:00-18:00\nsat/- 11:00-16:00\nsun/- :-:'
~~~Next, parse the cal:
~~~
> my_shop = sh(cal)
{ parsed_input: { vars: {}, events: [ [Object], [Object], [Object] ] },
parse: [Function: parse],
resolve: [Function: resolve],
business: [Function: business] }
~~~The fn of interest here is `#business()`. To get the status of our
little online store, pass an optional date to it:~~~
> my_shop.business('2018-02-08 01:00')
{ status: 'closed', next: 2018-02-08T07:00:00.000Z }
~~~Because it resolves the cal for 1am, it says that the shop is
'closed', & 'next' key indicates (printed in UTC here; the diff for
`Europe/Kiev` timezone is +2h) when the shop 'opens'.If we update the time to 9am:
~~~
> my_shop.business('2018-02-08 09:00')
{ status: 'open', next: 2018-02-08T11:00:00.000Z }
~~~it says, the shop is 'open' & it closes at 1pm for a lunch break.
2018-02-08 is Thursday, if we specify the next day as
~~~
> my_shop.business('2018-02-09 18:01')
{ status: 'closed', next: 2018-02-10T09:00:00.000Z }
~~~it says the shop is 'closed' & it opens at 11am next day (Saturday).
For Saturday it returns:
~~~
> my_shop.business('2018-02-10 18:01')
{ status: 'closed', next: 2018-02-12T07:00:00.000Z }
~~~Because we set no working hours for Sundays, it jumps to the next
available date, which in this case is Monday.## DSL
(See a comprehensive example in `test/calendar1.txt`.)
The DSL is line-oriented. Lines starting w/ `#` or empty lines are
ignored.There are 2 types of expressions: variable assignments (VA) & event
definitions (ED).VAs can appear in any order. Their goal is to change a
parsing/resolving/calculating behaviour. The VA syntax is:name = value
`value` is preserved verbatim.
EDs syntax is:
date timeranges flags desc
(`flags` & `desc` are optional.)
EDs are analysed in order of increasing precedence. If you want to
override an ED, add another one to the end of the cal.`date` has 2 forms: `day/month` or a single word like
`easter_catholic`. The premise of the latter is to allow complex
moving dates. The API allows to add user-defined words.`month` in `day/month` has 2 forms: a digit or `-` char that is auto
replaced by the cur month.`day` has 3 forms: a digit, `-` (auto replaced by a cur day) or
`dayOfTheWeek.spec`, where `dayOfTheWeek` is one of mon-sun words &
`spec` is either a digit of a word `last`. E.g., `fri.4` means the 4th
Friday of the month, `mon.last` -- the last Monday.`timeranges` ('working hours') are a list of `HH:MM-HH:MM` pairs
separated by commas. E.g.. `9:00-13:00,14:00-18:00` (or even just
`:-:`, where missing digits are auto substituted by 0s). An empty
range `0:0-0:0` means there are no working hours for the day.`flags` is a string, in which each char means smthg to the
resolver. `o` is should be used for the 'official' gov holidays, `-`
for any other event. No flag == `-`. Regular sat/sun EDs should *not*
use the `o` flag.`desc` is an arbitrary string. If you have a `desc` don't forget about
the `flags`.A suggested order of EDs:
~~~
# a requried default event, when no other dates match
-/- 9:00-18:00
# a specific date
24/8 :-: o Independence Day
# a regular weekend
sat/- 10:30-17:00
sun/- :-:
~~~### Supported variables
`auto-move-holidays = true` turns on the auto moving of the 'official'
holidays (marked w/ the `o` flag) to the next working day when they
fall on a Sat or Sun.## API
The lib operates in 3 distinct, consecutive phases: parsing, resolving
& calculation. The idea is: you parse a cal only once, then invoke a
calc fn as much you need (i.e., in a timer).To feel all 3 steps in a row, run `test/cli test/calendar1.txt`.
let sh = require('shopping-hours')
### 1. Parsing
~~~typescript
function sh(calendar: string, opt?: Options): SH;interface SH {
parsed_input: CalendarParsed;
parse(): CalendarParsed;
resolve(today: Date, pdata: CalendarParsed): Object;
business(today?: Date, pdata?: CalendarParsed): Status;
}
~~~`sh` internally invokes `#parse()` & assignes the result to
`#parsed_input`. May raise `ParseError` on invalid input.`opt` allows passing a *plugin* that calculates moveble dates.
~~~typescript
interface Options {
plugins?: Array<(today: Date) => Object>;
}
~~~Look for `plugin_easter` in `index.js` for an example.
### 2. Resolving
~~~typescript
SH.resolve(today: Date, pdata: CalendarParsed): Object;
~~~The next step is to resolve all `sun/-` et al. defs to actual
dates. You don't need to invoke this fn, the calc step does it autom.### 3. Calculating
~~~typescript
SH.business(today?: Date, pdata?: CalendarParsed): Status;interface Status {
status: "open" | "closed";
next: Date | null;
}
~~~The fn you invoke to get the status of a 'store' according to the
`today` param. If no `today` is passed, the new `Date()` object is
used.`Status#next` may be `null` if a loop in the cal is detected (no EDs
have a useful 'working hours' range).## License
MIT.