Skip to content

Commit

Permalink
refactor .active filter
Browse files Browse the repository at this point in the history
  • Loading branch information
commenthol committed Sep 10, 2017
1 parent 0b1f180 commit c306889
Show file tree
Hide file tree
Showing 10 changed files with 222 additions and 37 deletions.
7 changes: 6 additions & 1 deletion TODO.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
- [x] parsing rules, update specification
- [x] new CalEvent based on results from `date-chinese`
- [x] other timezone as Asia/Shanghai
- [ ] New Feature: Calculus for Diwali (KE, SU, IN)
- [x] New Feature: Change build process for `holidays.json`. Allow building for single countries
- [X] document in `README.md`
- [x] Change Rule: Move from `spring equinox` to `march equinox` to avoid confusions between southern/ northern hemisphere
Expand All @@ -19,3 +18,9 @@
en: Spring Equinox Day
jp: 春分の日
```
- [x] document disabling rule in states/ regions
- [x] active feature introduced with Kobe Bryant Day (US-CA-LA)
- document
- write test case
- [ ] split project in source and data
- [ ] New Feature: Calculus for Diwali (KE, SU, IN)
2 changes: 2 additions & 0 deletions docs/Holidays.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ initialize holidays for a country/state/region

- **opts.types**: `Array`, holiday types to consider

- **opts.data**: `Object`, holiday data object - see data/holidays.json


### Holidays.setHoliday(rule, opts)

Expand Down
56 changes: 52 additions & 4 deletions docs/specification.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Specification for `holidays.yaml`

Version: 1.0.0
Version: 1.1.0

This document describes the data contained within the files `holidays.yaml` and
`names.yaml`.
Expand Down Expand Up @@ -28,8 +28,10 @@ This document describes the data contained within the files `holidays.yaml` and
* [Enable Date only for odd/ even numbered years](#enable-date-only-for-odd-even-numbered-years)
* [Enable Date only for certain periods of years](#enable-date-only-for-certain-periods-of-years)
* [Holiday based on other holidays (bridge days)](#holiday-based-on-other-holidays-bridge-days)
* [Enabling a rule since or in certain years](#enabling-a-rule-since-or-in-certain-years)
* [Disabling a rule](#disabling-a-rule)
* [Moving a date](#moving-a-date)
* [Disabling a rule in states/ regions](#disabling-a-rule-in-states-regions)
* [Generation of `holidays.json`](#generation-of-holidaysjson)

<!-- toc! -->
Expand Down Expand Up @@ -168,7 +170,7 @@ A fix day for a given year is attributed with `YYYY-MM-DD`.

- `01-01` is January first
- `12-11` is December 11th
- `'2015-10-09'` is October 9th of 2015 (Enclose such date in quotes as otherwise it will be expanded to an ISO date by js-yaml)
- `'2015-10-09'` is October 9th of 2015 (Enclose such date in quotes as otherwise it will be expanded to an ISO date by yaml parser)

### Fixed Date at beginning of a Month

Expand Down Expand Up @@ -415,13 +417,36 @@ Rule: `<date> if MM-DD (and MM-DD)? is (<type>)? holiday`
- `09-22 if 09-21 is holiday` is September 22nd is public holiday only if September 21st is also a holiday
- `09-22 if 09-21 and 09-23 is public holiday` is September 22nd is public holiday only if September 21st and September 23rd are public holidays

### Enabling a rule since or in certain years

> __Note:__ Use quotes around dates!

```yaml
days:
# rule is active since 2004
08-25:
active:
- from: '2004-01-01'
...
# rule is active in years 1990...1999, 2004...2005-08, 2016...
08-24:
active:
- from: '1990-01-01'
to: '1999-07-01'
- from: '2004-01-01'
to: '2005-08-03'
- from: '2016-01-01'
```

### Disabling a rule

On any rule it is possible to disable it for a given date. For every year an entry can be applied to the list.

E.g. in case that the 4th Monday in November is the 2015-11-23 then the day will not be a holiday.

```
> __Note__: Use quotes around date!

```yaml
days:
4th monday after 11-01:
disable:
Expand All @@ -436,7 +461,9 @@ In order to move a date for a rule use `disable` together with `enable`.

E.g. in case that the 4th Monday in November is the 2015-11-23 then the day gets moved to 2015-11-27.

```
> __Note__: Use quotes around dates!

```yaml
days:
4th monday in November:
disable:
Expand All @@ -447,6 +474,27 @@ days:
en: Day of National Sovereignty
```

### Disabling a rule in states/ regions

Sometimes it is necessary to disable a general rule for a single state or region.

```yaml
holidays:
OZ:
name:
en: Oz
...
days:
04-01:
name:
en: 1st of April
regions:
IX:
name: Kingdom of IX
days:
04-01: false # disables rule
```

## Generation of `holidays.json`

To generate the file `holidays.json` which is used by the module run:
Expand Down
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,11 +38,11 @@
"cover": "istanbul cover _mocha --report lcov --report text -- -R dot --check-leaks test/*.mocha.js",
"lint": "eslint --quiet '**/*.js'",
"test": "mocha",
"zuul": "zuul test/Holidays.mocha.js",
"test:all": "npm run yaml && mocha test/all.mocha.js",
"transpile": "babel -d lib src",
"watch": "watch-run -p data/countries/*.yaml npm run yaml",
"yaml": "node scripts/holidays2json.js",
"test:all": "npm run yaml && mocha test/all.mocha.js"
"zuul": "zuul test/Holidays.mocha.js"
},
"babel": {
"presets": [
Expand Down
38 changes: 23 additions & 15 deletions src/CalEvent.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
'use strict'

const isDate = require('./internal/utils').isDate
const {isDate} = require('./internal/utils')
const CalDate = require('caldate')

class CalEvent {
Expand Down Expand Up @@ -40,30 +40,38 @@ class CalEvent {

/**
* @param {Number} year - year to filter
* @param {Object[]} active - definition of active ranges `{from: {Number}, [to]: {Number}}`
* @param {Object[]} active - definition of active ranges `{from: {Date}, [to]: {Date}}`
* @return {this} for chaining
*/
filter (year, active) {
var diff
var isActive = true
if (active) {
isActive = false
active.forEach((a) => {
function isActive (date) {
if (!active) {
if (date.year === year) {
return true
} else {
return false
}
}
const _date = date.toDate()
for (let a of active) {
const {from, to} = a
if (
(a.from && a.to && a.from <= year && a.to >= year) ||
(a.from && !a.to && a.from <= year)
date.year === year &&
((from && to && from <= _date && to > _date) ||
(from && !to && from <= _date))
) {
isActive = true
return true
}
})
}
}

this.dates = this.dates.filter((date) => {
if (!isActive || year !== date.year || date._filter) {
diff = year - date.year
} else {
if (!date._filter && isActive(date)) {
return date
}
})
return diff

return this
}

push (calEvent) {
Expand Down
27 changes: 14 additions & 13 deletions src/Data.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ class Data {
*/
getCountries (lang) {
var o = {}
var countries = this.data.holidays
var countries = this.data.holidays || {}
Object.keys(countries).forEach((k) => {
o[k] = this._name(countries, k, lang)
})
Expand Down Expand Up @@ -131,8 +131,7 @@ class Data {
*/
getHolidays () {
var self = this
var tmp
var o
var rules

if (!(this.opts && this.opts.country)) {
return
Expand All @@ -141,28 +140,30 @@ class Data {
var country = this.opts.country.toUpperCase()
var state = this.opts.state
var region = this.opts.region
var tmp = _.get(this.data, ['holidays', country])

if ((tmp = this.data.holidays[country])) {
o = {}
this._assign(o, tmp)
if (tmp) {
rules = {}
this._assign(rules, tmp)
if ((state && tmp.regions && (tmp = tmp.regions[state])) ||
(state && tmp.states && (tmp = tmp.states[state]))
) {
this._assign(o, tmp)
this._assign(rules, tmp)
if (region && tmp.regions && (tmp = tmp.regions[region])) {
this._assign(o, tmp)
this._assign(rules, tmp)
}
}
Object.keys(o).forEach(function (key) {
Object.keys(rules).forEach(function (key) {
// assign name references with `_name`
var _name = o[key]._name
var _name = rules[key]._name
if (_name && self.data.names[_name]) {
delete o[key]._name
o[key] = _.merge({}, self.data.names[_name], o[key])
delete rules[key]._name
rules[key] = _.merge({}, self.data.names[_name], rules[key])
}
})
}
return o

return rules
}

/**
Expand Down
22 changes: 20 additions & 2 deletions src/Holidays.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ const _ = {
omit: require('lodash.omit'),
set: require('lodash.set')
}
const toYear = require('./internal/utils').toYear
const {toYear, toDate} = require('./internal/utils')
const Data = require('./Data')
const DateFn = require('./DateFn')

Expand Down Expand Up @@ -50,6 +50,7 @@ Holidays.prototype = {
* @param {Array|String} opts.languages - set language(s) with ISO 639-1 shortcodes
* @param {String} opts.timezone - set timezone
* @param {Array} opts.types - holiday types to consider
* @param {Object} [opts.data] - holiday data object - see data/holidays.json
*/
init (country, state, region, opts) {
var self = this
Expand Down Expand Up @@ -96,6 +97,7 @@ Holidays.prototype = {
* @param {Object|String} [opts] - holiday options, if String then opts is used as name
* @param {Object} opts.name - translated holiday names e.g. `{ en: 'name', es: 'nombre', ... }`
* @param {String} opts.type - holiday type `public|bank|school|observance`
* @throws {TypeError}
* @return {Boolean} if holiday could be set returns `true`
*/
setHoliday (rule, opts) {
Expand All @@ -115,6 +117,21 @@ Holidays.prototype = {
opts = _.set({type: 'public'}, ['name', lang], opts)
}

// convert active properties to Date
if (opts.active) {
if (!Array.isArray(opts.active)) {
throw TypeError('.active is not of type Array: ' + rule)
}
opts.active = opts.active.map((a) => {
let from = toDate(a.from)
let to = toDate(a.to)
if (!(from || to)) {
throw TypeError('.active needs .from or .to property: ' + rule)
}
return {from, to}
})
}

// check for supported type
if (!this._hasType(opts.type)) {
return false
Expand All @@ -127,7 +144,8 @@ Holidays.prototype = {
this.holidays[rule].fn = fn
return true
} else {
console.error('could not parse rule:', rule)
// throw Error('could not parse rule: ' + rule) // NEXT
console.log('could not parse rule: ' + rule)
}
return false
},
Expand Down
22 changes: 22 additions & 0 deletions src/internal/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -70,3 +70,25 @@ exports.toYear = function toYear (year) {
}
return year
}

/**
* convert string to Date.
* 2017 : year = 2017, month = 1, day = 1
* '2017-07' : year = 2017, month = 7, day = 1
* '2017-07-03': year = 2017, month = 7, day = 3
* @param {String} str
* @param {Boolean} isUTC - return date in UTC
* @return {Date}
*/
exports.toDate = function toDate (str, isUTC) {
const m = /^(\d{4})(?:-(\d{2})(?:-(\d{2}))?)?.*$/.exec((str || '').toString())
if (m) {
m.shift()
let [year, month, day] = m.map((num) => parseInt(num || 1, 10))
if (isUTC) {
return new Date(Date.UTC(year, month - 1, day))
} else {
return new Date(year, month - 1, day)
}
}
}
Loading

0 comments on commit c306889

Please sign in to comment.