UNPKG

edtf

Version:

Extended Date Time Format (EDTF) / ISO 8601-2 Parser and Library

249 lines (196 loc) 4.87 kB
import assert from './assert.js' import { Bitmask } from './bitmask.js' import { ExtDateTime } from './interface.js' import { pad } from './date.js' const A = new WeakMap() const U = new WeakMap() const V = new WeakMap() const X = new WeakMap() export class Season extends ExtDateTime { constructor(input) { super() let uncertain, approximate, unspecified V.set(this, []) switch (typeof input) { case 'number': this.year = input this.season = arguments[1] || 21 break case 'string': input = Season.parse(input) // eslint-disable-next-line no-fallthrough case 'object': if (Array.isArray(input)) input = { values: input } { assert(input !== null) if (input.type) assert.equal('Season', input.type) assert(input.values) assert.equal(2, input.values.length) this.year = input.values[0] this.season = input.values[1] ;({ unspecified, uncertain, approximate } = input) } break case 'undefined': this.year = new Date().getUTCFullYear() this.season = 21 break default: throw new RangeError('Invalid season value') } this.unspecified = unspecified this.uncertain = uncertain this.approximate = approximate } get year() { return this.values[0] } set year(year) { this.values[0] = Number(year) } get season() { return this.values[1] } set season(season) { this.values[1] = validate(Number(season)) } get values() { return V.get(this) } set uncertain(value) { U.set(this, new Bitmask(value)) } get uncertain() { return U.get(this) } set approximate(value) { A.set(this, new Bitmask(value)) } get approximate() { return A.get(this) } set unspecified(value) { X.set(this, new Bitmask(value)) } get unspecified() { return X.get(this) } next(k = 1) { let { season, year, unspecified, approximate, uncertain } = this switch (true) { case (season >= 21 && season <= 36): [year, season] = inc(year, season, k, season - (season - 21) % 4, 4) break case (season >= 37 && season <= 39): [year, season] = inc(year, season, k, 37, 3) break case (season >= 40 && season <= 41): [year, season] = inc(year, season, k, 40, 2) break default: throw new RangeError(`Cannot compute next/prev for season ${season}`) } return new Season({ values: [year, season], approximate, uncertain, unspecified }) } prev(k = 1) { return this.next(-k) } get min() { // eslint-disable-line complexity switch (this.season) { case 21: case 25: case 32: case 33: case 40: case 37: return ExtDateTime.UTC(this.year, 0) case 22: case 26: case 31: case 34: return ExtDateTime.UTC(this.year, 3) case 23: case 27: case 30: case 35: case 41: return ExtDateTime.UTC(this.year, 6) case 24: case 28: case 29: case 36: return ExtDateTime.UTC(this.year, 9) case 38: return ExtDateTime.UTC(this.year, 4) case 39: return ExtDateTime.UTC(this.year, 8) default: return ExtDateTime.UTC(this.year, 0) } } get max() { // eslint-disable-line complexity let year = this.unspecified.max([pad(this.year)]) switch (this.season) { case 21: case 25: case 32: case 33: return ExtDateTime.UTC(year, 3) - 1 case 22: case 26: case 31: case 34: case 40: return ExtDateTime.UTC(year, 6) - 1 case 23: case 27: case 30: case 35: return ExtDateTime.UTC(year, 9) - 1 case 24: case 28: case 29: case 36: case 41: case 39: return ExtDateTime.UTC(year + 1, 0) - 1 case 37: return ExtDateTime.UTC(year, 5) - 1 case 38: return ExtDateTime.UTC(year, 9) - 1 default: return ExtDateTime.UTC(year + 1, 0) - 1 } } toEDTF() { let sign = (this.year < 0) ? '-' : '' let values = [pad(this.year), String(this.season)] if (this.unspecified.value) return sign + this.unspecified.masks(values).join('-') if (this.uncertain.value) values = this.uncertain.marks(values, '?') if (this.approximate.value) { values = this.approximate.marks(values, '~') .map(value => value.replace(/(~\?)|(\?~)/, '%')) } return sign + values.join('-') } } function validate(season) { if (isNaN(season) || season < 21 || season === Infinity) throw new RangeError(`invalid division of year: ${season}`) return season } function inc(year, season, by, base, size) { const m = (season + by) - base return [ year + Math.floor(m / size), validate(base + (m % size + size) % size) ] }