From a2b58e36ab47d14e082b7f5529f2c6b1d58ba7d1 Mon Sep 17 00:00:00 2001 From: spencer kelly Date: Tue, 22 Mar 2022 18:18:28 -0400 Subject: [PATCH 01/50] add c8 as dependency to gh script --- .github/workflows/coverage.yml | 1 + .github/workflows/release.yml | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index 4d177e053..405ba2c73 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -24,4 +24,5 @@ jobs: ${{ runner.os }}-node- - run: npm ci + - run: npm i --no-save c8 - run: npm run codecov diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 24e80249c..325bf5ffc 100755 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -33,7 +33,7 @@ jobs: run: | npm ci # npm i --no-save ts-node typescript - # npm run plugins:ci + npm run plugins:ci - name: static checks run: | From 337a86b34d6d5dc3deae20967efada1aedffa046 Mon Sep 17 00:00:00 2001 From: spencer kelly Date: Tue, 22 Mar 2022 18:24:27 -0400 Subject: [PATCH 02/50] npm audit fix --- package-lock.json | 103 +++++++++++++++++++++++++++++++++------------- 1 file changed, 74 insertions(+), 29 deletions(-) diff --git a/package-lock.json b/package-lock.json index 426b1b6b8..dad23f16c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "compromise", - "version": "13.11.4-rc6", + "version": "14.0.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "compromise", - "version": "13.11.4-rc6", + "version": "14.0.0", "license": "MIT", "dependencies": { "efrt": "2.5.0", @@ -20,7 +20,6 @@ "codecov": "3.8.3", "eslint": "8.11.0", "eslint-plugin-regexp": "1.6.0", - "minimum-model": "^0.0.1", "nlp-corpus": "^4.2.0", "rollup": "2.70.1", "rollup-plugin-filesize-check": "0.0.1", @@ -30,7 +29,7 @@ "tape": "5.5.2" }, "engines": { - "node": ">=8.0.0" + "node": ">=12.0.0" } }, "node_modules/@babel/code-frame": { @@ -1811,15 +1810,9 @@ } }, "node_modules/minimist": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", - "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", - "dev": true - }, - "node_modules/minimum-model": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/minimum-model/-/minimum-model-0.0.1.tgz", - "integrity": "sha512-p53RYP60oLubk6KTcstBy5yZOgHB9HN8Aq5fNJQ1kvE+GknAkDqrMokMyvXziz1qzCaaBKu95EY+fL9iDMaTfA==", + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", + "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==", "dev": true }, "node_modules/ms": { @@ -1841,12 +1834,23 @@ "dev": true }, "node_modules/node-fetch": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz", - "integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==", + "version": "2.6.7", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", + "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", "dev": true, + "dependencies": { + "whatwg-url": "^5.0.0" + }, "engines": { "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } } }, "node_modules/normalize-path": { @@ -2567,6 +2571,12 @@ "node": ">=8.0" } }, + "node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o=", + "dev": true + }, "node_modules/trim": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/trim/-/trim-1.0.1.tgz", @@ -2651,6 +2661,22 @@ "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==", "dev": true }, + "node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE=", + "dev": true + }, + "node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha1-lmRU6HZUYuN2RNNib2dCzotwll0=", + "dev": true, + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -4114,15 +4140,9 @@ } }, "minimist": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", - "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", - "dev": true - }, - "minimum-model": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/minimum-model/-/minimum-model-0.0.1.tgz", - "integrity": "sha512-p53RYP60oLubk6KTcstBy5yZOgHB9HN8Aq5fNJQ1kvE+GknAkDqrMokMyvXziz1qzCaaBKu95EY+fL9iDMaTfA==", + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", + "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==", "dev": true }, "ms": { @@ -4144,10 +4164,13 @@ "dev": true }, "node-fetch": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz", - "integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==", - "dev": true + "version": "2.6.7", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", + "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", + "dev": true, + "requires": { + "whatwg-url": "^5.0.0" + } }, "normalize-path": { "version": "3.0.0", @@ -4739,6 +4762,12 @@ "is-number": "^7.0.0" } }, + "tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o=", + "dev": true + }, "trim": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/trim/-/trim-1.0.1.tgz", @@ -4810,6 +4839,22 @@ "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==", "dev": true }, + "webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE=", + "dev": true + }, + "whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha1-lmRU6HZUYuN2RNNib2dCzotwll0=", + "dev": true, + "requires": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, "which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", From 4cfdcb1cdb7dc1a2379eeafcb3b893e2cc76be4a Mon Sep 17 00:00:00 2001 From: spencer kelly Date: Wed, 23 Mar 2022 11:33:40 -0400 Subject: [PATCH 03/50] normalize runtime issue --- scratch.js | 11 ++--------- src/3-three/normalize/methods.js | 6 ++++-- tests/three/normalize/normalize-custom.test.js | 7 +++++++ 3 files changed, 13 insertions(+), 11 deletions(-) diff --git a/scratch.js b/scratch.js index f3a7da3ce..cddd3a578 100644 --- a/scratch.js +++ b/scratch.js @@ -12,15 +12,8 @@ import nlp from './src/three.js' // m.match('three').remove() // m.debug() -let doc = nlp("$4.09CAD") -console.log(doc.money().json()) -/*[{ - text: 'four out of five', - terms: [ [Object], [Object], [Object], [Object] ], - fraction: { numerator: 4, denominator: 5, decimal: 0.8 } - } -]*/ - +let doc = nlp('simon says shoot the puck') +doc.none().normalize().debug() // let doc = nlp("Wayne's World, party time") // let str = doc.people().normalize('heavy').text() diff --git a/src/3-three/normalize/methods.js b/src/3-three/normalize/methods.js index af101fb65..47b9568a3 100644 --- a/src/3-three/normalize/methods.js +++ b/src/3-three/normalize/methods.js @@ -45,8 +45,10 @@ export default { // trim end let docs = doc.docs let terms = docs[docs.length - 1] - let lastTerm = terms[terms.length - 1] - lastTerm.post = lastTerm.post.replace(/ /g, '') + if (terms && terms.length > 0) { + let lastTerm = terms[terms.length - 1] + lastTerm.post = lastTerm.post.replace(/ /g, '') + } }, // ====== subsets === diff --git a/tests/three/normalize/normalize-custom.test.js b/tests/three/normalize/normalize-custom.test.js index 0a7d40e72..7f14ad030 100644 --- a/tests/three/normalize/normalize-custom.test.js +++ b/tests/three/normalize/normalize-custom.test.js @@ -43,3 +43,10 @@ test('normalize contractions', function (t) { t.equal(doc.text(), ` it is coöl, (i think) . He is cool; i said .`, here + 'normalize-contractions') t.end() }) + +test('normalize empty', function (t) { + let doc = nlp('').none() + doc.normalize() + t.equal(doc.text(), ``, here + 'normalize-nothing') + t.end() +}) From 2ba0f915339eb9582f91560e5ff95c57bdd1ceee Mon Sep 17 00:00:00 2001 From: spencer kelly Date: Wed, 23 Mar 2022 11:36:45 -0400 Subject: [PATCH 04/50] fix possessive plus pronoun --- scratch.js | 10 ++-------- .../postTagger/model/person/person-phrase.js | 18 +++--------------- 2 files changed, 5 insertions(+), 23 deletions(-) diff --git a/scratch.js b/scratch.js index cddd3a578..a632797fe 100644 --- a/scratch.js +++ b/scratch.js @@ -3,7 +3,7 @@ import nlp from './src/three.js' // import plg from './plugins/speech/src/plugin.js' // nlp.plugin(plg) -// nlp.verbose('tagger') +nlp.verbose('tagger') // nlp.verbose('chunker') // weird remove issue @@ -12,11 +12,5 @@ import nlp from './src/three.js' // m.match('three').remove() // m.debug() -let doc = nlp('simon says shoot the puck') -doc.none().normalize().debug() -// let doc = nlp("Wayne's World, party time") -// let str = doc.people().normalize('heavy').text() -// console.log(str) -// "wayne" -// console.log(doc.text()) \ No newline at end of file +let doc = nlp("Like the time I caught the ferry ").debug() \ No newline at end of file diff --git a/src/2-two/postTagger/model/person/person-phrase.js b/src/2-two/postTagger/model/person/person-phrase.js index 24e7bddf6..694149cd2 100644 --- a/src/2-two/postTagger/model/person/person-phrase.js +++ b/src/2-two/postTagger/model/person/person-phrase.js @@ -68,21 +68,9 @@ export default [ //Cliff Clavin { match: '%Person|Noun% #ProperNoun', tag: 'Person', reason: 'switch-person', safe: true }, // john keith jones - { - match: '#Person [#ProperNoun #ProperNoun]', - group: 0, - tag: 'Person', - reason: 'three-name-person', - safe: true, - }, + { match: '#Person [#ProperNoun #ProperNoun]', group: 0, tag: 'Person', ifNo: '#Possessive', reason: 'three-name-person', safe: true }, //John Foo - { - match: '#FirstName #Acronym? [#ProperNoun]', - group: 0, - tag: 'LastName', - reason: 'firstname-titlecase', - // safe: true, - }, + { match: '#FirstName #Acronym? [#ProperNoun]', group: 0, tag: 'LastName', ifNo: '#Possessive', reason: 'firstname-titlecase' }, // john stewart { match: '#FirstName [#FirstName]', group: 0, tag: 'LastName', reason: 'firstname-firstname' }, //Joe K. Sombrero @@ -92,7 +80,7 @@ export default [ //Joe springer sr { match: '#ProperNoun [#Honorific]', group: 0, tag: 'Person', reason: 'last-sr' }, // dr john foobar - { match: '#Honorific #FirstName [#Singular]', group: 0, tag: 'LastName', reason: 'dr-john-foo', safe: true }, + { match: '#Honorific #FirstName [#Singular]', group: 0, tag: 'LastName', ifNo: '#Possessive', reason: 'dr-john-foo', safe: true }, //his-excellency { match: '[(his|her) (majesty|honour|worship|excellency|honorable)] #Person', From 1872b59cebf4ae6660f76db91086921e0fcc4839 Mon Sep 17 00:00:00 2001 From: spencer kelly Date: Wed, 23 Mar 2022 11:48:23 -0400 Subject: [PATCH 05/50] tagger fixes --- scratch.js | 12 ++++++------ src/2-two/postTagger/model/_misc.js | 2 +- src/2-two/postTagger/model/conjunctions.js | 2 ++ src/2-two/postTagger/model/verbs/imperative.js | 2 +- 4 files changed, 10 insertions(+), 8 deletions(-) diff --git a/scratch.js b/scratch.js index a632797fe..166d80a2c 100644 --- a/scratch.js +++ b/scratch.js @@ -3,14 +3,14 @@ import nlp from './src/three.js' // import plg from './plugins/speech/src/plugin.js' // nlp.plugin(plg) -nlp.verbose('tagger') +// nlp.verbose('tagger') // nlp.verbose('chunker') // weird remove issue -// let m = nlp('one two three. foo.', { two: 'Infinitive' }) -// m = m.splitOn('two').eq(0).tag('Foo') -// m.match('three').remove() -// m.debug() +let m = nlp('one two three. foo.', { two: 'Infinitive' }) +m = m.splitOn('two').eq(0).tag('Foo') +m.match('three').remove() +m.debug() -let doc = nlp("Like the time I caught the ferry ").debug() \ No newline at end of file +// let doc = nlp("Now, to take the ferry cost a nickel.").debug() \ No newline at end of file diff --git a/src/2-two/postTagger/model/_misc.js b/src/2-two/postTagger/model/_misc.js index 211ecdea7..30bdfbc66 100644 --- a/src/2-two/postTagger/model/_misc.js +++ b/src/2-two/postTagger/model/_misc.js @@ -13,7 +13,7 @@ let matches = [ //swear-words as non-expression POS { match: 'holy (shit|fuck|hell)', tag: 'Expression', reason: 'swears-expression' }, // well.. - { match: '^(well|so|okay)', tag: 'Expression', reason: 'well-' }, + { match: '^(well|so|okay|now)', tag: 'Expression', reason: 'well-' }, // some sort of { match: 'some sort of', tag: 'Adjective Noun Conjunction', reason: 'some-sort-of' }, // of some sort diff --git a/src/2-two/postTagger/model/conjunctions.js b/src/2-two/postTagger/model/conjunctions.js index 7f8dec84c..e0ceefe21 100644 --- a/src/2-two/postTagger/model/conjunctions.js +++ b/src/2-two/postTagger/model/conjunctions.js @@ -27,6 +27,8 @@ export default [ { match: '#Copula just [like]', group: 0, tag: 'Preposition', reason: 'like-preposition' }, //folks like her { match: '#Noun [like] #Noun', group: 0, tag: 'Preposition', reason: 'noun-like' }, + //like the time + { match: '[like] #Determiner', group: 0, tag: 'Preposition', reason: 'like-the' }, diff --git a/src/2-two/postTagger/model/verbs/imperative.js b/src/2-two/postTagger/model/verbs/imperative.js index 366ca8f26..aa767753e 100644 --- a/src/2-two/postTagger/model/verbs/imperative.js +++ b/src/2-two/postTagger/model/verbs/imperative.js @@ -18,7 +18,7 @@ export default [ // turn down the noise { match: '^[#Infinitive] (up|down|over) #Determiner', group: 0, tag: 'Imperative', reason: 'turn-down' }, // eat my shorts - { match: '^[#Infinitive] (your|my|the|some|a|an)', group: 0, tag: 'Imperative', reason: 'eat-my-shorts' }, + { match: '^[#Infinitive] (your|my|the|some|a|an)', group: 0, ifNo: 'like', tag: 'Imperative', reason: 'eat-my-shorts' }, // tell him the story { match: '^[#Infinitive] (him|her|it|us|me)', group: 0, tag: 'Imperative', reason: 'tell-him' }, // one-word imperatives From dd2523542ae26ce47e4a6e6d5bf12b05c13a9464 Mon Sep 17 00:00:00 2001 From: spencer kelly Date: Wed, 23 Mar 2022 13:06:36 -0400 Subject: [PATCH 06/50] add toUppercase term method --- scratch.js | 11 ++++------- src/1-one/match/methods/termMethods.js | 1 + 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/scratch.js b/scratch.js index 166d80a2c..860d1fd61 100644 --- a/scratch.js +++ b/scratch.js @@ -7,10 +7,7 @@ import nlp from './src/three.js' // nlp.verbose('chunker') // weird remove issue -let m = nlp('one two three. foo.', { two: 'Infinitive' }) -m = m.splitOn('two').eq(0).tag('Foo') -m.match('three').remove() -m.debug() - - -// let doc = nlp("Now, to take the ferry cost a nickel.").debug() \ No newline at end of file +// let m = nlp('one two three. foo.', { two: 'Infinitive' }) +// m = m.splitOn('two').eq(0).tag('Foo') +// m.match('three').remove() +// m.debug() \ No newline at end of file diff --git a/src/1-one/match/methods/termMethods.js b/src/1-one/match/methods/termMethods.js index c32137e91..05e2b6fec 100644 --- a/src/1-one/match/methods/termMethods.js +++ b/src/1-one/match/methods/termMethods.js @@ -40,6 +40,7 @@ const methods = { isAcronym: term => term.tags.has('Acronym'), isKnown: term => term.tags.size > 0, isTitleCase: term => /^[A-Z][a-z'\u00C0-\u00FF]/.test(term.text), //|| /^[A-Z]$/.test(term.text) + isUpperCase: term => /^[A-Z]+$/.test(term.text), } // aliases methods.hasQuotation = methods.hasQuote From f7266baf8aa14cd682467c97903fd771d44a57b4 Mon Sep 17 00:00:00 2001 From: spencer kelly Date: Wed, 23 Mar 2022 13:07:42 -0400 Subject: [PATCH 07/50] test for term method --- scratch.js | 19 ++++++++++++++++++- tests/one/match/match-method.test.js | 4 ++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/scratch.js b/scratch.js index 860d1fd61..3179b865a 100644 --- a/scratch.js +++ b/scratch.js @@ -10,4 +10,21 @@ import nlp from './src/three.js' // let m = nlp('one two three. foo.', { two: 'Infinitive' }) // m = m.splitOn('two').eq(0).tag('Foo') // m.match('three').remove() -// m.debug() \ No newline at end of file +// m.debug() + + +// nlp('set the SCE to AUX.').match('@isUpperCase').debug() + +// let doc = nlp("Now, to take the ferry cost a nickel.").debug() + +//get filesize +// const bytes = Buffer.byteLength(txt) +// const bytes = (new TextEncoder().encode(txt)).length +// const size = Math.ceil(bytes / 1024) +// console.log(`${size}kb`) +// let doc = nlp("I'm looking for Amanda Hugginkiss") +// // cache the root form of each word +// doc.compute('root') +// // use a 'root' lookup: +// let m = doc.match('{look}') +// m.debug() diff --git a/tests/one/match/match-method.test.js b/tests/one/match/match-method.test.js index 5a0914748..91a388930 100644 --- a/tests/one/match/match-method.test.js +++ b/tests/one/match/match-method.test.js @@ -22,5 +22,9 @@ test('match @functions', function (t) { m = doc.match('(foo|!@hasComma)') t.equal(m.text(), 'i am much better and faster', here + 'negative in optional function') + + m = nlp('set the SCE to AUX.').match('@isUpperCase') + t.equal(m.length, 2, here + 'two uppercase') + t.end() }) From 7fa8b72b5778b385799a9720bf13884dac217ced Mon Sep 17 00:00:00 2001 From: spencer kelly Date: Wed, 23 Mar 2022 14:51:37 -0400 Subject: [PATCH 08/50] splitup noun chunks --- scratch.js | 19 +++---------------- src/3-three/chunker/api/chunks.js | 31 +++++++++++++++++++------------ 2 files changed, 22 insertions(+), 28 deletions(-) diff --git a/scratch.js b/scratch.js index 3179b865a..0b4e676b4 100644 --- a/scratch.js +++ b/scratch.js @@ -12,19 +12,6 @@ import nlp from './src/three.js' // m.match('three').remove() // m.debug() - -// nlp('set the SCE to AUX.').match('@isUpperCase').debug() - -// let doc = nlp("Now, to take the ferry cost a nickel.").debug() - -//get filesize -// const bytes = Buffer.byteLength(txt) -// const bytes = (new TextEncoder().encode(txt)).length -// const size = Math.ceil(bytes / 1024) -// console.log(`${size}kb`) -// let doc = nlp("I'm looking for Amanda Hugginkiss") -// // cache the root form of each word -// doc.compute('root') -// // use a 'root' lookup: -// let m = doc.match('{look}') -// m.debug() +let doc = nlp(`This week I can't make it`) +doc.chunks().debug('chunks') +// doc.nouns().debug('chunks') diff --git a/src/3-three/chunker/api/chunks.js b/src/3-three/chunker/api/chunks.js index 8c8ce8eef..cf3102452 100644 --- a/src/3-three/chunker/api/chunks.js +++ b/src/3-three/chunker/api/chunks.js @@ -1,24 +1,31 @@ const chunks = function () { let carry = [] - let roll = null - let same = null + let ptr = null + let current = null this.docs.forEach(terms => { terms.forEach(term => { // start a new chunk - if (term.chunk !== same) { - if (roll) { - roll[2] = term.index[1] - carry.push(roll) + if (term.chunk !== current) { + if (ptr) { + ptr[2] = term.index[1] + carry.push(ptr) } - same = term.chunk - roll = [term.index[0], term.index[1]] + current = term.chunk + ptr = [term.index[0], term.index[1]] } }) }) - if (roll) { - carry.push(roll) + if (ptr) { + carry.push(ptr) } - - return this.update(carry) + let parts = this.update(carry) + // split up verb-phrases, and noun-phrases + parts = parts.map(c => { + if (c.has('')) { + return c.nouns() + } + return c + }) + return parts } export default chunks From c590f665632e6882b8287bb4f4141ec3703f4b1d Mon Sep 17 00:00:00 2001 From: spencer kelly Date: Wed, 23 Mar 2022 17:05:15 -0400 Subject: [PATCH 09/50] add new graph to readme --- README.md | 2 +- scratch.js | 26 +++++++++++++++++++--- scripts/perf/novel.js | 4 ++-- src/2-two/postTagger/model/conjunctions.js | 7 +++--- src/3-three/chunker/api/chunks.js | 5 +++++ 5 files changed, 35 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index bea928cdf..e84f4944d 100644 --- a/README.md +++ b/README.md @@ -281,7 +281,7 @@ it's pretty fast. It can run on keypress: diff --git a/scratch.js b/scratch.js index 0b4e676b4..defa5f2fa 100644 --- a/scratch.js +++ b/scratch.js @@ -1,5 +1,5 @@ /* eslint-disable no-console, no-unused-vars */ -import nlp from './src/three.js' +import nlp from './src/one.js' // import plg from './plugins/speech/src/plugin.js' // nlp.plugin(plg) @@ -12,6 +12,26 @@ import nlp from './src/three.js' // m.match('three').remove() // m.debug() -let doc = nlp(`This week I can't make it`) -doc.chunks().debug('chunks') +// let doc = nlp('He will learn to really like this') +// doc.chunks().debug('chunks') // doc.nouns().debug('chunks') + +import wtf from '/Users/spencer/mountain/wtf_wikipedia/builds/wtf_wikipedia.mjs' + +wtf.fetch('Sea breeze').then(doc => { + let txt = doc.text() + let begin = new Date() + nlp(txt) + // + let end = new Date() + console.log((end.getTime() - begin.getTime())) + +}) + +// let doc = nlp("looked for Amanda Hugginkiss") +// // cache the root form of each word +// doc.compute('cache') +// // use a 'soft' lookup: +// let m = doc.match('{look}') +// // print our result: +// m.debug() \ No newline at end of file diff --git a/scripts/perf/novel.js b/scripts/perf/novel.js index f132dbf29..000213404 100644 --- a/scripts/perf/novel.js +++ b/scripts/perf/novel.js @@ -10,7 +10,7 @@ import fs from 'fs' // const txt = await wtf.fetch('Julius and Ethel Rosenberg').then(d => d.text()) // const txt = corpus.all().slice(0, 10000).join('\n') let txt = fs.readFileSync('/Users/spencer/data/infinite-jest/infinite-jest.txt').toString() - txt = txt.substring(39390, 80000) + // txt = txt.substring(39390, 80000) //get filesize const bytes = Buffer.byteLength(txt) const size = Math.ceil(bytes / 1024) @@ -22,7 +22,7 @@ import fs from 'fs' let begin = new Date() let doc = nlp(txt) - doc.sentences().forEach(s => { + doc.chunks().forEach(s => { if (s.verbs().length === 2) { console.log(s.text()) } diff --git a/src/2-two/postTagger/model/conjunctions.js b/src/2-two/postTagger/model/conjunctions.js index e0ceefe21..dd1b041da 100644 --- a/src/2-two/postTagger/model/conjunctions.js +++ b/src/2-two/postTagger/model/conjunctions.js @@ -24,11 +24,12 @@ export default [ { match: '#Verb #Adverb? #Noun [(that|which)]', group: 0, tag: 'Preposition', reason: 'that-prep' }, //work, which has been done. { match: '@hasComma [which] (#Pronoun|#Verb)', group: 0, tag: 'Preposition', reason: 'which-copula' }, - { match: '#Copula just [like]', group: 0, tag: 'Preposition', reason: 'like-preposition' }, //folks like her - { match: '#Noun [like] #Noun', group: 0, tag: 'Preposition', reason: 'noun-like' }, + { match: '#Plural [like] #Noun', group: 0, tag: 'Preposition', reason: 'noun-like' }, //like the time - { match: '[like] #Determiner', group: 0, tag: 'Preposition', reason: 'like-the' }, + { match: '^[like] #Determiner', group: 0, tag: 'Preposition', reason: 'like-the' }, + // really like + { match: '#Adverb [like]', group: 0, tag: 'Verb', reason: 'really-like' }, diff --git a/src/3-three/chunker/api/chunks.js b/src/3-three/chunker/api/chunks.js index cf3102452..8e688bd41 100644 --- a/src/3-three/chunker/api/chunks.js +++ b/src/3-three/chunker/api/chunks.js @@ -24,6 +24,11 @@ const chunks = function () { if (c.has('')) { return c.nouns() } + // if (c.has('')) { + // if (c.verbs().length > 1) { + // console.log(c.text()) + // } + // } return c }) return parts From f0514241ef73a1bcc08acbfe5be2dabe2f9ca037 Mon Sep 17 00:00:00 2001 From: spencer kelly Date: Thu, 24 Mar 2022 14:51:31 -0400 Subject: [PATCH 10/50] param sig for fuzzy options to before/after methods --- README.md | 2 +- scratch.js | 70 ++++++++++++++++++++----------- src/1-one/match/api/lookaround.js | 12 +++--- types/view/one.ts | 30 ++++++------- 4 files changed, 68 insertions(+), 46 deletions(-) diff --git a/README.md b/README.md index e84f4944d..fb56e2d7f 100644 --- a/README.md +++ b/README.md @@ -817,7 +817,7 @@ nlp.extend({ | [Named-Entities](https://observablehq.com/@spencermountain/compromise-topics) | [Utils](https://observablehq.com/@spencermountain/compromise-utils) | [Penn-tags](https://observablehq.com/@spencermountain/compromise-penn-tags) | | [Whitespace](https://observablehq.com/@spencermountain/compromise-whitespace) | [Verbs](https://observablehq.com/@spencermountain/verbs) | [Typeahead](https://observablehq.com/@spencermountain/compromise/compromise-typeahead) | | [World data](https://observablehq.com/@spencermountain/compromise-world) | [Normalization](https://observablehq.com/@spencermountain/compromise-normalization) | | -| [Fuzzy-matching](https://observablehq.com/@spencermountain/compromise-fuzzy-matching) | [Typescript](https://observablehq.com/@spencermountain/compromise-typescript) | | +| [Fuzzy-matching](https://observablehq.com/@spencermountain/compromise-fuzzy-matching) | [Typescript](https://observablehq.com/@spencermountain/compromise-typescript) | [Mutation](https://observablehq.com/@spencermountain/compromise-mutation) |
diff --git a/scratch.js b/scratch.js index defa5f2fa..420bd1f8c 100644 --- a/scratch.js +++ b/scratch.js @@ -1,5 +1,5 @@ /* eslint-disable no-console, no-unused-vars */ -import nlp from './src/one.js' +import nlp from './src/three.js' // import plg from './plugins/speech/src/plugin.js' // nlp.plugin(plg) @@ -12,26 +12,48 @@ import nlp from './src/one.js' // m.match('three').remove() // m.debug() -// let doc = nlp('He will learn to really like this') -// doc.chunks().debug('chunks') -// doc.nouns().debug('chunks') - -import wtf from '/Users/spencer/mountain/wtf_wikipedia/builds/wtf_wikipedia.mjs' - -wtf.fetch('Sea breeze').then(doc => { - let txt = doc.text() - let begin = new Date() - nlp(txt) - // - let end = new Date() - console.log((end.getTime() - begin.getTime())) - -}) - -// let doc = nlp("looked for Amanda Hugginkiss") -// // cache the root form of each word -// doc.compute('cache') -// // use a 'soft' lookup: -// let m = doc.match('{look}') -// // print our result: -// m.debug() \ No newline at end of file + +// let doc = nlp('January the 12th of 2022 at 3pm') +// let tmp = doc.clone() +// tmp.remove('(the|of|at)') +// tmp.numbers().toCardinal() +// // 'january 12 2022' +// let tmpYear = tmp.match('#Month #Value [#Value]', 0) +// // get the match in the original document +// let year = doc.match(tmpYear) +// console.log(tmpYear) +// year.debug() + + + +// let doc = nlp('one two three') +// let tmp = doc.clone() +// // mutate the original +// doc.remove('two') +// // return a partial +// return doc.match(tmp).text() + + +// let doc = nlp(`Springfield, springfield! it's a hell of a town.`) +// let terms = doc.terms() //split by term +// //remove duplicate matches +// doc = doc.unique() +// return doc.out('array') + + +// let doc = nlp(`i have two questions for Homer - 'Why lie?' and 'Lies, why?'`) +// doc.quotations().splitOn().out('array') + +// let doc = nlp('the sky is dark') +// return doc.adjectives().json()[0] + +nlp('the so-called group of seven').normalize({ numbers: true }).out() + +nlp(`i saw the game that the Toronto Maple Leafs won`).verbs().isSingular().debug() + +nlp("you and your whole lousy operation stink").verbs().adverbs().debug() + + +let doc = nlp(`wayne's World, party-time, excellent!! 🎸`) +doc.remove('(#Emoticon|#Emoji)') +console.log(doc.text()) \ No newline at end of file diff --git a/src/1-one/match/api/lookaround.js b/src/1-one/match/api/lookaround.js index a6a1bf09f..687d5a432 100644 --- a/src/1-one/match/api/lookaround.js +++ b/src/1-one/match/api/lookaround.js @@ -1,5 +1,5 @@ -const before = function (regs, group) { +const before = function (regs, group, opts) { const { indexN } = this.methods.one.pointer let pre = [] let byN = indexN(this.fullPointer) @@ -14,10 +14,10 @@ const before = function (regs, group) { if (!regs) { return preWords } - return preWords.match(regs, group) + return preWords.match(regs, group, opts) } -const after = function (regs, group) { +const after = function (regs, group, opts) { const { indexN } = this.methods.one.pointer let post = [] let byN = indexN(this.fullPointer) @@ -34,7 +34,7 @@ const after = function (regs, group) { if (!regs) { return postWords } - return postWords.match(regs, group) + return postWords.match(regs, group, opts) } const growLeft = function (regs, group, opts) { @@ -67,8 +67,8 @@ const growRight = function (regs, group, opts) { return this.update(ptrs) } -const grow = function (regs, group) { - return this.growRight(regs, group).growLeft(regs, group) +const grow = function (regs, group, opts) { + return this.growRight(regs, group, opts).growLeft(regs, group, opts) } export default { before, after, growLeft, growRight, grow } diff --git a/types/view/one.ts b/types/view/one.ts index 30cf1141f..79605e11a 100644 --- a/types/view/one.ts +++ b/types/view/one.ts @@ -81,33 +81,33 @@ class View { // Match /** return matching patterns in this doc */ - match: (match: string | View, options?: any, group?: string | number) => View + match: (match: string | View, group?: string | number, options?: object) => View /** return only the first match */ - matchOne: (match: string | View, options?: any) => View + matchOne: (match: string | View, group?: string | number, options?: object) => View /** Return a boolean if this match exists */ - has: (match: string | View, options?: any) => boolean + has: (match: string | View, group?: string | number, options?: object) => boolean /** return each current phrase, only if it contains this match */ - if: (match: string | View, options?: any) => View + if: (match: string | View, group?: string | number, options?: object) => View /** Filter-out any current phrases that have this match */ - ifNo: (match: string | View, options?: any) => View + ifNo: (match: string | View, group?: string | number, options?: object) => View /** return the terms before each match */ - before: (match: string | View, options?: any) => View + before: (match: string | View, group?: string | number, options?: object) => View /** return the terms after each match */ - after: (match: string | View, options?: any) => View + after: (match: string | View, group?: string | number, options?: object) => View /** add any immediately-preceding matches to the view*/ - growLeft: (match: string | View, options?: any) => View + growLeft: (match: string | View, group?: string | number, options?: object) => View /** add any immediately-following matches to the view*/ - growRight: (match: string | View, options?: any) => View + growRight: (match: string | View, group?: string | number, options?: object) => View /** expand the view with any left-or-right matches*/ - grow: (match: string | View, options?: any) => View + grow: (match: string | View, group?: string | number, options?: object) => View /** .split() [alias] */ - splitOn: (match?: string) => View + splitOn: (match?: string, group?: string | number) => View /** separate everything after the match as a new phrase */ - splitBefore: (match?: string) => View + splitBefore: (match?: string, group?: string | number) => View /** separate everything before the word, as a new phrase */ - splitAfter: (match?: string) => View + splitAfter: (match?: string, group?: string | number) => View // Case /** turn every letter of every term to lower-cse */ @@ -129,9 +129,9 @@ class View { /** fully remove these terms from the document */ remove: (match: string | View) => View /** search and replace match with new content */ - replace: (match: string | View, text?: string | Function, keepTags?: boolean | object, keepCase?: boolean) => View + replace: (from: string | View, to?: string | Function, keep?: object) => View /** substitute-in new content */ - replaceWith: (text: string | Function, keepTags?: boolean | object, keepCase?: boolean) => View + replaceWith: (to: string | Function, keep?: object) => View /** remove any duplicate matches */ unique: () => View From 88c967c0d5e8e3b8888e04df015969a2f2fb45de Mon Sep 17 00:00:00 2001 From: spencer kelly Date: Thu, 24 Mar 2022 16:13:29 -0400 Subject: [PATCH 11/50] fix date plugin browser export format --- plugins/dates/builds/compromise-dates.cjs | 5067 ++++++++++++++++- plugins/dates/builds/compromise-dates.min.js | 2 +- plugins/dates/builds/compromise-dates.mjs | 2 +- plugins/dates/demo/index.html | 29 + plugins/dates/package-lock.json | 71 +- plugins/dates/rollup.config.js | 7 +- plugins/dates/scratch.js | 1 + .../api/parse/one/01-tokenize/_timezones.js | 2 +- 8 files changed, 4938 insertions(+), 243 deletions(-) create mode 100644 plugins/dates/demo/index.html diff --git a/plugins/dates/builds/compromise-dates.cjs b/plugins/dates/builds/compromise-dates.cjs index bec31571a..e75be742c 100644 --- a/plugins/dates/builds/compromise-dates.cjs +++ b/plugins/dates/builds/compromise-dates.cjs @@ -1,13 +1,8 @@ (function (global, factory) { - typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('spacetime'), require('spacetime-holiday')) : - typeof define === 'function' && define.amd ? define(['spacetime', 'spacetime-holiday'], factory) : - (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.compromiseDates = factory(global.spacetime, global.spacetimeHoliday)); -})(this, (function (spacetime, spacetimeHoliday) { 'use strict'; - - function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; } - - var spacetime__default = /*#__PURE__*/_interopDefaultLegacy(spacetime); - var spacetimeHoliday__default = /*#__PURE__*/_interopDefaultLegacy(spacetimeHoliday); + typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : + typeof define === 'function' && define.amd ? define(factory) : + (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.compromiseDates = factory()); +})(this, (function () { 'use strict'; // chop things up into bite-size pieces const split = function (dates) { @@ -82,6 +77,7 @@ dates = dates.not('^and'); return dates }; + var split$1 = split; const findDate = function (doc) { // if (doc.world.isVerbose() === 'date') { @@ -126,13 +122,14 @@ // 'one saturday' dates = dates.notIf('^one (#WeekDay|#Month)$'); // tokenize the dates - dates = split(dates); + dates = split$1(dates); // $5 an hour dates = dates.notIf('(#Money|#Percentage)'); dates = dates.notIf('^per #Duration'); return dates }; + var find$1 = findDate; const knownUnits = { second: true, @@ -147,123 +144,4189 @@ year: true, }; - const aliases = { - wk: 'week', - min: 'minute', - sec: 'second', - weekend: 'week', //for now... + const aliases$2 = { + wk: 'week', + min: 'minute', + sec: 'second', + weekend: 'week', //for now... + }; + + const parseUnit = function (m) { + let unit = m.match('#Duration').text('normal'); + unit = unit.replace(/s$/, ''); + // support shorthands like 'min' + if (aliases$2.hasOwnProperty(unit)) { + unit = aliases$2[unit]; + } + return unit + }; + + //turn '5 weeks before' to {weeks:5} + const parseShift = function (doc) { + let result = {}; + let m = doc.none(); + let shift = doc.match('#DateShift+'); + if (shift.found === false) { + return { res: result, m } + } + + // '5 weeks' + shift.match('#Cardinal #Duration').forEach((ts) => { + let num = ts.match('#Cardinal').numbers().get()[0]; + if (num && typeof num === 'number') { + let unit = parseUnit(ts); + if (knownUnits[unit] === true) { + result[unit] = num; + } + } + }); + //is it 2 weeks ago? → -2 + if (shift.has('(before|ago|hence|back)$') === true) { + Object.keys(result).forEach((k) => (result[k] *= -1)); + } + m = shift.match('#Cardinal #Duration'); + shift = shift.not(m); + + // supoprt '1 day after tomorrow' + m = shift.match('[#Duration] [(after|before)]'); + if (m.found) { + let unit = m.groups('unit').text('reduced'); + // unit = unit.replace(/s$/, '') + let dir = m.groups('dir').text('reduced'); + if (dir === 'after') { + result[unit] = 1; + } else if (dir === 'before') { + result[unit] = -1; + } + } + + // in half an hour + m = shift.match('half (a|an) [#Duration]', 0); + if (m.found) { + let unit = parseUnit(m); + result[unit] = 0.5; + } + // finally, remove it from our text + m = doc.match('#DateShift+'); + return { result, m } + }; + var doShift = parseShift; + + /* + a 'counter' is a Unit determined after a point + * first hour of x + * 7th week in x + * last year in x + * + unlike a shift, like "2 weeks after x" + */ + const oneBased = { + minute: true, + }; + + const getCounter = function (doc) { + // 7th week of + let m = doc.match('[#Value] [#Duration+] (of|in)'); + if (m.found) { + let obj = m.groups(); + let num = obj.num.numbers().get()[0]; + let unit = obj.unit.text('reduced'); + let result = { + unit: unit, + num: Number(num) || 0, + }; + // 0-based or 1-based units + if (!oneBased[unit]) { + result.num -= 1; + } + return { result, m } + } + // first week of + m = doc.match('[(first|initial|last|final)] [#Duration+] (of|in)'); + if (m.found) { + let obj = m.groups(); + let dir = obj.dir.text('reduced'); + let unit = obj.unit.text('reduced'); + if (dir === 'initial') { + dir = 'first'; + } + if (dir === 'final') { + dir = 'last'; + } + let result = { + unit: unit, + dir: dir, + }; + return { result, m } + } + + return { result: null, m: doc.none() } + }; + var doCounter = getCounter; + + const MSEC_IN_HOUR = 60 * 60 * 1000; + + //convert our local date syntax a javascript UTC date + const toUtc = (dstChange, offset, year) => { + const [month, rest] = dstChange.split('/'); + const [day, hour] = rest.split(':'); + return Date.UTC(year, month - 1, day, hour) - offset * MSEC_IN_HOUR + }; + + // compare epoch with dst change events (in utc) + const inSummerTime = (epoch, start, end, summerOffset, winterOffset) => { + const year = new Date(epoch).getUTCFullYear(); + const startUtc = toUtc(start, winterOffset, year); + const endUtc = toUtc(end, summerOffset, year); + // simple number comparison now + return epoch >= startUtc && epoch < endUtc + }; + + var inSummerTime$1 = inSummerTime; + + // this method avoids having to do a full dst-calculation on every operation + // it reproduces some things in ./index.js, but speeds up spacetime considerably + const quickOffset = s => { + let zones = s.timezones; + let obj = zones[s.tz]; + if (obj === undefined) { + console.warn("Warning: couldn't find timezone " + s.tz); + return 0 + } + if (obj.dst === undefined) { + return obj.offset + } + + //get our two possible offsets + let jul = obj.offset; + let dec = obj.offset + 1; // assume it's the same for now + if (obj.hem === 'n') { + dec = jul - 1; + } + let split = obj.dst.split('->'); + let inSummer = inSummerTime$1(s.epoch, split[0], split[1], jul, dec); + if (inSummer === true) { + return jul + } + return dec + }; + var quickOffset$1 = quickOffset; + + var data = { + "9|s": "2/dili,2/jayapura", + "9|n": "2/chita,2/khandyga,2/pyongyang,2/seoul,2/tokyo,11/palau,japan,rok", + "9.5|s|04/03:03->10/02:02": "4/adelaide,4/broken_hill,4/south,4/yancowinna", + "9.5|s": "4/darwin,4/north", + "8|s|03/08:01->10/04:00": "12/casey", + "8|s": "2/kuala_lumpur,2/makassar,2/singapore,4/perth,2/ujung_pandang,4/west,singapore", + "8|n": "2/brunei,2/choibalsan,2/hong_kong,2/irkutsk,2/kuching,2/macau,2/manila,2/shanghai,2/taipei,2/ulaanbaatar,2/chongqing,2/chungking,2/harbin,2/macao,2/ulan_bator,hongkong,prc,roc", + "8.75|s": "4/eucla", + "7|s": "12/davis,2/jakarta,9/christmas", + "7|n": "2/bangkok,2/barnaul,2/hovd,2/krasnoyarsk,2/novokuznetsk,2/novosibirsk,2/phnom_penh,2/pontianak,2/ho_chi_minh,2/tomsk,2/vientiane,2/saigon", + "6|s": "12/vostok", + "6|n": "2/almaty,2/bishkek,2/dhaka,2/omsk,2/qyzylorda,2/qostanay,2/thimphu,2/urumqi,9/chagos,2/dacca,2/kashgar,2/thimbu", + "6.5|n": "2/yangon,9/cocos,2/rangoon", + "5|s": "12/mawson,9/kerguelen", + "5|n": "2/aqtau,2/aqtobe,2/ashgabat,2/atyrau,2/dushanbe,2/karachi,2/oral,2/samarkand,2/tashkent,2/yekaterinburg,9/maldives,2/ashkhabad", + "5.75|n": "2/katmandu,2/kathmandu", + "5.5|n": "2/kolkata,2/colombo,2/calcutta", + "4|s": "9/reunion", + "4|n": "2/baku,2/dubai,2/muscat,2/tbilisi,2/yerevan,8/astrakhan,8/samara,8/saratov,8/ulyanovsk,8/volgograd,2/volgograd,9/mahe,9/mauritius", + "4.5|n|03/22:00->09/21:24": "2/tehran,iran", + "4.5|n": "2/kabul", + "3|s": "12/syowa,9/antananarivo", + "3|n|03/27:03->10/30:04": "2/famagusta,2/nicosia,8/athens,8/bucharest,8/helsinki,8/kiev,8/mariehamn,8/riga,8/sofia,8/tallinn,8/uzhgorod,8/vilnius,8/zaporozhye,8/nicosia", + "3|n|03/27:02->10/30:03": "8/chisinau,8/tiraspol", + "3|n|03/27:00->10/29:24": "2/beirut", + "3|n|03/26:00->10/28:01": "2/gaza,2/hebron", + "3|n|03/25:02->10/30:02": "2/jerusalem,2/tel_aviv,israel", + "3|n|03/25:00->10/27:24": "2/damascus", + "3|n|02/25:00->10/28:01": "2/amman", + "3|n": "0/addis_ababa,0/asmara,0/asmera,0/dar_es_salaam,0/djibouti,0/juba,0/kampala,0/mogadishu,0/nairobi,2/aden,2/baghdad,2/bahrain,2/kuwait,2/qatar,2/riyadh,8/istanbul,8/kirov,8/minsk,8/moscow,8/simferopol,9/comoro,9/mayotte,2/istanbul,turkey,w-su", + "2|s|03/27:02->10/30:02": "12/troll", + "2|s": "0/gaborone,0/harare,0/johannesburg,0/lubumbashi,0/lusaka,0/maputo,0/maseru,0/mbabane", + "2|n|03/27:02->10/30:03": "0/ceuta,arctic/longyearbyen,8/amsterdam,8/andorra,8/belgrade,8/berlin,8/bratislava,8/brussels,8/budapest,8/busingen,8/copenhagen,8/gibraltar,8/ljubljana,8/luxembourg,8/madrid,8/malta,8/monaco,8/oslo,8/paris,8/podgorica,8/prague,8/rome,8/san_marino,8/sarajevo,8/skopje,8/stockholm,8/tirane,8/vaduz,8/vatican,8/vienna,8/warsaw,8/zagreb,8/zurich,3/jan_mayen,poland", + "2|n": "0/blantyre,0/bujumbura,0/cairo,0/khartoum,0/kigali,0/tripoli,8/kaliningrad,egypt,libya", + "1|s": "0/brazzaville,0/kinshasa,0/luanda,0/windhoek", + "1|n|03/27:03->05/08:02": "0/casablanca,0/el_aaiun", + "1|n|03/27:01->10/30:02": "3/canary,3/faroe,3/madeira,8/dublin,8/guernsey,8/isle_of_man,8/jersey,8/lisbon,8/london,3/faeroe,eire,8/belfast,gb-eire,gb,portugal", + "1|n": "0/algiers,0/bangui,0/douala,0/lagos,0/libreville,0/malabo,0/ndjamena,0/niamey,0/porto-novo,0/tunis", + "14|n": "11/kiritimati", + "13|s|04/04:04->09/26:03": "11/apia", + "13|s|01/15:02->11/05:03": "11/tongatapu", + "13|n": "11/enderbury,11/fakaofo", + "12|s|04/03:03->09/25:02": "12/mcmurdo,11/auckland,12/south_pole,nz", + "12|s|01/17:03->11/14:02": "11/fiji", + "12|n": "2/anadyr,2/kamchatka,2/srednekolymsk,11/funafuti,11/kwajalein,11/majuro,11/nauru,11/tarawa,11/wake,11/wallis,kwajalein", + "12.75|s|04/03:03->04/03:02": "11/chatham,nz-chat", + "11|s|04/03:03->10/02:02": "12/macquarie", + "11|s": "11/bougainville", + "11|n": "2/magadan,2/sakhalin,11/efate,11/guadalcanal,11/kosrae,11/noumea,11/pohnpei,11/ponape", + "11.5|n|04/03:03->10/02:02": "11/norfolk", + "10|s|04/03:03->10/02:02": "4/currie,4/hobart,4/melbourne,4/sydney,4/act,4/canberra,4/nsw,4/tasmania,4/victoria", + "10|s": "12/dumontdurville,4/brisbane,4/lindeman,11/port_moresby,4/queensland", + "10|n": "2/ust-nera,2/vladivostok,2/yakutsk,11/guam,11/saipan,11/chuuk,11/truk,11/yap", + "10.5|s|04/03:01->10/02:02": "4/lord_howe,4/lhi", + "0|n|03/27:00->10/30:01": "1/scoresbysund,3/azores", + "0|n": "0/abidjan,0/accra,0/bamako,0/banjul,0/bissau,0/conakry,0/dakar,0/freetown,0/lome,0/monrovia,0/nouakchott,0/ouagadougou,0/sao_tome,1/danmarkshavn,3/reykjavik,3/st_helena,13/gmt,13/utc,0/timbuktu,13/greenwich,13/uct,13/universal,13/zulu,gmt-0,gmt+0,gmt0,greenwich,iceland,uct,universal,utc,zulu", + "-9|n|03/13:02->11/06:02": "1/adak,1/atka,us/aleutian", + "-9|n": "11/gambier", + "-9.5|n": "11/marquesas", + "-8|n|03/13:02->11/06:02": "1/anchorage,1/juneau,1/metlakatla,1/nome,1/sitka,1/yakutat,us/alaska", + "-8|n": "11/pitcairn", + "-7|n|03/13:02->11/06:02": "1/los_angeles,1/santa_isabel,1/tijuana,1/vancouver,1/ensenada,6/pacific,10/bajanorte,us/pacific-new,us/pacific", + "-7|n|03/08:02->11/01:01": "1/dawson,1/whitehorse,6/yukon", + "-7|n": "1/creston,1/dawson_creek,1/fort_nelson,1/hermosillo,1/phoenix,us/arizona", + "-6|s|04/02:22->09/03:22": "11/easter,7/easterisland", + "-6|n|04/03:02->10/30:02": "1/chihuahua,1/mazatlan,10/bajasur", + "-6|n|03/13:02->11/06:02": "1/boise,1/cambridge_bay,1/denver,1/edmonton,1/inuvik,1/ojinaga,1/yellowknife,1/shiprock,6/mountain,navajo,us/mountain", + "-6|n": "1/belize,1/costa_rica,1/el_salvador,1/guatemala,1/managua,1/regina,1/swift_current,1/tegucigalpa,11/galapagos,6/east-saskatchewan,6/saskatchewan", + "-5|s": "1/lima,1/rio_branco,1/porto_acre,5/acre", + "-5|n|04/03:02->10/30:02": "1/bahia_banderas,1/merida,1/mexico_city,1/monterrey,10/general", + "-5|n|03/13:02->11/06:02": "1/chicago,1/matamoros,1/menominee,1/rainy_river,1/rankin_inlet,1/resolute,1/winnipeg,1/indiana/knox,1/indiana/tell_city,1/north_dakota/beulah,1/north_dakota/center,1/north_dakota/new_salem,1/knox_in,6/central,us/central,us/indiana-starke", + "-5|n|03/12:03->11/05:01": "1/north_dakota", + "-5|n": "1/bogota,1/cancun,1/cayman,1/coral_harbour,1/eirunepe,1/guayaquil,1/jamaica,1/panama,1/atikokan,jamaica", + "-4|s|05/13:23->08/13:01": "12/palmer", + "-4|s|04/02:24->09/04:00": "1/santiago,7/continental", + "-4|s|03/26:24->10/02:00": "1/asuncion", + "-4|s|02/16:24->11/03:00": "1/campo_grande,1/cuiaba", + "-4|s": "1/la_paz,1/manaus,5/west", + "-4|n|03/13:02->11/06:02": "1/detroit,1/grand_turk,1/indianapolis,1/iqaluit,1/louisville,1/montreal,1/nassau,1/new_york,1/nipigon,1/pangnirtung,1/port-au-prince,1/thunder_bay,1/toronto,1/indiana/marengo,1/indiana/petersburg,1/indiana/vevay,1/indiana/vincennes,1/indiana/winamac,1/kentucky/monticello,1/fort_wayne,1/indiana/indianapolis,1/kentucky/louisville,6/eastern,us/east-indiana,us/eastern,us/michigan", + "-4|n|03/13:00->11/06:01": "1/havana,cuba", + "-4|n|03/12:03->11/05:01": "1/indiana,1/kentucky", + "-4|n": "1/anguilla,1/antigua,1/aruba,1/barbados,1/blanc-sablon,1/boa_vista,1/caracas,1/curacao,1/dominica,1/grenada,1/guadeloupe,1/guyana,1/kralendijk,1/lower_princes,1/marigot,1/martinique,1/montserrat,1/port_of_spain,1/porto_velho,1/puerto_rico,1/santo_domingo,1/st_barthelemy,1/st_kitts,1/st_lucia,1/st_thomas,1/st_vincent,1/tortola,1/virgin", + "-3|s": "1/argentina,1/buenos_aires,1/catamarca,1/cordoba,1/fortaleza,1/jujuy,1/mendoza,1/montevideo,1/punta_arenas,1/sao_paulo,12/rothera,3/stanley,1/argentina/la_rioja,1/argentina/rio_gallegos,1/argentina/salta,1/argentina/san_juan,1/argentina/san_luis,1/argentina/tucuman,1/argentina/ushuaia,1/argentina/comodrivadavia,1/argentina/buenos_aires,1/argentina/catamarca,1/argentina/cordoba,1/argentina/jujuy,1/argentina/mendoza,1/argentina/rosario,1/rosario,5/east", + "-3|n|03/13:02->11/06:02": "1/glace_bay,1/goose_bay,1/halifax,1/moncton,1/thule,3/bermuda,6/atlantic", + "-3|n": "1/araguaina,1/bahia,1/belem,1/cayenne,1/maceio,1/paramaribo,1/recife,1/santarem", + "-2|n|03/26:22->10/29:23": "1/nuuk,1/godthab", + "-2|n|03/13:02->11/06:02": "1/miquelon", + "-2|n": "1/noronha,3/south_georgia,5/denoronha", + "-2.5|n|03/13:02->11/06:02": "1/st_johns,6/newfoundland", + "-1|n": "3/cape_verde", + "-11|n": "11/midway,11/niue,11/pago_pago,11/samoa,us/samoa", + "-10|n": "11/honolulu,11/johnston,11/rarotonga,11/tahiti,us/hawaii" + }; + + //prefixes for iana names.. + var prefixes = [ + 'africa', + 'america', + 'asia', + 'atlantic', + 'australia', + 'brazil', + 'canada', + 'chile', + 'europe', + 'indian', + 'mexico', + 'pacific', + 'antarctica', + 'etc' + ]; + + let all = {}; + Object.keys(data).forEach((k) => { + let split = k.split('|'); + let obj = { + offset: Number(split[0]), + hem: split[1] + }; + if (split[2]) { + obj.dst = split[2]; + } + let names = data[k].split(','); + names.forEach((str) => { + str = str.replace(/(^[0-9]+)\//, (before, num) => { + num = Number(num); + return prefixes[num] + '/' + }); + all[str] = obj; + }); + }); + + all.utc = { + offset: 0, + hem: 'n' //default to northern hemisphere - (sorry!) + }; + + //add etc/gmt+n + for (let i = -14; i <= 14; i += 0.5) { + let num = i; + if (num > 0) { + num = '+' + num; + } + let name = 'etc/gmt' + num; + all[name] = { + offset: i * -1, //they're negative! + hem: 'n' //(sorry) + }; + name = 'utc/gmt' + num; //this one too, why not. + all[name] = { + offset: i * -1, + hem: 'n' + }; + } + + var zones$1 = all; + + //find the implicit iana code for this machine. + //safely query the Intl object + //based on - https://bitbucket.org/pellepim/jstimezonedetect/src + const fallbackTZ = 'utc'; // + + //this Intl object is not supported often, yet + const safeIntl = () => { + if (typeof Intl === 'undefined' || typeof Intl.DateTimeFormat === 'undefined') { + return null + } + let format = Intl.DateTimeFormat(); + if (typeof format === 'undefined' || typeof format.resolvedOptions === 'undefined') { + return null + } + let timezone = format.resolvedOptions().timeZone; + if (!timezone) { + return null + } + return timezone.toLowerCase() + }; + + const guessTz = () => { + let timezone = safeIntl(); + if (timezone === null) { + return fallbackTZ + } + return timezone + }; + //do it once per computer + var guessTz$1 = guessTz; + + const isOffset$1 = /(\-?[0-9]+)h(rs)?/i; + const isNumber$1 = /(\-?[0-9]+)/; + const utcOffset$1 = /utc([\-+]?[0-9]+)/i; + const gmtOffset$1 = /gmt([\-+]?[0-9]+)/i; + + const toIana$1 = function (num) { + num = Number(num); + if (num >= -13 && num <= 13) { + num = num * -1; //it's opposite! + num = (num > 0 ? '+' : '') + num; //add plus sign + return 'etc/gmt' + num + } + return null + }; + + const parseOffset$3 = function (tz) { + // '+5hrs' + let m = tz.match(isOffset$1); + if (m !== null) { + return toIana$1(m[1]) + } + // 'utc+5' + m = tz.match(utcOffset$1); + if (m !== null) { + return toIana$1(m[1]) + } + // 'GMT-5' (not opposite) + m = tz.match(gmtOffset$1); + if (m !== null) { + let num = Number(m[1]) * -1; + return toIana$1(num) + } + // '+5' + m = tz.match(isNumber$1); + if (m !== null) { + return toIana$1(m[1]) + } + return null + }; + var parseOffset$4 = parseOffset$3; + + const local = guessTz$1(); + + //add all the city names by themselves + const cities = Object.keys(zones$1).reduce((h, k) => { + let city = k.split('/')[1] || ''; + city = city.replace(/_/g, ' '); + h[city] = k; + return h + }, {}); + + //try to match these against iana form + const normalize$5 = (tz) => { + tz = tz.replace(/ time/g, ''); + tz = tz.replace(/ (standard|daylight|summer)/g, ''); + tz = tz.replace(/\b(east|west|north|south)ern/g, '$1'); + tz = tz.replace(/\b(africa|america|australia)n/g, '$1'); + tz = tz.replace(/\beuropean/g, 'europe'); + tz = tz.replace(/\islands/g, 'island'); + return tz + }; + + // try our best to reconcile the timzone to this given string + const lookupTz = (str, zones) => { + if (!str) { + return local + } + if (typeof str !== 'string') { + console.error("Timezone must be a string - recieved: '", str, "'\n"); + } + let tz = str.trim(); + // let split = str.split('/') + //support long timezones like 'America/Argentina/Rio_Gallegos' + // if (split.length > 2 && zones.hasOwnProperty(tz) === false) { + // tz = split[0] + '/' + split[1] + // } + tz = tz.toLowerCase(); + if (zones.hasOwnProperty(tz) === true) { + return tz + } + //lookup more loosely.. + tz = normalize$5(tz); + if (zones.hasOwnProperty(tz) === true) { + return tz + } + //try city-names + if (cities.hasOwnProperty(tz) === true) { + return cities[tz] + } + // //try to parse '-5h' + if (/[0-9]/.test(tz) === true) { + let id = parseOffset$4(tz); + if (id) { + return id + } + } + + throw new Error( + "Spacetime: Cannot find timezone named: '" + str + "'. Please enter an IANA timezone id." + ) + }; + var findTz = lookupTz; + + //git:blame @JuliasCaesar https://www.timeanddate.com/date/leapyear.html + function isLeapYear(year) { return (year % 4 === 0 && year % 100 !== 0) || year % 400 === 0 } + // unsurprisingly-nasty `typeof date` call + function isDate(d) { return Object.prototype.toString.call(d) === '[object Date]' && !isNaN(d.valueOf()) } + function isArray$1(input) { return Object.prototype.toString.call(input) === '[object Array]' } + function isObject(input) { return Object.prototype.toString.call(input) === '[object Object]' } + function isBoolean(input) { return Object.prototype.toString.call(input) === '[object Boolean]' } + + function zeroPad(str, len = 2) { + let pad = '0'; + str = str + ''; + return str.length >= len ? str : new Array(len - str.length + 1).join(pad) + str + } + + function titleCase$1(str) { + if (!str) { + return '' + } + return str[0].toUpperCase() + str.substr(1) + } + + function ordinal(i) { + let j = i % 10; + let k = i % 100; + if (j === 1 && k !== 11) { + return i + 'st' + } + if (j === 2 && k !== 12) { + return i + 'nd' + } + if (j === 3 && k !== 13) { + return i + 'rd' + } + return i + 'th' + } + + //strip 'st' off '1st'.. + function toCardinal(str) { + str = String(str); + str = str.replace(/([0-9])(st|nd|rd|th)$/i, '$1'); + return parseInt(str, 10) + } + + //used mostly for cleanup of unit names, like 'months' + function normalize$4(str = '') { + str = str.toLowerCase().trim(); + str = str.replace(/ies$/, 'y'); //'centuries' + str = str.replace(/s$/, ''); + str = str.replace(/-/g, ''); + if (str === 'day' || str === 'days') { + return 'date' + } + if (str === 'min' || str === 'mins') { + return 'minute' + } + return str + } + + function getEpoch(tmp) { + //support epoch + if (typeof tmp === 'number') { + return tmp + } + //suport date objects + if (isDate(tmp)) { + return tmp.getTime() + } + if (tmp.epoch) { + return tmp.epoch + } + return null + } + + //make sure this input is a spacetime obj + function beADate(d, s) { + if (isObject(d) === false) { + return s.clone().set(d) + } + return d + } + + function formatTimezone(offset, delimiter = '') { + const sign = offset > 0 ? '+' : '-'; + const absOffset = Math.abs(offset); + const hours = zeroPad(parseInt('' + absOffset, 10)); + const minutes = zeroPad((absOffset % 1) * 60); + return `${sign}${hours}${delimiter}${minutes}` + } + + const defaults$1 = { + year: new Date().getFullYear(), + month: 0, + date: 1 + }; + + //support [2016, 03, 01] format + const parseArray$1 = (s, arr, today) => { + if (arr.length === 0) { + return s + } + let order = ['year', 'month', 'date', 'hour', 'minute', 'second', 'millisecond']; + for (let i = 0; i < order.length; i++) { + let num = arr[i] || today[order[i]] || defaults$1[order[i]] || 0; + s = s[order[i]](num); + } + return s + }; + + //support {year:2016, month:3} format + const parseObject$1 = (s, obj, today) => { + // if obj is empty, do nothing + if (Object.keys(obj).length === 0) { + return s + } + obj = Object.assign({}, defaults$1, today, obj); + let keys = Object.keys(obj); + for (let i = 0; i < keys.length; i++) { + let unit = keys[i]; + //make sure we have this method + if (s[unit] === undefined || typeof s[unit] !== 'function') { + continue + } + //make sure the value is a number + if (obj[unit] === null || obj[unit] === undefined || obj[unit] === '') { + continue + } + let num = obj[unit] || today[unit] || defaults$1[unit] || 0; + s = s[unit](num); + } + return s + }; + + // this may seem like an arbitrary number, but it's 'within jan 1970' + // this is only really ambiguous until 2054 or so + const parseNumber$1 = function (s, input) { + const minimumEpoch = 2500000000; + // if the given epoch is really small, they've probably given seconds and not milliseconds + // anything below this number is likely (but not necessarily) a mistaken input. + if (input > 0 && input < minimumEpoch && s.silent === false) { + console.warn(' - Warning: You are setting the date to January 1970.'); + console.warn(' - did input seconds instead of milliseconds?'); + } + s.epoch = input; + return s + }; + + var fns = { + parseArray: parseArray$1, + parseObject: parseObject$1, + parseNumber: parseNumber$1 + }; + + // pull in 'today' data for the baseline moment + const getNow = function (s) { + s.epoch = Date.now(); + Object.keys(s._today || {}).forEach((k) => { + if (typeof s[k] === 'function') { + s = s[k](s._today[k]); + } + }); + return s + }; + + const dates$4 = { + now: (s) => { + return getNow(s) + }, + today: (s) => { + return getNow(s) + }, + tonight: (s) => { + s = getNow(s); + s = s.hour(18); //6pm + return s + }, + tomorrow: (s) => { + s = getNow(s); + s = s.add(1, 'day'); + s = s.startOf('day'); + return s + }, + yesterday: (s) => { + s = getNow(s); + s = s.subtract(1, 'day'); + s = s.startOf('day'); + return s + }, + christmas: (s) => { + let year = getNow(s).year(); + s = s.set([year, 11, 25, 18, 0, 0]); // Dec 25 + return s + }, + 'new years': (s) => { + let year = getNow(s).year(); + s = s.set([year, 11, 31, 18, 0, 0]); // Dec 31 + return s + } + }; + dates$4['new years eve'] = dates$4['new years']; + var namedDates = dates$4; + + //little cleanup.. + const normalize$2 = function (str) { + // remove all day-names + str = str.replace(/\b(mon|tues?|wed|wednes|thur?s?|fri|sat|satur|sun)(day)?\b/i, ''); + //remove ordinal ending + str = str.replace(/([0-9])(th|rd|st|nd)/, '$1'); + str = str.replace(/,/g, ''); + str = str.replace(/ +/g, ' ').trim(); + return str + }; + + var normalize$3 = normalize$2; + + let o = { + millisecond: 1 + }; + o.second = 1000; + o.minute = 60000; + o.hour = 3.6e6; // dst is supported post-hoc + o.day = 8.64e7; // + o.date = o.day; + o.month = 8.64e7 * 29.5; //(average) + o.week = 6.048e8; + o.year = 3.154e10; // leap-years are supported post-hoc + //add plurals + Object.keys(o).forEach(k => { + o[k + 's'] = o[k]; + }); + var ms = o; + + //basically, step-forward/backward until js Date object says we're there. + const walk = (s, n, fn, unit, previous) => { + let current = s.d[fn](); + if (current === n) { + return //already there + } + let startUnit = previous === null ? null : s.d[previous](); + let original = s.epoch; + //try to get it as close as we can + let diff = n - current; + s.epoch += ms[unit] * diff; + //DST edge-case: if we are going many days, be a little conservative + // console.log(unit, diff) + if (unit === 'day') { + // s.epoch -= ms.minute + //but don't push it over a month + if (Math.abs(diff) > 28 && n < 28) { + s.epoch += ms.hour; + } + } + // 1st time: oops, did we change previous unit? revert it. + if (previous !== null && startUnit !== s.d[previous]()) { + // console.warn('spacetime warning: missed setting ' + unit) + s.epoch = original; + // s.epoch += ms[unit] * diff * 0.89 // maybe try and make it close...? + } + //repair it if we've gone too far or something + //(go by half-steps, just in case) + const halfStep = ms[unit] / 2; + while (s.d[fn]() < n) { + s.epoch += halfStep; + } + + while (s.d[fn]() > n) { + s.epoch -= halfStep; + } + // 2nd time: did we change previous unit? revert it. + if (previous !== null && startUnit !== s.d[previous]()) { + // console.warn('spacetime warning: missed setting ' + unit) + s.epoch = original; + } + }; + //find the desired date by a increment/check while loop + const units$4 = { + year: { + valid: (n) => n > -4000 && n < 4000, + walkTo: (s, n) => walk(s, n, 'getFullYear', 'year', null) + }, + month: { + valid: (n) => n >= 0 && n <= 11, + walkTo: (s, n) => { + let d = s.d; + let current = d.getMonth(); + let original = s.epoch; + let startUnit = d.getFullYear(); + if (current === n) { + return + } + //try to get it as close as we can.. + let diff = n - current; + s.epoch += ms.day * (diff * 28); //special case + //oops, did we change the year? revert it. + if (startUnit !== s.d.getFullYear()) { + s.epoch = original; + } + //increment by day + while (s.d.getMonth() < n) { + s.epoch += ms.day; + } + while (s.d.getMonth() > n) { + s.epoch -= ms.day; + } + } + }, + date: { + valid: (n) => n > 0 && n <= 31, + walkTo: (s, n) => walk(s, n, 'getDate', 'day', 'getMonth') + }, + hour: { + valid: (n) => n >= 0 && n < 24, + walkTo: (s, n) => walk(s, n, 'getHours', 'hour', 'getDate') + }, + minute: { + valid: (n) => n >= 0 && n < 60, + walkTo: (s, n) => walk(s, n, 'getMinutes', 'minute', 'getHours') + }, + second: { + valid: (n) => n >= 0 && n < 60, + walkTo: (s, n) => { + //do this one directly + s.epoch = s.seconds(n).epoch; + } + }, + millisecond: { + valid: (n) => n >= 0 && n < 1000, + walkTo: (s, n) => { + //do this one directly + s.epoch = s.milliseconds(n).epoch; + } + } + }; + + const walkTo = (s, wants) => { + let keys = Object.keys(units$4); + let old = s.clone(); + for (let i = 0; i < keys.length; i++) { + let k = keys[i]; + let n = wants[k]; + if (n === undefined) { + n = old[k](); + } + if (typeof n === 'string') { + n = parseInt(n, 10); + } + //make-sure it's valid + if (!units$4[k].valid(n)) { + s.epoch = null; + if (s.silent === false) { + console.warn('invalid ' + k + ': ' + n); + } + return + } + units$4[k].walkTo(s, n); + } + return + }; + + var walkTo$1 = walkTo; + + const monthLengths = [ + 31, // January - 31 days + 28, // February - 28 days in a common year and 29 days in leap years + 31, // March - 31 days + 30, // April - 30 days + 31, // May - 31 days + 30, // June - 30 days + 31, // July - 31 days + 31, // August - 31 days + 30, // September - 30 days + 31, // October - 31 days + 30, // November - 30 days + 31 // December - 31 days + ]; + var monthLength = monthLengths; + + // 28 - feb + // 30 - april, june, sept, nov + // 31 - jan, march, may, july, aug, oct, dec + + let shortMonths = [ + 'jan', + 'feb', + 'mar', + 'apr', + 'may', + 'jun', + 'jul', + 'aug', + 'sep', + 'oct', + 'nov', + 'dec' + ]; + let longMonths = [ + 'january', + 'february', + 'march', + 'april', + 'may', + 'june', + 'july', + 'august', + 'september', + 'october', + 'november', + 'december' + ]; + + function buildMapping() { + const obj = { + sep: 8 //support this format + }; + for (let i = 0; i < shortMonths.length; i++) { + obj[shortMonths[i]] = i; + } + for (let i = 0; i < longMonths.length; i++) { + obj[longMonths[i]] = i; + } + return obj + } + + function short$1() { return shortMonths } + function long$1() { return longMonths } + function mapping$3() { return buildMapping() } + function set$3(i18n) { + shortMonths = i18n.short || shortMonths; + longMonths = i18n.long || longMonths; + } + + //pull-apart ISO offsets, like "+0100" + const parseOffset$1 = (s, offset) => { + if (!offset) { + return s + } + + // according to ISO8601, tz could be hh:mm, hhmm or hh + // so need few more steps before the calculation. + let num = 0; + + // for (+-)hh:mm + if (/^[\+-]?[0-9]{2}:[0-9]{2}$/.test(offset)) { + //support "+01:00" + if (/:00/.test(offset) === true) { + offset = offset.replace(/:00/, ''); + } + //support "+01:30" + if (/:30/.test(offset) === true) { + offset = offset.replace(/:30/, '.5'); + } + } + + // for (+-)hhmm + if (/^[\+-]?[0-9]{4}$/.test(offset)) { + offset = offset.replace(/30$/, '.5'); + } + num = parseFloat(offset); + + //divide by 100 or 10 - , "+0100", "+01" + if (Math.abs(num) > 100) { + num = num / 100; + } + //this is a fancy-move + if (num === 0 || offset === 'Z' || offset === 'z') { + s.tz = 'etc/gmt'; + return s + } + //okay, try to match it to a utc timezone + //remember - this is opposite! a -5 offset maps to Etc/GMT+5 ¯\_(:/)_/¯ + //https://askubuntu.com/questions/519550/why-is-the-8-timezone-called-gmt-8-in-the-filesystem + num *= -1; + + if (num >= 0) { + num = '+' + num; + } + let tz = 'etc/gmt' + num; + let zones = s.timezones; + + if (zones[tz]) { + // log a warning if we're over-writing a given timezone? + // console.log('changing timezone to: ' + tz) + s.tz = tz; + } + return s + }; + var parseOffset$2 = parseOffset$1; + + // truncate any sub-millisecond values + const parseMs = function (str = '') { + str = String(str); + //js does not support sub-millisecond values + // so truncate these - 2021-11-02T19:55:30.087772 + if (str.length > 3) { + str = str.substr(0, 3); + } else if (str.length === 1) { + // assume ms are zero-padded on the left + // but maybe not on the right. + // turn '.10' into '.100' + str = str + '00'; + } else if (str.length === 2) { + str = str + '0'; + } + return Number(str) || 0 + }; + + const parseTime$2 = (s, str = '') => { + // remove all whitespace + str = str.replace(/^\s+/, '').toLowerCase(); + //formal time format - 04:30.23 + let arr = str.match(/([0-9]{1,2}):([0-9]{1,2}):?([0-9]{1,2})?[:\.]?([0-9]{1,4})?/); + if (arr !== null) { + //validate it a little + let h = Number(arr[1]); + if (h < 0 || h > 24) { + return s.startOf('day') + } + let m = Number(arr[2]); //don't accept '5:3pm' + if (arr[2].length < 2 || m < 0 || m > 59) { + return s.startOf('day') + } + s = s.hour(h); + s = s.minute(m); + s = s.seconds(arr[3] || 0); + s = s.millisecond(parseMs(arr[4])); + //parse-out am/pm + let ampm = str.match(/[\b0-9] ?(am|pm)\b/); + if (ampm !== null && ampm[1]) { + s = s.ampm(ampm[1]); + } + return s + } + + //try an informal form - 5pm (no minutes) + arr = str.match(/([0-9]+) ?(am|pm)/); + if (arr !== null && arr[1]) { + let h = Number(arr[1]); + //validate it a little.. + if (h > 12 || h < 1) { + return s.startOf('day') + } + s = s.hour(arr[1] || 0); + s = s.ampm(arr[2]); + s = s.startOf('hour'); + return s + } + + //no time info found, use start-of-day + s = s.startOf('day'); + return s + }; + var parseTime$3 = parseTime$2; + + let months$1 = mapping$3(); + + //given a month, return whether day number exists in it + const validate$1 = (obj) => { + //invalid values + if (monthLength.hasOwnProperty(obj.month) !== true) { + return false + } + //support leap-year in february + if (obj.month === 1) { + if (isLeapYear(obj.year) && obj.date <= 29) { + return true + } else { + return obj.date <= 28 + } + } + //is this date too-big for this month? + let max = monthLength[obj.month] || 0; + if (obj.date <= max) { + return true + } + return false + }; + + const parseYear = (str = '', today) => { + str = str.trim(); + // parse '86 shorthand + if (/^'[0-9][0-9]$/.test(str) === true) { + let num = Number(str.replace(/'/, '')); + if (num > 50) { + return 1900 + num + } + return 2000 + num + } + let year = parseInt(str, 10); + // use a given year from options.today + if (!year && today) { + year = today.year; + } + // fallback to this year + year = year || new Date().getFullYear(); + return year + }; + + const parseMonth = function (str) { + str = str.toLowerCase().trim(); + if (str === 'sept') { + return months$1.sep + } + return months$1[str] + }; + + var ymd = [ + // ===== + // y-m-d + // ===== + //iso-this 1998-05-30T22:00:00:000Z, iso-that 2017-04-03T08:00:00-0700 + { + reg: /^(\-?0?0?[0-9]{3,4})-([0-9]{1,2})-([0-9]{1,2})[T| ]([0-9.:]+)(Z|[0-9\-\+:]+)?$/i, + parse: (s, m) => { + let obj = { + year: m[1], + month: parseInt(m[2], 10) - 1, + date: m[3] + }; + if (validate$1(obj) === false) { + s.epoch = null; + return s + } + parseOffset$2(s, m[5]); + walkTo$1(s, obj); + s = parseTime$3(s, m[4]); + return s + } + }, + //short-iso "2015-03-25" or "2015/03/25" or "2015/03/25 12:26:14 PM" + { + reg: /^([0-9]{4})[\-\/\. ]([0-9]{1,2})[\-\/\. ]([0-9]{1,2})( [0-9]{1,2}(:[0-9]{0,2})?(:[0-9]{0,3})? ?(am|pm)?)?$/i, + parse: (s, m) => { + let obj = { + year: m[1], + month: parseInt(m[2], 10) - 1, + date: parseInt(m[3], 10) + }; + if (obj.month >= 12) { + //support yyyy/dd/mm (weird, but ok) + obj.date = parseInt(m[2], 10); + obj.month = parseInt(m[3], 10) - 1; + } + if (validate$1(obj) === false) { + s.epoch = null; + return s + } + walkTo$1(s, obj); + s = parseTime$3(s, m[4]); + return s + } + }, + + //text-month "2015-feb-25" + { + reg: /^([0-9]{4})[\-\/\. ]([a-z]+)[\-\/\. ]([0-9]{1,2})( [0-9]{1,2}(:[0-9]{0,2})?(:[0-9]{0,3})? ?(am|pm)?)?$/i, + parse: (s, m) => { + let obj = { + year: parseYear(m[1], s._today), + month: parseMonth(m[2]), + date: toCardinal(m[3] || '') + }; + if (validate$1(obj) === false) { + s.epoch = null; + return s + } + walkTo$1(s, obj); + s = parseTime$3(s, m[4]); + return s + } + } + ]; + + var mdy = [ + // ===== + // m-d-y + // ===== + //mm/dd/yyyy - uk/canada "6/28/2019, 12:26:14 PM" + { + reg: /^([0-9]{1,2})[\-\/.]([0-9]{1,2})[\-\/.]?([0-9]{4})?( [0-9]{1,2}:[0-9]{2}:?[0-9]{0,2}? ?(am|pm|gmt))?$/i, + parse: (s, arr) => { + let month = parseInt(arr[1], 10) - 1; + let date = parseInt(arr[2], 10); + //support dd/mm/yyy + if (s.british || month >= 12) { + date = parseInt(arr[1], 10); + month = parseInt(arr[2], 10) - 1; + } + let obj = { + date, + month, + year: parseYear(arr[3], s._today) || new Date().getFullYear() + }; + if (validate$1(obj) === false) { + s.epoch = null; + return s + } + walkTo$1(s, obj); + s = parseTime$3(s, arr[4]); + return s + } + }, + //alt short format - "feb-25-2015" + { + reg: /^([a-z]+)[\-\/\. ]([0-9]{1,2})[\-\/\. ]?([0-9]{4}|'[0-9]{2})?( [0-9]{1,2}(:[0-9]{0,2})?(:[0-9]{0,3})? ?(am|pm)?)?$/i, + parse: (s, arr) => { + let obj = { + year: parseYear(arr[3], s._today), + month: parseMonth(arr[1]), + date: toCardinal(arr[2] || '') + }; + if (validate$1(obj) === false) { + s.epoch = null; + return s + } + walkTo$1(s, obj); + s = parseTime$3(s, arr[4]); + return s + } + }, + + //Long "Mar 25 2015" + //February 22, 2017 15:30:00 + { + reg: /^([a-z]+) ([0-9]{1,2})( [0-9]{4})?( ([0-9:]+( ?am| ?pm| ?gmt)?))?$/i, + parse: (s, arr) => { + let obj = { + year: parseYear(arr[3], s._today), + month: parseMonth(arr[1]), + date: toCardinal(arr[2] || '') + }; + if (validate$1(obj) === false) { + s.epoch = null; + return s + } + walkTo$1(s, obj); + s = parseTime$3(s, arr[4]); + return s + } + }, + // 'Sun Mar 14 15:09:48 +0000 2021' + { + reg: /^([a-z]+) ([0-9]{1,2})( [0-9:]+)?( \+[0-9]{4})?( [0-9]{4})?$/i, + parse: (s, arr) => { + let obj = { + year: parseYear(arr[5], s._today), + month: parseMonth(arr[1]), + date: toCardinal(arr[2] || '') + }; + if (validate$1(obj) === false) { + s.epoch = null; + return s + } + walkTo$1(s, obj); + s = parseTime$3(s, arr[3]); + return s + } + } + ]; + + var dmy = [ + // ===== + // d-m-y + // ===== + //common british format - "25-feb-2015" + { + reg: /^([0-9]{1,2})[\-\/]([a-z]+)[\-\/]?([0-9]{4})?$/i, + parse: (s, m) => { + let obj = { + year: parseYear(m[3], s._today), + month: parseMonth(m[2]), + date: toCardinal(m[1] || '') + }; + if (validate$1(obj) === false) { + s.epoch = null; + return s + } + walkTo$1(s, obj); + s = parseTime$3(s, m[4]); + return s + } + }, + // "25 Mar 2015" + { + reg: /^([0-9]{1,2})( [a-z]+)( [0-9]{4}| '[0-9]{2})? ?([0-9]{1,2}:[0-9]{2}:?[0-9]{0,2}? ?(am|pm|gmt))?$/i, + parse: (s, m) => { + let obj = { + year: parseYear(m[3], s._today), + month: parseMonth(m[2]), + date: toCardinal(m[1]) + }; + if (!obj.month || validate$1(obj) === false) { + s.epoch = null; + return s + } + walkTo$1(s, obj); + s = parseTime$3(s, m[4]); + return s + } + }, + // 01-jan-2020 + { + reg: /^([0-9]{1,2})[\. -/]([a-z]+)[\. -/]([0-9]{4})?( [0-9]{1,2}(:[0-9]{0,2})?(:[0-9]{0,3})? ?(am|pm)?)?$/i, + parse: (s, m) => { + let obj = { + date: Number(m[1]), + month: parseMonth(m[2]), + year: Number(m[3]) + }; + if (validate$1(obj) === false) { + s.epoch = null; + return s + } + walkTo$1(s, obj); + s = s.startOf('day'); + s = parseTime$3(s, m[4]); + return s + } + } + ]; + + var misc = [ + // ===== + // no dates + // ===== + + // '2012-06' month-only + { + reg: /^([0-9]{4})[\-\/]([0-9]{2})$/i, + parse: (s, m) => { + let obj = { + year: m[1], + month: parseInt(m[2], 10) - 1, + date: 1 + }; + if (validate$1(obj) === false) { + s.epoch = null; + return s + } + walkTo$1(s, obj); + s = parseTime$3(s, m[4]); + return s + } + }, + + //February 2017 (implied date) + { + reg: /^([a-z]+) ([0-9]{4})$/i, + parse: (s, arr) => { + let obj = { + year: parseYear(arr[2], s._today), + month: parseMonth(arr[1]), + date: s._today.date || 1 + }; + if (validate$1(obj) === false) { + s.epoch = null; + return s + } + walkTo$1(s, obj); + s = parseTime$3(s, arr[4]); + return s + } + }, + + { + // 'q2 2002' + reg: /^(q[0-9])( of)?( [0-9]{4})?/i, + parse: (s, arr) => { + let quarter = arr[1] || ''; + s = s.quarter(quarter); + let year = arr[3] || ''; + if (year) { + year = year.trim(); + s = s.year(year); + } + return s + } + }, + { + // 'summer 2002' + reg: /^(spring|summer|winter|fall|autumn)( of)?( [0-9]{4})?/i, + parse: (s, arr) => { + let season = arr[1] || ''; + s = s.season(season); + let year = arr[3] || ''; + if (year) { + year = year.trim(); + s = s.year(year); + } + return s + } + }, + { + // '200bc' + reg: /^[0-9,]+ ?b\.?c\.?$/i, + parse: (s, arr) => { + let str = arr[0] || ''; + //make year-negative + str = str.replace(/^([0-9,]+) ?b\.?c\.?$/i, '-$1'); + let d = new Date(); + let obj = { + year: parseInt(str.trim(), 10), + month: d.getMonth(), + date: d.getDate() + }; + if (validate$1(obj) === false) { + s.epoch = null; + return s + } + walkTo$1(s, obj); + s = parseTime$3(s); + return s + } + }, + { + // '200ad' + reg: /^[0-9,]+ ?(a\.?d\.?|c\.?e\.?)$/i, + parse: (s, arr) => { + let str = arr[0] || ''; + //remove commas + str = str.replace(/,/g, ''); + let d = new Date(); + let obj = { + year: parseInt(str.trim(), 10), + month: d.getMonth(), + date: d.getDate() + }; + if (validate$1(obj) === false) { + s.epoch = null; + return s + } + walkTo$1(s, obj); + s = parseTime$3(s); + return s + } + }, + { + // '1992' + reg: /^[0-9]{4}( ?a\.?d\.?)?$/i, + parse: (s, arr) => { + let today = s._today; + // using today's date, but a new month is awkward. + if (today.month && !today.date) { + today.date = 1; + } + let d = new Date(); + let obj = { + year: parseYear(arr[0], today), + month: today.month || d.getMonth(), + date: today.date || d.getDate() + }; + if (validate$1(obj) === false) { + s.epoch = null; + return s + } + walkTo$1(s, obj); + s = parseTime$3(s); + return s + } + } + ]; + + var parsers = [].concat(ymd, mdy, dmy, misc); + + const parseString = function (s, input, givenTz) { + // let parsers = s.parsers || [] + //try each text-parse template, use the first good result + for (let i = 0; i < parsers.length; i++) { + let m = input.match(parsers[i].reg); + if (m) { + // console.log(parsers[i].reg) + let res = parsers[i].parse(s, m, givenTz); + if (res !== null && res.isValid()) { + return res + } + } + } + if (s.silent === false) { + console.warn("Warning: couldn't parse date-string: '" + input + "'"); + } + s.epoch = null; + return s + }; + var parseString$1 = parseString; + + const { parseArray, parseObject, parseNumber } = fns; + //we have to actually parse these inputs ourselves + // - can't use built-in js parser ;( + //========================================= + // ISO Date "2015-03-25" + // Short Date "03/25/2015" or "2015/03/25" + // Long Date "Mar 25 2015" or "25 Mar 2015" + // Full Date "Wednesday March 25 2015" + //========================================= + + const defaults = { + year: new Date().getFullYear(), + month: 0, + date: 1 + }; + + //find the epoch from different input styles + const parseInput = (s, input) => { + let today = s._today || defaults; + //if we've been given a epoch number, it's easy + if (typeof input === 'number') { + return parseNumber(s, input) + } + //set tmp time + s.epoch = Date.now(); + // overwrite tmp time with 'today' value, if exists + if (s._today && isObject(s._today) && Object.keys(s._today).length > 0) { + let res = parseObject(s, today, defaults); + if (res.isValid()) { + s.epoch = res.epoch; + } + } + // null input means 'now' + if (input === null || input === undefined || input === '') { + return s //k, we're good. + } + //support input of Date() object + if (isDate(input) === true) { + s.epoch = input.getTime(); + return s + } + //support [2016, 03, 01] format + if (isArray$1(input) === true) { + s = parseArray(s, input, today); + return s + } + //support {year:2016, month:3} format + if (isObject(input) === true) { + //support spacetime object as input + if (input.epoch) { + s.epoch = input.epoch; + s.tz = input.tz; + return s + } + s = parseObject(s, input, today); + return s + } + //input as a string.. + if (typeof input !== 'string') { + return s + } + //little cleanup.. + input = normalize$3(input); + //try some known-words, like 'now' + if (namedDates.hasOwnProperty(input) === true) { + s = namedDates[input](s); + return s + } + //try each text-parse template, use the first good result + return parseString$1(s, input) + }; + var handleInput = parseInput; + + let shortDays = ['sun', 'mon', 'tue', 'wed', 'thu', 'fri', 'sat']; + let longDays = ['sunday', 'monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday']; + + function short() { return shortDays } + function long() { return longDays } + function set$2(i18n) { + shortDays = i18n.short || shortDays; + longDays = i18n.long || longDays; + } + const aliases$1 = { + mo: 1, + tu: 2, + we: 3, + th: 4, + fr: 5, + sa: 6, + su: 7, + tues: 2, + weds: 3, + wedn: 3, + thur: 4, + thurs: 4 + }; + + let titleCaseEnabled = true; + + function useTitleCase() { + return titleCaseEnabled + } + + function set$1(val) { + titleCaseEnabled = val; + } + + // create the timezone offset part of an iso timestamp + // it's kind of nuts how involved this is + // "+01:00", "+0100", or simply "+01" + const isoOffset = s => { + let offset = s.timezone().current.offset; + return !offset ? 'Z' : formatTimezone(offset, ':') + }; + + var isoOffset$1 = isoOffset; + + const applyCaseFormat = (str) => { + if (useTitleCase()) { + return titleCase$1(str) + } + return str + }; + + // iso-year padding + const padYear = (num) => { + if (num >= 0) { + return zeroPad(num, 4) + } else { + num = Math.abs(num); + return '-' + zeroPad(num, 4) + } + }; + + const format = { + day: (s) => applyCaseFormat(s.dayName()), + 'day-short': (s) => applyCaseFormat(short()[s.day()]), + 'day-number': (s) => s.day(), + 'day-ordinal': (s) => ordinal(s.day()), + 'day-pad': (s) => zeroPad(s.day()), + + date: (s) => s.date(), + 'date-ordinal': (s) => ordinal(s.date()), + 'date-pad': (s) => zeroPad(s.date()), + + month: (s) => applyCaseFormat(s.monthName()), + 'month-short': (s) => applyCaseFormat(short$1()[s.month()]), + 'month-number': (s) => s.month(), + 'month-ordinal': (s) => ordinal(s.month()), + 'month-pad': (s) => zeroPad(s.month()), + 'iso-month': (s) => zeroPad(s.month() + 1), //1-based months + + year: (s) => { + let year = s.year(); + if (year > 0) { + return year + } + year = Math.abs(year); + return year + ' BC' + }, + 'year-short': (s) => { + let year = s.year(); + if (year > 0) { + return `'${String(s.year()).substr(2, 4)}` + } + year = Math.abs(year); + return year + ' BC' + }, + 'iso-year': (s) => { + let year = s.year(); + let isNegative = year < 0; + let str = zeroPad(Math.abs(year), 4); //0-padded + if (isNegative) { + //negative years are for some reason 6-digits ('-00008') + str = zeroPad(str, 6); + str = '-' + str; + } + return str + }, + + time: (s) => s.time(), + 'time-24': (s) => `${s.hour24()}:${zeroPad(s.minute())}`, + + hour: (s) => s.hour12(), + 'hour-pad': (s) => zeroPad(s.hour12()), + 'hour-24': (s) => s.hour24(), + 'hour-24-pad': (s) => zeroPad(s.hour24()), + + minute: (s) => s.minute(), + 'minute-pad': (s) => zeroPad(s.minute()), + second: (s) => s.second(), + 'second-pad': (s) => zeroPad(s.second()), + millisecond: (s) => s.millisecond(), + 'millisecond-pad': (s) => zeroPad(s.millisecond(), 3), + + ampm: (s) => s.ampm(), + quarter: (s) => 'Q' + s.quarter(), + season: (s) => s.season(), + era: (s) => s.era(), + json: (s) => s.json(), + timezone: (s) => s.timezone().name, + offset: (s) => isoOffset$1(s), + + numeric: (s) => `${s.year()}/${zeroPad(s.month() + 1)}/${zeroPad(s.date())}`, // yyyy/mm/dd + 'numeric-us': (s) => `${zeroPad(s.month() + 1)}/${zeroPad(s.date())}/${s.year()}`, // mm/dd/yyyy + 'numeric-uk': (s) => `${zeroPad(s.date())}/${zeroPad(s.month() + 1)}/${s.year()}`, //dd/mm/yyyy + 'mm/dd': (s) => `${zeroPad(s.month() + 1)}/${zeroPad(s.date())}`, //mm/dd + + // ... https://en.wikipedia.org/wiki/ISO_8601 ;((( + iso: (s) => { + let year = s.format('iso-year'); + let month = zeroPad(s.month() + 1); //1-based months + let date = zeroPad(s.date()); + let hour = zeroPad(s.h24()); + let minute = zeroPad(s.minute()); + let second = zeroPad(s.second()); + let ms = zeroPad(s.millisecond(), 3); + let offset = isoOffset$1(s); + return `${year}-${month}-${date}T${hour}:${minute}:${second}.${ms}${offset}` //2018-03-09T08:50:00.000-05:00 + }, + 'iso-short': (s) => { + let month = zeroPad(s.month() + 1); //1-based months + let date = zeroPad(s.date()); + let year = padYear(s.year()); + return `${year}-${month}-${date}` //2017-02-15 + }, + 'iso-utc': (s) => { + return new Date(s.epoch).toISOString() //2017-03-08T19:45:28.367Z + }, + + //i made these up + nice: (s) => `${short$1()[s.month()]} ${ordinal(s.date())}, ${s.time()}`, + 'nice-24': (s) => + `${short$1()[s.month()]} ${ordinal(s.date())}, ${s.hour24()}:${zeroPad( + s.minute() + )}`, + 'nice-year': (s) => `${short$1()[s.month()]} ${ordinal(s.date())}, ${s.year()}`, + 'nice-day': (s) => + `${short()[s.day()]} ${applyCaseFormat(short$1()[s.month()])} ${ordinal( + s.date() + )}`, + 'nice-full': (s) => + `${s.dayName()} ${applyCaseFormat(s.monthName())} ${ordinal(s.date())}, ${s.time()}`, + 'nice-full-24': (s) => + `${s.dayName()} ${applyCaseFormat(s.monthName())} ${ordinal( + s.date() + )}, ${s.hour24()}:${zeroPad(s.minute())}` + }; + //aliases + const aliases = { + 'day-name': 'day', + 'month-name': 'month', + 'iso 8601': 'iso', + 'time-h24': 'time-24', + 'time-12': 'time', + 'time-h12': 'time', + tz: 'timezone', + 'day-num': 'day-number', + 'month-num': 'month-number', + 'month-iso': 'iso-month', + 'year-iso': 'iso-year', + 'nice-short': 'nice', + 'nice-short-24': 'nice-24', + mdy: 'numeric-us', + dmy: 'numeric-uk', + ymd: 'numeric', + 'yyyy/mm/dd': 'numeric', + 'mm/dd/yyyy': 'numeric-us', + 'dd/mm/yyyy': 'numeric-us', + 'little-endian': 'numeric-uk', + 'big-endian': 'numeric', + 'day-nice': 'nice-day' + }; + Object.keys(aliases).forEach((k) => (format[k] = format[aliases[k]])); + + const printFormat = (s, str = '') => { + //don't print anything if it's an invalid date + if (s.isValid() !== true) { + return '' + } + //support .format('month') + if (format.hasOwnProperty(str)) { + let out = format[str](s) || ''; + if (str !== 'json') { + out = String(out); + if (str !== 'ampm') { + out = applyCaseFormat(out); + } + } + return out + } + //support '{hour}:{minute}' notation + if (str.indexOf('{') !== -1) { + let sections = /\{(.+?)\}/g; + str = str.replace(sections, (_, fmt) => { + fmt = fmt.toLowerCase().trim(); + if (format.hasOwnProperty(fmt)) { + let out = String(format[fmt](s)); + if (fmt !== 'ampm') { + return applyCaseFormat(out) + } + return out + } + return '' + }); + return str + } + + return s.format('iso-short') + }; + var format$1 = printFormat; + + //parse this insane unix-time-templating thing, from the 19th century + //http://unicode.org/reports/tr35/tr35-25.html#Date_Format_Patterns + + //time-symbols we support + const mapping$2 = { + G: (s) => s.era(), + GG: (s) => s.era(), + GGG: (s) => s.era(), + GGGG: (s) => (s.era() === 'AD' ? 'Anno Domini' : 'Before Christ'), + //year + y: (s) => s.year(), + yy: (s) => { + //last two chars + return zeroPad(Number(String(s.year()).substr(2, 4))) + }, + yyy: (s) => s.year(), + yyyy: (s) => s.year(), + yyyyy: (s) => '0' + s.year(), + // u: (s) => {},//extended non-gregorian years + + //quarter + Q: (s) => s.quarter(), + QQ: (s) => s.quarter(), + QQQ: (s) => s.quarter(), + QQQQ: (s) => s.quarter(), + + //month + M: (s) => s.month() + 1, + MM: (s) => zeroPad(s.month() + 1), + MMM: (s) => s.format('month-short'), + MMMM: (s) => s.format('month'), + + //week + w: (s) => s.week(), + ww: (s) => zeroPad(s.week()), + //week of month + // W: (s) => s.week(), + + //date of month + d: (s) => s.date(), + dd: (s) => zeroPad(s.date()), + //date of year + D: (s) => s.dayOfYear(), + DD: (s) => zeroPad(s.dayOfYear()), + DDD: (s) => zeroPad(s.dayOfYear(), 3), + + // F: (s) => {},//date of week in month + // g: (s) => {},//modified julian day + + //day + E: (s) => s.format('day-short'), + EE: (s) => s.format('day-short'), + EEE: (s) => s.format('day-short'), + EEEE: (s) => s.format('day'), + EEEEE: (s) => s.format('day')[0], + e: (s) => s.day(), + ee: (s) => s.day(), + eee: (s) => s.format('day-short'), + eeee: (s) => s.format('day'), + eeeee: (s) => s.format('day')[0], + + //am/pm + a: (s) => s.ampm().toUpperCase(), + aa: (s) => s.ampm().toUpperCase(), + aaa: (s) => s.ampm().toUpperCase(), + aaaa: (s) => s.ampm().toUpperCase(), + + //hour + h: (s) => s.h12(), + hh: (s) => zeroPad(s.h12()), + H: (s) => s.hour(), + HH: (s) => zeroPad(s.hour()), + // j: (s) => {},//weird hour format + + m: (s) => s.minute(), + mm: (s) => zeroPad(s.minute()), + s: (s) => s.second(), + ss: (s) => zeroPad(s.second()), + + //milliseconds + SSS: (s) => zeroPad(s.millisecond(), 3), + //milliseconds in the day + A: (s) => s.epoch - s.startOf('day').epoch, + //timezone + z: (s) => s.timezone().name, + zz: (s) => s.timezone().name, + zzz: (s) => s.timezone().name, + zzzz: (s) => s.timezone().name, + Z: (s) => formatTimezone(s.timezone().current.offset), + ZZ: (s) => formatTimezone(s.timezone().current.offset), + ZZZ: (s) => formatTimezone(s.timezone().current.offset), + ZZZZ: (s) => formatTimezone(s.timezone().current.offset, ':') + }; + + const addAlias = (char, to, n) => { + let name = char; + let toName = to; + for (let i = 0; i < n; i += 1) { + mapping$2[name] = mapping$2[toName]; + name += char; + toName += to; + } + }; + addAlias('q', 'Q', 4); + addAlias('L', 'M', 4); + addAlias('Y', 'y', 4); + addAlias('c', 'e', 4); + addAlias('k', 'H', 2); + addAlias('K', 'h', 2); + addAlias('S', 's', 2); + addAlias('v', 'z', 4); + addAlias('V', 'Z', 4); + + // support unix-style escaping with ' character + const escapeChars = function (arr) { + for (let i = 0; i < arr.length; i += 1) { + if (arr[i] === `'`) { + // greedy-search for next apostrophe + for (let o = i + 1; o < arr.length; o += 1) { + if (arr[o]) { + arr[i] += arr[o]; + } + if (arr[o] === `'`) { + arr[o] = null; + break + } + arr[o] = null; + } + } + } + return arr.filter((ch) => ch) + }; + + //combine consecutive chars, like 'yyyy' as one. + const combineRepeated = function (arr) { + for (let i = 0; i < arr.length; i += 1) { + let c = arr[i]; + // greedy-forward + for (let o = i + 1; o < arr.length; o += 1) { + if (arr[o] === c) { + arr[i] += arr[o]; + arr[o] = null; + } else { + break + } + } + } + // '' means one apostrophe + arr = arr.filter((ch) => ch); + arr = arr.map((str) => { + if (str === `''`) { + str = `'`; + } + return str + }); + return arr + }; + + const unixFmt = (s, str) => { + let arr = str.split(''); + // support character escaping + arr = escapeChars(arr); + //combine 'yyyy' as string. + arr = combineRepeated(arr); + return arr.reduce((txt, c) => { + if (mapping$2[c] !== undefined) { + txt += mapping$2[c](s) || ''; + } else { + // 'unescape' + if (/^'.{1,}'$/.test(c)) { + c = c.replace(/'/g, ''); + } + txt += c; + } + return txt + }, '') + }; + var unixFmt$1 = unixFmt; + + const units$3 = ['year', 'season', 'quarter', 'month', 'week', 'day', 'quarterHour', 'hour', 'minute']; + + const doUnit = function (s, k) { + let start = s.clone().startOf(k); + let end = s.clone().endOf(k); + let duration = end.epoch - start.epoch; + let percent = (s.epoch - start.epoch) / duration; + return parseFloat(percent.toFixed(2)) + }; + + //how far it is along, from 0-1 + const progress = (s, unit) => { + if (unit) { + unit = normalize$4(unit); + return doUnit(s, unit) + } + let obj = {}; + units$3.forEach(k => { + obj[k] = doUnit(s, k); + }); + return obj + }; + + var progress$1 = progress; + + //round to either current, or +1 of this unit + const nearest = (s, unit) => { + //how far have we gone? + let prog = s.progress(); + unit = normalize$4(unit); + //fix camel-case for this one + if (unit === 'quarterhour') { + unit = 'quarterHour'; + } + if (prog[unit] !== undefined) { + // go forward one? + if (prog[unit] > 0.5) { + s = s.add(1, unit); + } + // go to start + s = s.startOf(unit); + } else if (s.silent === false) { + console.warn("no known unit '" + unit + "'"); + } + return s + }; + var nearest$1 = nearest; + + //increment until dates are the same + const climb = (a, b, unit) => { + let i = 0; + a = a.clone(); + while (a.isBefore(b)) { + //do proper, expensive increment to catch all-the-tricks + a = a.add(1, unit); + i += 1; + } + //oops, we went too-far.. + if (a.isAfter(b, unit)) { + i -= 1; + } + return i + }; + + // do a thurough +=1 on the unit, until they match + // for speed-reasons, only used on day, month, week. + const diffOne = (a, b, unit) => { + if (a.isBefore(b)) { + return climb(a, b, unit) + } else { + return climb(b, a, unit) * -1 //reverse it + } + }; + + var diffOne$1 = diffOne; + + // don't do anything too fancy here. + // 2020 - 2019 may be 1 year, or 0 years + // - '1 year difference' means 366 days during a leap year + const fastYear = (a, b) => { + let years = b.year() - a.year(); + // should we decrement it by 1? + a = a.year(b.year()); + if (a.isAfter(b)) { + years -= 1; + } + return years + }; + + // use a waterfall-method for computing a diff of any 'pre-knowable' units + // compute years, then compute months, etc.. + // ... then ms-math for any very-small units + const diff$1 = function (a, b) { + // an hour is always the same # of milliseconds + // so these units can be 'pre-calculated' + let msDiff = b.epoch - a.epoch; + let obj = { + milliseconds: msDiff, + seconds: parseInt(msDiff / 1000, 10) + }; + obj.minutes = parseInt(obj.seconds / 60, 10); + obj.hours = parseInt(obj.minutes / 60, 10); + + //do the year + let tmp = a.clone(); + obj.years = fastYear(tmp, b); + tmp = a.add(obj.years, 'year'); + + //there's always 12 months in a year... + obj.months = obj.years * 12; + tmp = a.add(obj.months, 'month'); + obj.months += diffOne$1(tmp, b, 'month'); + + // there's always atleast 52 weeks in a year.. + // (month * 4) isn't as close + obj.weeks = obj.years * 52; + tmp = a.add(obj.weeks, 'week'); + obj.weeks += diffOne$1(tmp, b, 'week'); + + // there's always atleast 7 days in a week + obj.days = obj.weeks * 7; + tmp = a.add(obj.days, 'day'); + obj.days += diffOne$1(tmp, b, 'day'); + + return obj + }; + var waterfall = diff$1; + + const reverseDiff = function (obj) { + Object.keys(obj).forEach((k) => { + obj[k] *= -1; + }); + return obj + }; + + // this method counts a total # of each unit, between a, b. + // '1 month' means 28 days in february + // '1 year' means 366 days in a leap year + const main$1 = function (a, b, unit) { + b = beADate(b, a); + //reverse values, if necessary + let reversed = false; + if (a.isAfter(b)) { + let tmp = a; + a = b; + b = tmp; + reversed = true; + } + //compute them all (i know!) + let obj = waterfall(a, b); + if (reversed) { + obj = reverseDiff(obj); + } + //return just the requested unit + if (unit) { + //make sure it's plural-form + unit = normalize$4(unit); + if (/s$/.test(unit) !== true) { + unit += 's'; + } + if (unit === 'dates') { + unit = 'days'; + } + return obj[unit] + } + return obj + }; + + var diff = main$1; + + /* + ISO 8601 duration format + // https://en.wikipedia.org/wiki/ISO_8601#Durations + "P3Y6M4DT12H30M5S" + P the start of the duration representation. + Y the number of years. + M the number of months. + W the number of weeks. + D the number of days. + T of the representation. + H the number of hours. + M the number of minutes. + S the number of seconds. + */ + + const fmt = (n) => Math.abs(n) || 0; + + const toISO = function (diff) { + let iso = 'P'; + iso += fmt(diff.years) + 'Y'; + iso += fmt(diff.months) + 'M'; + iso += fmt(diff.days) + 'DT'; + iso += fmt(diff.hours) + 'H'; + iso += fmt(diff.minutes) + 'M'; + iso += fmt(diff.seconds) + 'S'; + return iso + }; + var toISO$1 = toISO; + + //get number of hours/minutes... between the two dates + function getDiff(a, b) { + const isBefore = a.isBefore(b); + const later = isBefore ? b : a; + let earlier = isBefore ? a : b; + earlier = earlier.clone(); + const diff = { + years: 0, + months: 0, + days: 0, + hours: 0, + minutes: 0, + seconds: 0 + }; + Object.keys(diff).forEach((unit) => { + if (earlier.isSame(later, unit)) { + return + } + let max = earlier.diff(later, unit); + earlier = earlier.add(max, unit); + diff[unit] = max; + }); + //reverse it, if necessary + if (isBefore) { + Object.keys(diff).forEach((u) => { + if (diff[u] !== 0) { + diff[u] *= -1; + } + }); + } + return diff + } + + //our conceptual 'break-points' for each unit + const qualifiers = { + months: { + almost: 10, + over: 4 + }, + days: { + almost: 25, + over: 10 + }, + hours: { + almost: 20, + over: 8 + }, + minutes: { + almost: 50, + over: 20 + }, + seconds: { + almost: 50, + over: 20 + } + }; + + // Expects a plural unit arg + function pluralize(value, unit) { + if (value === 1) { + unit = unit.slice(0, -1); + } + return value + ' ' + unit + } + + const toSoft = function (diff) { + let rounded = null; + let qualified = null; + let abbreviated = []; + let englishValues = []; + //go through each value and create its text-representation + Object.keys(diff).forEach((unit, i, units) => { + const value = Math.abs(diff[unit]); + if (value === 0) { + return + } + abbreviated.push(value + unit[0]); + const englishValue = pluralize(value, unit); + englishValues.push(englishValue); + if (!rounded) { + rounded = qualified = englishValue; + if (i > 4) { + return + } + //is it a 'almost' something, etc? + const nextUnit = units[i + 1]; + const nextValue = Math.abs(diff[nextUnit]); + if (nextValue > qualifiers[nextUnit].almost) { + rounded = pluralize(value + 1, unit); + qualified = 'almost ' + rounded; + } else if (nextValue > qualifiers[nextUnit].over) { + qualified = 'over ' + englishValue; + } + } + }); + return { qualified, rounded, abbreviated, englishValues } + }; + var soften = toSoft; + + //by spencermountain + Shaun Grady + + //create the human-readable diff between the two dates + const since = (start, end) => { + end = beADate(end, start); + const diff = getDiff(start, end); + const isNow = Object.keys(diff).every((u) => !diff[u]); + if (isNow === true) { + return { + diff, + rounded: 'now', + qualified: 'now', + precise: 'now', + abbreviated: [], + iso: 'P0Y0M0DT0H0M0S', + direction: 'present', + } + } + let precise; + let direction = 'future'; + + let { rounded, qualified, englishValues, abbreviated } = soften(diff); + + //make them into a string + precise = englishValues.splice(0, 2).join(', '); + //handle before/after logic + if (start.isAfter(end) === true) { + rounded += ' ago'; + qualified += ' ago'; + precise += ' ago'; + direction = 'past'; + } else { + rounded = 'in ' + rounded; + qualified = 'in ' + qualified; + precise = 'in ' + precise; + } + // https://en.wikipedia.org/wiki/ISO_8601#Durations + // P[n]Y[n]M[n]DT[n]H[n]M[n]S + let iso = toISO$1(diff); + return { + diff, + rounded, + qualified, + precise, + abbreviated, + iso, + direction, + } + }; + + var since$1 = since; + + //https://www.timeanddate.com/calendar/aboutseasons.html + // Spring - from March 1 to May 31; + // Summer - from June 1 to August 31; + // Fall (autumn) - from September 1 to November 30; and, + // Winter - from December 1 to February 28 (February 29 in a leap year). + const north = [ + ['spring', 2, 1], + ['summer', 5, 1], + ['fall', 8, 1], + ['autumn', 8, 1], + ['winter', 11, 1] //dec 1 + ]; + const south = [ + ['fall', 2, 1], + ['autumn', 2, 1], + ['winter', 5, 1], + ['spring', 8, 1], + ['summer', 11, 1] //dec 1 + ]; + + var seasons$1 = { north, south }; + + var quarters = [ + null, + [0, 1], //jan 1 + [3, 1], //apr 1 + [6, 1], //july 1 + [9, 1] //oct 1 + ]; + + const units$2 = { + minute: (s) => { + walkTo$1(s, { + second: 0, + millisecond: 0 + }); + return s + }, + quarterhour: (s) => { + let minute = s.minutes(); + if (minute >= 45) { + s = s.minutes(45); + } else if (minute >= 30) { + s = s.minutes(30); + } else if (minute >= 15) { + s = s.minutes(15); + } else { + s = s.minutes(0); + } + walkTo$1(s, { + second: 0, + millisecond: 0 + }); + return s + }, + hour: (s) => { + walkTo$1(s, { + minute: 0, + second: 0, + millisecond: 0 + }); + return s + }, + day: (s) => { + walkTo$1(s, { + hour: 0, + minute: 0, + second: 0, + millisecond: 0 + }); + return s + }, + week: (s) => { + let original = s.clone(); + s = s.day(s._weekStart); //monday + if (s.isAfter(original)) { + s = s.subtract(1, 'week'); + } + walkTo$1(s, { + hour: 0, + minute: 0, + second: 0, + millisecond: 0 + }); + return s + }, + month: (s) => { + walkTo$1(s, { + date: 1, + hour: 0, + minute: 0, + second: 0, + millisecond: 0 + }); + return s + }, + quarter: (s) => { + let q = s.quarter(); + if (quarters[q]) { + walkTo$1(s, { + month: quarters[q][0], + date: quarters[q][1], + hour: 0, + minute: 0, + second: 0, + millisecond: 0 + }); + } + return s + }, + season: (s) => { + let current = s.season(); + let hem = 'north'; + if (s.hemisphere() === 'South') { + hem = 'south'; + } + for (let i = 0; i < seasons$1[hem].length; i++) { + if (seasons$1[hem][i][0] === current) { + //winter goes between years + let year = s.year(); + if (current === 'winter' && s.month() < 3) { + year -= 1; + } + walkTo$1(s, { + year, + month: seasons$1[hem][i][1], + date: seasons$1[hem][i][2], + hour: 0, + minute: 0, + second: 0, + millisecond: 0 + }); + return s + } + } + return s + }, + year: (s) => { + walkTo$1(s, { + month: 0, + date: 1, + hour: 0, + minute: 0, + second: 0, + millisecond: 0 + }); + return s + }, + decade: (s) => { + s = s.startOf('year'); + let year = s.year(); + let decade = parseInt(year / 10, 10) * 10; + s = s.year(decade); + return s + }, + century: (s) => { + s = s.startOf('year'); + let year = s.year(); + // near 0AD goes '-1 | +1' + let decade = parseInt(year / 100, 10) * 100; + s = s.year(decade); + return s + } + }; + units$2.date = units$2.day; + + const startOf = (a, unit) => { + let s = a.clone(); + unit = normalize$4(unit); + if (units$2[unit]) { + return units$2[unit](s) + } + if (unit === 'summer' || unit === 'winter') { + s = s.season(unit); + return units$2.season(s) + } + return s + }; + + //piggy-backs off startOf + const endOf = (a, unit) => { + let s = a.clone(); + unit = normalize$4(unit); + if (units$2[unit]) { + // go to beginning, go to next one, step back 1ms + s = units$2[unit](s); // startof + s = s.add(1, unit); + s = s.subtract(1, 'millisecond'); + return s + } + return s + }; + + //is it 'wednesday'? + const isDay = function (unit) { + if (short().find((s) => s === unit)) { + return true + } + if (long().find((s) => s === unit)) { + return true + } + return false + }; + + // return a list of the weeks/months/days between a -> b + // returns spacetime objects in the timezone of the input + const every = function (start, unit, end) { + if (!unit || !end) { + return [] + } + //cleanup unit param + unit = normalize$4(unit); + //cleanup to param + end = start.clone().set(end); + //swap them, if they're backwards + if (start.isAfter(end)) { + let tmp = start; + start = end; + end = tmp; + } + + //support 'every wednesday' + let d = start.clone(); + if (isDay(unit)) { + d = d.next(unit); + unit = 'week'; + } else { + let first = d.startOf(unit); + if (first.isBefore(start)) { + d = d.next(unit); + } + } + //okay, actually start doing it + let result = []; + while (d.isBefore(end)) { + result.push(d); + d = d.add(1, unit); + } + return result + }; + var every$1 = every; + + const parseDst = dst => { + if (!dst) { + return [] + } + return dst.split('->') + }; + + const titleCase = str => { + str = str[0].toUpperCase() + str.substr(1); + str = str.replace(/\/gmt/, '/GMT'); + str = str.replace(/[\/_]([a-z])/gi, s => { + return s.toUpperCase() + }); + return str + }; + + //get metadata about this timezone + const timezone$1 = s => { + let zones = s.timezones; + let tz = s.tz; + if (zones.hasOwnProperty(tz) === false) { + tz = findTz(s.tz, zones); + } + if (tz === null) { + if (s.silent === false) { + console.warn("Warn: could not find given or local timezone - '" + s.tz + "'"); + } + return { + current: { + epochShift: 0 + } + } + } + let found = zones[tz]; + let result = { + name: titleCase(tz), + hasDst: Boolean(found.dst), + default_offset: found.offset, + //do north-hemisphere version as default (sorry!) + hemisphere: found.hem === 's' ? 'South' : 'North', + current: {} + }; + + if (result.hasDst) { + let arr = parseDst(found.dst); + result.change = { + start: arr[0], + back: arr[1] + }; + } + //find the offsets for summer/winter times + //(these variable names are north-centric) + let summer = found.offset; // (july) + let winter = summer; // (january) assume it's the same for now + if (result.hasDst === true) { + if (result.hemisphere === 'North') { + winter = summer - 1; + } else { + //southern hemisphere + winter = found.offset + 1; + } + } + + //find out which offset to use right now + //use 'summer' time july-time + if (result.hasDst === false) { + result.current.offset = summer; + result.current.isDST = false; + } else if (inSummerTime$1(s.epoch, result.change.start, result.change.back, summer, winter) === true) { + result.current.offset = summer; + result.current.isDST = result.hemisphere === 'North'; //dst 'on' in winter in north + } else { + //use 'winter' january-time + result.current.offset = winter; + result.current.isDST = result.hemisphere === 'South'; //dst 'on' in summer in south + } + return result + }; + var timezone$2 = timezone$1; + + const units$1 = [ + 'century', + 'decade', + 'year', + 'month', + 'date', + 'day', + 'hour', + 'minute', + 'second', + 'millisecond' + ]; + + //the spacetime instance methods (also, the API) + const methods$4 = { + set: function (input, tz) { + let s = this.clone(); + s = handleInput(s, input, null); + if (tz) { + this.tz = findTz(tz); + } + return s + }, + timezone: function () { + return timezone$2(this) + }, + isDST: function () { + return timezone$2(this).current.isDST + }, + hasDST: function () { + return timezone$2(this).hasDst + }, + offset: function () { + return timezone$2(this).current.offset * 60 + }, + hemisphere: function () { + return timezone$2(this).hemisphere + }, + format: function (fmt) { + return format$1(this, fmt) + }, + unixFmt: function (fmt) { + return unixFmt$1(this, fmt) + }, + startOf: function (unit) { + return startOf(this, unit) + }, + endOf: function (unit) { + return endOf(this, unit) + }, + leapYear: function () { + let year = this.year(); + return isLeapYear(year) + }, + progress: function (unit) { + return progress$1(this, unit) + }, + nearest: function (unit) { + return nearest$1(this, unit) + }, + diff: function (d, unit) { + return diff(this, d, unit) + }, + since: function (d) { + if (!d) { + d = this.clone().set(); + } + return since$1(this, d) + }, + next: function (unit) { + let s = this.add(1, unit); + return s.startOf(unit) + }, + //the start of the previous year/week/century + last: function (unit) { + let s = this.subtract(1, unit); + return s.startOf(unit) + }, + isValid: function () { + //null/undefined epochs + if (!this.epoch && this.epoch !== 0) { + return false + } + return !isNaN(this.d.getTime()) + }, + //travel to this timezone + goto: function (tz) { + let s = this.clone(); + s.tz = findTz(tz, s.timezones); //science! + return s + }, + //get each week/month/day between a -> b + every: function (unit, to) { + // allow swapping these params: + if (typeof unit === 'object' && typeof to === 'string') { + let tmp = to; + to = unit; + unit = tmp; + } + return every$1(this, unit, to) + }, + isAwake: function () { + let hour = this.hour(); + //10pm -> 8am + if (hour < 8 || hour > 22) { + return false + } + return true + }, + isAsleep: function () { + return !this.isAwake() + }, + daysInMonth: function () { + switch (this.month()) { + case 0: + return 31 + case 1: + return this.leapYear() ? 29 : 28 + case 2: + return 31 + case 3: + return 30 + case 4: + return 31 + case 5: + return 30 + case 6: + return 31 + case 7: + return 31 + case 8: + return 30 + case 9: + return 31 + case 10: + return 30 + case 11: + return 31 + default: + throw new Error('Invalid Month state.') + } + }, + //pretty-printing + log: function () { + console.log(''); + console.log(format$1(this, 'nice-short')); + return this + }, + logYear: function () { + console.log(''); + console.log(format$1(this, 'full-short')); + return this + }, + json: function () { + return units$1.reduce((h, unit) => { + h[unit] = this[unit](); + return h + }, {}) + }, + debug: function () { + let tz = this.timezone(); + let date = this.format('MM') + ' ' + this.format('date-ordinal') + ' ' + this.year(); + date += '\n - ' + this.format('time'); + console.log('\n\n', date + '\n - ' + tz.name + ' (' + tz.current.offset + ')'); + return this + }, + //alias of 'since' but opposite - like moment.js + from: function (d) { + d = this.clone().set(d); + return d.since(this) + }, + fromNow: function () { + let d = this.clone().set(Date.now()); + return d.since(this) + }, + weekStart: function (input) { + //accept a number directly + if (typeof input === 'number') { + this._weekStart = input; + return this + } + if (typeof input === 'string') { + // accept 'wednesday' + input = input.toLowerCase().trim(); + let num = short().indexOf(input); + if (num === -1) { + num = long().indexOf(input); + } + if (num === -1) { + num = 1; //go back to default + } + this._weekStart = num; + } else { + console.warn('Spacetime Error: Cannot understand .weekStart() input:', input); + } + return this + } + }; + // aliases + methods$4.inDST = methods$4.isDST; + methods$4.round = methods$4.nearest; + methods$4.each = methods$4.every; + var methods$5 = methods$4; + + // javascript setX methods like setDate() can't be used because of the local bias + + const validate = (n) => { + //handle number as a string + if (typeof n === 'string') { + n = parseInt(n, 10); + } + return n + }; + + const order$1 = ['year', 'month', 'date', 'hour', 'minute', 'second', 'millisecond']; + + //reduce hostile micro-changes when moving dates by millisecond + const confirm = (s, tmp, unit) => { + let n = order$1.indexOf(unit); + let arr = order$1.slice(n, order$1.length); + for (let i = 0; i < arr.length; i++) { + let want = tmp[arr[i]](); + s[arr[i]](want); + } + return s + }; + + // allow specifying setter direction + const fwdBkwd = function (s, old, goFwd, unit) { + if (goFwd === true && s.isBefore(old)) { + s = s.add(1, unit); + } else if (goFwd === false && s.isAfter(old)) { + s = s.minus(1, unit); + } + return s + }; + + const milliseconds = function (s, n) { + n = validate(n); + let current = s.millisecond(); + let diff = current - n; //milliseconds to shift by + return s.epoch - diff + }; + + const seconds = function (s, n, goFwd) { + n = validate(n); + let old = s.clone(); + let diff = s.second() - n; + let shift = diff * ms.second; + s.epoch = s.epoch - shift; + s = fwdBkwd(s, old, goFwd, 'minute'); // specify direction + return s.epoch + }; + + const minutes = function (s, n, goFwd) { + n = validate(n); + let old = s.clone(); + let diff = s.minute() - n; + let shift = diff * ms.minute; + s.epoch -= shift; + confirm(s, old, 'second'); + s = fwdBkwd(s, old, goFwd, 'hour'); // specify direction + return s.epoch + }; + + const hours = function (s, n, goFwd) { + n = validate(n); + if (n >= 24) { + n = 24; + } else if (n < 0) { + n = 0; + } + let old = s.clone(); + let diff = s.hour() - n; + let shift = diff * ms.hour; + s.epoch -= shift; + // oops, did we change the day? + if (s.date() !== old.date()) { + s = old.clone(); + if (diff > 1) { + diff -= 1; + } + if (diff < 1) { + diff += 1; + } + shift = diff * ms.hour; + s.epoch -= shift; + } + walkTo$1(s, { + hour: n + }); + confirm(s, old, 'minute'); + s = fwdBkwd(s, old, goFwd, 'day'); // specify direction + return s.epoch + }; + + const time$1 = function (s, str, goFwd) { + let m = str.match(/([0-9]{1,2})[:h]([0-9]{1,2})(:[0-9]{1,2})? ?(am|pm)?/); + if (!m) { + //fallback to support just '2am' + m = str.match(/([0-9]{1,2}) ?(am|pm)/); + if (!m) { + return s.epoch + } + m.splice(2, 0, '0'); //add implicit 0 minutes + m.splice(3, 0, ''); //add implicit seconds + } + let h24 = false; + let hour = parseInt(m[1], 10); + let minute = parseInt(m[2], 10); + if (minute >= 60) { + minute = 59; + } + if (hour > 12) { + h24 = true; + } + //make the hour into proper 24h time + if (h24 === false) { + if (m[4] === 'am' && hour === 12) { + //12am is midnight + hour = 0; + } + if (m[4] === 'pm' && hour < 12) { + //12pm is noon + hour += 12; + } + } + // handle seconds + m[3] = m[3] || ''; + m[3] = m[3].replace(/:/, ''); + let sec = parseInt(m[3], 10) || 0; + let old = s.clone(); + s = s.hour(hour); + s = s.minute(minute); + s = s.second(sec); + s = s.millisecond(0); + s = fwdBkwd(s, old, goFwd, 'day'); // specify direction + return s.epoch + }; + + const date = function (s, n, goFwd) { + n = validate(n); + //avoid setting february 31st + if (n > 28) { + let month = s.month(); + let max = monthLength[month]; + // support leap day in february + if (month === 1 && n === 29 && isLeapYear(s.year())) { + max = 29; + } + if (n > max) { + n = max; + } + } + //avoid setting < 0 + if (n <= 0) { + n = 1; + } + let old = s.clone(); + walkTo$1(s, { + date: n + }); + s = fwdBkwd(s, old, goFwd, 'month'); // specify direction + return s.epoch + }; + + const month = function (s, n, goFwd) { + if (typeof n === 'string') { + n = mapping$3()[n.toLowerCase()]; + } + n = validate(n); + //don't go past december + if (n >= 12) { + n = 11; + } + if (n <= 0) { + n = 0; + } + + let d = s.date(); + //there's no 30th of february, etc. + if (d > monthLength[n]) { + //make it as close as we can.. + d = monthLength[n]; + } + let old = s.clone(); + walkTo$1(s, { + month: n, + d + }); + s = fwdBkwd(s, old, goFwd, 'year'); // specify direction + return s.epoch + }; + + const year = function (s, n) { + // support '97 + if (typeof n === 'string' && /^'[0-9]{2}$/.test(n)) { + n = n.replace(/'/, '').trim(); + n = Number(n); + // '89 is 1989 + if (n > 30) { + //change this in 10y + n = 1900 + n; + } else { + // '12 is 2012 + n = 2000 + n; + } + } + n = validate(n); + walkTo$1(s, { + year: n + }); + return s.epoch + }; + + const week = function (s, n, goFwd) { + let old = s.clone(); + n = validate(n); + s = s.month(0); + s = s.date(1); + s = s.day('monday'); + //first week starts first Thurs in Jan + // so mon dec 28th is 1st week + // so mon dec 29th is not the week + if (s.monthName() === 'december' && s.date() >= 28) { + s = s.add(1, 'week'); + } + n -= 1; //1-based + s = s.add(n, 'weeks'); + s = fwdBkwd(s, old, goFwd, 'year'); // specify direction + return s.epoch + }; + + const dayOfYear = function (s, n, goFwd) { + n = validate(n); + let old = s.clone(); + n -= 1; //days are 1-based + if (n <= 0) { + n = 0; + } else if (n >= 365) { + n = 364; + } + s = s.startOf('year'); + s = s.add(n, 'day'); + confirm(s, old, 'hour'); + s = fwdBkwd(s, old, goFwd, 'year'); // specify direction + return s.epoch + }; + + let morning = 'am'; + let evening = 'pm'; + + function am() { return morning } + function pm() { return evening } + function set(i18n) { + morning = i18n.am || morning; + evening = i18n.pm || evening; + } + + const methods$3 = { + millisecond: function (num) { + if (num !== undefined) { + let s = this.clone(); + s.epoch = milliseconds(s, num); + return s + } + return this.d.getMilliseconds() + }, + second: function (num, goFwd) { + if (num !== undefined) { + let s = this.clone(); + s.epoch = seconds(s, num, goFwd); + return s + } + return this.d.getSeconds() + }, + minute: function (num, goFwd) { + if (num !== undefined) { + let s = this.clone(); + s.epoch = minutes(s, num, goFwd); + return s + } + return this.d.getMinutes() + }, + hour: function (num, goFwd) { + let d = this.d; + if (num !== undefined) { + let s = this.clone(); + s.epoch = hours(s, num, goFwd); + return s + } + return d.getHours() + }, + + //'3:30' is 3.5 + hourFloat: function (num, goFwd) { + if (num !== undefined) { + let s = this.clone(); + let minute = num % 1; + minute = minute * 60; + let hour = parseInt(num, 10); + s.epoch = hours(s, hour, goFwd); + s.epoch = minutes(s, minute, goFwd); + return s + } + let d = this.d; + let hour = d.getHours(); + let minute = d.getMinutes(); + minute = minute / 60; + return hour + minute + }, + + // hour in 12h format + hour12: function (str, goFwd) { + let d = this.d; + if (str !== undefined) { + let s = this.clone(); + str = '' + str; + let m = str.match(/^([0-9]+)(am|pm)$/); + if (m) { + let hour = parseInt(m[1], 10); + if (m[2] === 'pm') { + hour += 12; + } + s.epoch = hours(s, hour, goFwd); + } + return s + } + //get the hour + let hour12 = d.getHours(); + if (hour12 > 12) { + hour12 = hour12 - 12; + } + if (hour12 === 0) { + hour12 = 12; + } + return hour12 + }, + + //some ambiguity here with 12/24h + time: function (str, goFwd) { + if (str !== undefined) { + let s = this.clone(); + str = str.toLowerCase().trim(); + s.epoch = time$1(s, str, goFwd); + return s + } + return `${this.h12()}:${zeroPad(this.minute())}${this.ampm()}` + }, + + // either 'am' or 'pm' + ampm: function (input, goFwd) { + // let which = 'am' + let which = am(); + let hour = this.hour(); + if (hour >= 12) { + // which = 'pm' + which = pm(); + } + if (typeof input !== 'string') { + return which + } + //okay, we're doing a setter + let s = this.clone(); + input = input.toLowerCase().trim(); + //ampm should never change the day + // - so use `.hour(n)` instead of `.minus(12,'hour')` + if (hour >= 12 && input === 'am') { + //noon is 12pm + hour -= 12; + return s.hour(hour, goFwd) + } + if (hour < 12 && input === 'pm') { + hour += 12; + return s.hour(hour, goFwd) + } + return s + }, + + //some hard-coded times of day, like 'noon' + dayTime: function (str, goFwd) { + if (str !== undefined) { + const times = { + morning: '7:00am', + breakfast: '7:00am', + noon: '12:00am', + lunch: '12:00pm', + afternoon: '2:00pm', + evening: '6:00pm', + dinner: '6:00pm', + night: '11:00pm', + midnight: '23:59pm' + }; + let s = this.clone(); + str = str || ''; + str = str.toLowerCase(); + if (times.hasOwnProperty(str) === true) { + s = s.time(times[str], goFwd); + } + return s + } + let h = this.hour(); + if (h < 6) { + return 'night' + } + if (h < 12) { + //until noon + return 'morning' + } + if (h < 17) { + //until 5pm + return 'afternoon' + } + if (h < 22) { + //until 10pm + return 'evening' + } + return 'night' + }, + + //parse a proper iso string + iso: function (num) { + if (num !== undefined) { + return this.set(num) + } + return this.format('iso') + } + }; + var timeFns = methods$3; + + const methods$2 = { + // # day in the month + date: function (num, goFwd) { + if (num !== undefined) { + let s = this.clone(); + num = parseInt(num, 10); + if (num) { + s.epoch = date(s, num, goFwd); + } + return s + } + return this.d.getDate() + }, + + //like 'wednesday' (hard!) + day: function (input, goFwd) { + if (input === undefined) { + return this.d.getDay() + } + let original = this.clone(); + let want = input; + // accept 'wednesday' + if (typeof input === 'string') { + input = input.toLowerCase(); + if (aliases$1.hasOwnProperty(input)) { + want = aliases$1[input]; + } else { + want = short().indexOf(input); + if (want === -1) { + want = long().indexOf(input); + } + } + } + //move approx + let day = this.d.getDay(); + let diff = day - want; + if (goFwd === true && diff > 0) { + diff = diff - 7; + } + if (goFwd === false && diff < 0) { + diff = diff + 7; + } + let s = this.subtract(diff, 'days'); + //tighten it back up + walkTo$1(s, { + hour: original.hour(), + minute: original.minute(), + second: original.second() + }); + return s + }, + + //these are helpful name-wrappers + dayName: function (input, goFwd) { + if (input === undefined) { + return long()[this.day()] + } + let s = this.clone(); + s = s.day(input, goFwd); + return s + } + }; + var dateFns = methods$2; + + const clearMinutes = (s) => { + s = s.minute(0); + s = s.second(0); + s = s.millisecond(1); + return s + }; + + const methods$1 = { + // day 0-366 + dayOfYear: function (num, goFwd) { + if (num !== undefined) { + let s = this.clone(); + s.epoch = dayOfYear(s, num, goFwd); + return s + } + //days since newyears - jan 1st is 1, jan 2nd is 2... + let sum = 0; + let month = this.d.getMonth(); + let tmp; + //count the num days in each month + for (let i = 1; i <= month; i++) { + tmp = new Date(); + tmp.setDate(1); + tmp.setFullYear(this.d.getFullYear()); //the year matters, because leap-years + tmp.setHours(1); + tmp.setMinutes(1); + tmp.setMonth(i); + tmp.setHours(-2); //the last day of the month + sum += tmp.getDate(); + } + return sum + this.d.getDate() + }, + + //since the start of the year + week: function (num, goFwd) { + // week-setter + if (num !== undefined) { + let s = this.clone(); + s.epoch = week(this, num, goFwd); + s = clearMinutes(s); + return s + } + //find-out which week it is + let tmp = this.clone(); + tmp = tmp.month(0); + tmp = tmp.date(1); + tmp = clearMinutes(tmp); + tmp = tmp.day('monday'); + //don't go into last-year + if (tmp.monthName() === 'december' && tmp.date() >= 28) { + tmp = tmp.add(1, 'week'); + } + // is first monday the 1st? + let toAdd = 1; + if (tmp.date() === 1) { + toAdd = 0; + } + tmp = tmp.minus(1, 'second'); + const thisOne = this.epoch; + //if the week technically hasn't started yet + if (tmp.epoch > thisOne) { + return 1 + } + //speed it up, if we can + let i = 0; + let skipWeeks = this.month() * 4; + tmp.epoch += ms.week * skipWeeks; + i += skipWeeks; + for (; i <= 52; i++) { + if (tmp.epoch > thisOne) { + return i + toAdd + } + tmp = tmp.add(1, 'week'); + } + return 52 + }, + //either name or number + month: function (input, goFwd) { + if (input !== undefined) { + let s = this.clone(); + s.epoch = month(s, input, goFwd); + return s + } + return this.d.getMonth() + }, + //'january' + monthName: function (input, goFwd) { + if (input !== undefined) { + let s = this.clone(); + s = s.month(input, goFwd); + return s + } + return long$1()[this.month()] + }, + + //q1, q2, q3, q4 + quarter: function (num, goFwd) { + if (num !== undefined) { + if (typeof num === 'string') { + num = num.replace(/^q/i, ''); + num = parseInt(num, 10); + } + if (quarters[num]) { + let s = this.clone(); + let month = quarters[num][0]; + s = s.month(month, goFwd); + s = s.date(1, goFwd); + s = s.startOf('day'); + return s + } + } + let month = this.d.getMonth(); + for (let i = 1; i < quarters.length; i++) { + if (month < quarters[i][0]) { + return i - 1 + } + } + return 4 + }, + + //spring, summer, winter, fall + season: function (input, goFwd) { + let hem = 'north'; + if (this.hemisphere() === 'South') { + hem = 'south'; + } + if (input !== undefined) { + let s = this.clone(); + for (let i = 0; i < seasons$1[hem].length; i++) { + if (input === seasons$1[hem][i][0]) { + s = s.month(seasons$1[hem][i][1], goFwd); + s = s.date(1); + s = s.startOf('day'); + } + } + return s + } + let month = this.d.getMonth(); + for (let i = 0; i < seasons$1[hem].length - 1; i++) { + if (month >= seasons$1[hem][i][1] && month < seasons$1[hem][i + 1][1]) { + return seasons$1[hem][i][0] + } + } + return 'winter' + }, + + //the year number + year: function (num) { + if (num !== undefined) { + let s = this.clone(); + s.epoch = year(s, num); + return s + } + return this.d.getFullYear() + }, + + //bc/ad years + era: function (str) { + if (str !== undefined) { + let s = this.clone(); + str = str.toLowerCase(); + //TODO: there is no year-0AD i think. may have off-by-1 error here + let year$1 = s.d.getFullYear(); + //make '1992' into 1992bc.. + if (str === 'bc' && year$1 > 0) { + s.epoch = year(s, year$1 * -1); + } + //make '1992bc' into '1992' + if (str === 'ad' && year$1 < 0) { + s.epoch = year(s, year$1 * -1); + } + return s + } + if (this.d.getFullYear() < 0) { + return 'BC' + } + return 'AD' + }, + + // 2019 -> 2010 + decade: function (input) { + if (input !== undefined) { + input = String(input); + input = input.replace(/([0-9])'?s$/, '$1'); //1950's + input = input.replace(/([0-9])(th|rd|st|nd)/, '$1'); //fix ordinals + if (!input) { + console.warn('Spacetime: Invalid decade input'); + return this + } + // assume 20th century?? for '70s'. + if (input.length === 2 && /[0-9][0-9]/.test(input)) { + input = '19' + input; + } + let year = Number(input); + if (isNaN(year)) { + return this + } + // round it down to the decade + year = Math.floor(year / 10) * 10; + return this.year(year) //.startOf('decade') + } + return this.startOf('decade').year() + }, + // 1950 -> 19+1 + century: function (input) { + if (input !== undefined) { + if (typeof input === 'string') { + input = input.replace(/([0-9])(th|rd|st|nd)/, '$1'); //fix ordinals + input = input.replace(/([0-9]+) ?(b\.?c\.?|a\.?d\.?)/i, (a, b, c) => { + if (c.match(/b\.?c\.?/i)) { + b = '-' + b; + } + return b + }); + input = input.replace(/c$/, ''); //20thC + } + let year = Number(input); + if (isNaN(input)) { + console.warn('Spacetime: Invalid century input'); + return this + } + // there is no century 0 + if (year === 0) { + year = 1; + } + if (year >= 0) { + year = (year - 1) * 100; + } else { + year = (year + 1) * 100; + } + return this.year(year) + } + // century getter + let num = this.startOf('century').year(); + num = Math.floor(num / 100); + if (num < 0) { + return num - 1 + } + return num + 1 + }, + // 2019 -> 2+1 + millenium: function (input) { + if (input !== undefined) { + if (typeof input === 'string') { + input = input.replace(/([0-9])(th|rd|st|nd)/, '$1'); //fix ordinals + input = Number(input); + if (isNaN(input)) { + console.warn('Spacetime: Invalid millenium input'); + return this + } + } + if (input > 0) { + input -= 1; + } + let year = input * 1000; + // there is no year 0 + if (year === 0) { + year = 1; + } + return this.year(year) + } + // get the current millenium + let num = Math.floor(this.year() / 1000); + if (num >= 0) { + num += 1; + } + return num + } + }; + var yearFns = methods$1; + + const methods = Object.assign({}, timeFns, dateFns, yearFns); + + //aliases + methods.milliseconds = methods.millisecond; + methods.seconds = methods.second; + methods.minutes = methods.minute; + methods.hours = methods.hour; + methods.hour24 = methods.hour; + methods.h12 = methods.hour12; + methods.h24 = methods.hour24; + methods.days = methods.day; + + const addMethods$4 = Space => { + //hook the methods into prototype + Object.keys(methods).forEach(k => { + Space.prototype[k] = methods[k]; + }); + }; + + var queryFns = addMethods$4; + + const getMonthLength = function (month, year) { + if (month === 1 && isLeapYear(year)) { + return 29 + } + return monthLength[month] + }; + + //month is the one thing we 'model/compute' + //- because ms-shifting can be off by enough + const rollMonth = (want, old) => { + //increment year + if (want.month > 0) { + let years = parseInt(want.month / 12, 10); + want.year = old.year() + years; + want.month = want.month % 12; + } else if (want.month < 0) { + let m = Math.abs(want.month); + let years = parseInt(m / 12, 10); + if (m % 12 !== 0) { + years += 1; + } + want.year = old.year() - years; + //ignore extras + want.month = want.month % 12; + want.month = want.month + 12; + if (want.month === 12) { + want.month = 0; + } + } + return want + }; + + // briefly support day=-2 (this does not need to be perfect.) + const rollDaysDown = (want, old, sum) => { + want.year = old.year(); + want.month = old.month(); + let date = old.date(); + want.date = date - Math.abs(sum); + while (want.date < 1) { + want.month -= 1; + if (want.month < 0) { + want.month = 11; + want.year -= 1; + } + let max = getMonthLength(want.month, want.year); + want.date += max; + } + return want + }; + + // briefly support day=33 (this does not need to be perfect.) + const rollDaysUp = (want, old, sum) => { + let year = old.year(); + let month = old.month(); + let max = getMonthLength(month, year); + while (sum > max) { + sum -= max; + month += 1; + if (month >= 12) { + month -= 12; + year += 1; + } + max = getMonthLength(month, year); + } + want.month = month; + want.date = sum; + return want + }; + + const months = rollMonth; + const days = rollDaysUp; + const daysBack = rollDaysDown; + + // this logic is a bit of a mess, + // but briefly: + // millisecond-math, and some post-processing covers most-things + // we 'model' the calendar here only a little bit + // and that usually works-out... + + const order = ['millisecond', 'second', 'minute', 'hour', 'date', 'month']; + let keep = { + second: order.slice(0, 1), + minute: order.slice(0, 2), + quarterhour: order.slice(0, 2), + hour: order.slice(0, 3), + date: order.slice(0, 4), + month: order.slice(0, 4), + quarter: order.slice(0, 4), + season: order.slice(0, 4), + year: order, + decade: order, + century: order + }; + keep.week = keep.hour; + keep.season = keep.date; + keep.quarter = keep.date; + + // Units need to be dst adjuested + const dstAwareUnits = { + year: true, + quarter: true, + season: true, + month: true, + week: true, + date: true + }; + + const keepDate = { + month: true, + quarter: true, + season: true, + year: true + }; + + const addMethods$3 = (SpaceTime) => { + SpaceTime.prototype.add = function (num, unit) { + let s = this.clone(); + + if (!unit || num === 0) { + return s //don't bother + } + let old = this.clone(); + unit = normalize$4(unit); + if (unit === 'millisecond') { + s.epoch += num; + return s + } + // support 'fortnight' alias + if (unit === 'fortnight') { + num *= 2; + unit = 'week'; + } + //move forward by the estimated milliseconds (rough) + if (ms[unit]) { + s.epoch += ms[unit] * num; + } else if (unit === 'week' || unit === 'weekend') { + s.epoch += ms.day * (num * 7); + } else if (unit === 'quarter' || unit === 'season') { + s.epoch += ms.month * (num * 3); + } else if (unit === 'quarterhour') { + s.epoch += ms.minute * 15 * num; + } + //now ensure our milliseconds/etc are in-line + let want = {}; + if (keep[unit]) { + keep[unit].forEach((u) => { + want[u] = old[u](); + }); + } + + if (dstAwareUnits[unit]) { + const diff = old.timezone().current.offset - s.timezone().current.offset; + s.epoch += diff * 3600 * 1000; + } + + //ensure month/year has ticked-over + if (unit === 'month') { + want.month = old.month() + num; + //month is the one unit we 'model' directly + want = months(want, old); + } + //support coercing a week, too + if (unit === 'week') { + let sum = old.date() + num * 7; + if (sum <= 28 && sum > 1) { + want.date = sum; + } + } + if (unit === 'weekend' && s.dayName() !== 'saturday') { + s = s.day('saturday', true); //ensure it's saturday + } + //support 25-hour day-changes on dst-changes + else if (unit === 'date') { + if (num < 0) { + want = daysBack(want, old, num); + } else { + //specify a naive date number, if it's easy to do... + let sum = old.date() + num; + // ok, model this one too + want = days(want, old, sum); + } + //manually punt it if we haven't moved at all.. + if (num !== 0 && old.isSame(s, 'day')) { + want.date = old.date() + num; + } + } + // ensure a quarter is 3 months over + else if (unit === 'quarter') { + want.month = old.month() + num * 3; + want.year = old.year(); + // handle rollover + if (want.month < 0) { + let years = Math.floor(want.month / 12); + let remainder = want.month + Math.abs(years) * 12; + want.month = remainder; + want.year += years; + } else if (want.month >= 12) { + let years = Math.floor(want.month / 12); + want.month = want.month % 12; + want.year += years; + } + want.date = old.date(); + } + //ensure year has changed (leap-years) + else if (unit === 'year') { + let wantYear = old.year() + num; + let haveYear = s.year(); + if (haveYear < wantYear) { + let toAdd = Math.floor(num / 4) || 1; //approx num of leap-days + s.epoch += Math.abs(ms.day * toAdd); + } else if (haveYear > wantYear) { + let toAdd = Math.floor(num / 4) || 1; //approx num of leap-days + s.epoch += ms.day * toAdd; + } + } + //these are easier + else if (unit === 'decade') { + want.year = s.year() + 10; + } else if (unit === 'century') { + want.year = s.year() + 100; + } + //keep current date, unless the month doesn't have it. + if (keepDate[unit]) { + let max = monthLength[want.month]; + want.date = old.date(); + if (want.date > max) { + want.date = max; + } + } + if (Object.keys(want).length > 1) { + walkTo$1(s, want); + } + return s + }; + + //subtract is only add *-1 + SpaceTime.prototype.subtract = function (num, unit) { + let s = this.clone(); + return s.add(num * -1, unit) + }; + //add aliases + SpaceTime.prototype.minus = SpaceTime.prototype.subtract; + SpaceTime.prototype.plus = SpaceTime.prototype.add; + }; + + var addFns = addMethods$3; + + //make a string, for easy comparison between dates + const print = { + millisecond: (s) => { + return s.epoch + }, + second: (s) => { + return [s.year(), s.month(), s.date(), s.hour(), s.minute(), s.second()].join('-') + }, + minute: (s) => { + return [s.year(), s.month(), s.date(), s.hour(), s.minute()].join('-') + }, + hour: (s) => { + return [s.year(), s.month(), s.date(), s.hour()].join('-') + }, + day: (s) => { + return [s.year(), s.month(), s.date()].join('-') + }, + week: (s) => { + return [s.year(), s.week()].join('-') + }, + month: (s) => { + return [s.year(), s.month()].join('-') + }, + quarter: (s) => { + return [s.year(), s.quarter()].join('-') + }, + year: (s) => { + return s.year() + } + }; + print.date = print.day; + + const addMethods$2 = (SpaceTime) => { + SpaceTime.prototype.isSame = function (b, unit, tzAware = true) { + let a = this; + if (!unit) { + return null + } + // support swapped params + if (typeof b === 'string' && typeof unit === 'object') { + let tmp = b; + b = unit; + unit = tmp; + } + if (typeof b === 'string' || typeof b === 'number') { + b = new SpaceTime(b, this.timezone.name); + } + //support 'seconds' aswell as 'second' + unit = unit.replace(/s$/, ''); + + // make them the same timezone for proper comparison + if (tzAware === true && a.tz !== b.tz) { + b = b.clone(); + b.tz = a.tz; + } + if (print[unit]) { + return print[unit](a) === print[unit](b) + } + return null + }; }; - const parseUnit = function (m) { - let unit = m.match('#Duration').text('normal'); - unit = unit.replace(/s$/, ''); - // support shorthands like 'min' - if (aliases.hasOwnProperty(unit)) { - unit = aliases[unit]; - } - return unit + var sameFns = addMethods$2; + + const addMethods$1 = SpaceTime => { + const methods = { + isAfter: function (d) { + d = beADate(d, this); + let epoch = getEpoch(d); + if (epoch === null) { + return null + } + return this.epoch > epoch + }, + isBefore: function (d) { + d = beADate(d, this); + let epoch = getEpoch(d); + if (epoch === null) { + return null + } + return this.epoch < epoch + }, + isEqual: function (d) { + d = beADate(d, this); + let epoch = getEpoch(d); + if (epoch === null) { + return null + } + return this.epoch === epoch + }, + isBetween: function (start, end, isInclusive = false) { + start = beADate(start, this); + end = beADate(end, this); + let startEpoch = getEpoch(start); + if (startEpoch === null) { + return null + } + let endEpoch = getEpoch(end); + if (endEpoch === null) { + return null + } + if (isInclusive) { + return this.isBetween(start, end) || this.isEqual(start) || this.isEqual(end); + } + return startEpoch < this.epoch && this.epoch < endEpoch + } + }; + + //hook them into proto + Object.keys(methods).forEach(k => { + SpaceTime.prototype[k] = methods[k]; + }); }; - //turn '5 weeks before' to {weeks:5} - const parseShift = function (doc) { - let result = {}; - let m = doc.none(); - let shift = doc.match('#DateShift+'); - if (shift.found === false) { - return { res: result, m } - } + var compareFns = addMethods$1; - // '5 weeks' - shift.match('#Cardinal #Duration').forEach((ts) => { - let num = ts.match('#Cardinal').numbers().get()[0]; - if (num && typeof num === 'number') { - let unit = parseUnit(ts); - if (knownUnits[unit] === true) { - result[unit] = num; + const addMethods = SpaceTime => { + const methods = { + i18n: data => { + //change the day names + if (isObject(data.days)) { + set$2(data.days); + } + //change the month names + if (isObject(data.months)) { + set$3(data.months); + } + + // change the the display style of the month / day names + if (isBoolean(data.useTitleCase)) { + set$1(data.useTitleCase); + } + + //change am and pm strings + if (isObject(data.ampm)) { + set(data.ampm); } } + }; + + //hook them into proto + Object.keys(methods).forEach(k => { + SpaceTime.prototype[k] = methods[k]; }); - //is it 2 weeks ago? → -2 - if (shift.has('(before|ago|hence|back)$') === true) { - Object.keys(result).forEach((k) => (result[k] *= -1)); - } - m = shift.match('#Cardinal #Duration'); - shift = shift.not(m); + }; - // supoprt '1 day after tomorrow' - m = shift.match('[#Duration] [(after|before)]'); - if (m.found) { - let unit = m.groups('unit').text('reduced'); - // unit = unit.replace(/s$/, '') - let dir = m.groups('dir').text('reduced'); - if (dir === 'after') { - result[unit] = 1; - } else if (dir === 'before') { - result[unit] = -1; + var i18nFns = addMethods; + + let timezones$1 = zones$1; + //fake timezone-support, for fakers (es5 class) + const SpaceTime = function (input, tz, options = {}) { + //the holy moment + this.epoch = null; + //the shift for the given timezone + this.tz = findTz(tz, timezones$1); + //whether to output warnings to console + this.silent = typeof options.silent !== 'undefined' ? options.silent : true; + // favour british interpretation of 02/02/2018, etc + this.british = options.dmy || options.british; + + //does the week start on sunday, or monday: + this._weekStart = 1; //default to monday + if (options.weekStart !== undefined) { + this._weekStart = options.weekStart; + } + // the reference today date object, (for testing) + this._today = {}; + if (options.today !== undefined) { + this._today = options.today; + } + // dunno if this is a good idea, or not + // Object.defineProperty(this, 'parsers', { + // enumerable: false, + // writable: true, + // value: parsers + // }) + //add getter/setters + Object.defineProperty(this, 'd', { + //return a js date object + get: function () { + let offset = quickOffset$1(this); + //every computer is somewhere- get this computer's built-in offset + let bias = new Date(this.epoch).getTimezoneOffset() || 0; + //movement + let shift = bias + offset * 60; //in minutes + shift = shift * 60 * 1000; //in ms + //remove this computer's offset + let epoch = this.epoch + shift; + let d = new Date(epoch); + return d } - } + }); + //add this data on the object, to allow adding new timezones + Object.defineProperty(this, 'timezones', { + get: () => timezones$1, + set: (obj) => { + timezones$1 = obj; + return obj + } + }); + //parse the various formats + let tmp = handleInput(this, input); + this.epoch = tmp.epoch; + }; - // in half an hour - m = shift.match('half (a|an) [#Duration]', 0); - if (m.found) { - let unit = parseUnit(m); - result[unit] = 0.5; - } - // finally, remove it from our text - m = doc.match('#DateShift+'); - return { result, m } + //(add instance methods to prototype) + Object.keys(methods$5).forEach((k) => { + SpaceTime.prototype[k] = methods$5[k]; + }); + + // ¯\_(ツ)_/¯ + SpaceTime.prototype.clone = function () { + return new SpaceTime(this.epoch, this.tz, { + silent: this.silent, + weekStart: this._weekStart, + today: this._today, + parsers: this.parsers + }) }; - /* - a 'counter' is a Unit determined after a point - * first hour of x - * 7th week in x - * last year in x - * - unlike a shift, like "2 weeks after x" - */ - const oneBased = { - minute: true, + /** + * @deprecated use toNativeDate() + * @returns native date object at the same epoch + */ + SpaceTime.prototype.toLocalDate = function () { + return this.toNativeDate() }; - const getCounter = function (doc) { - // 7th week of - let m = doc.match('[#Value] [#Duration+] (of|in)'); - if (m.found) { - let obj = m.groups(); - let num = obj.num.numbers().get()[0]; - let unit = obj.unit.text('reduced'); - let result = { - unit: unit, - num: Number(num) || 0, - }; - // 0-based or 1-based units - if (!oneBased[unit]) { - result.num -= 1; - } - return { result, m } + /** + * @returns native date object at the same epoch + */ + SpaceTime.prototype.toNativeDate = function () { + return new Date(this.epoch) + }; + + //append more methods + queryFns(SpaceTime); + addFns(SpaceTime); + sameFns(SpaceTime); + compareFns(SpaceTime); + i18nFns(SpaceTime); + + var Spacetime = SpaceTime; + + // const timezones = require('../data'); + + const whereIts = (a, b) => { + let start = new Spacetime(null); + let end = new Spacetime(null); + start = start.time(a); + //if b is undefined, use as 'within one hour' + if (b) { + end = end.time(b); + } else { + end = start.add(59, 'minutes'); } - // first week of - m = doc.match('[(first|initial|last|final)] [#Duration+] (of|in)'); - if (m.found) { - let obj = m.groups(); - let dir = obj.dir.text('reduced'); - let unit = obj.unit.text('reduced'); - if (dir === 'initial') { - dir = 'first'; + + let startHour = start.hour(); + let endHour = end.hour(); + let tzs = Object.keys(start.timezones).filter((tz) => { + if (tz.indexOf('/') === -1) { + return false } - if (dir === 'final') { - dir = 'last'; + let m = new Spacetime(null, tz); + let hour = m.hour(); + //do 'calendar-compare' not real-time-compare + if (hour >= startHour && hour <= endHour) { + //test minutes too, if applicable + if (hour === startHour && m.minute() < start.minute()) { + return false + } + if (hour === endHour && m.minute() > end.minute()) { + return false + } + return true } - let result = { - unit: unit, - dir: dir, - }; - return { result, m } - } + return false + }); + return tzs + }; + var whereIts$1 = whereIts; - return { result: null, m: doc.none() } + var version = '7.1.2'; + + const main = (input, tz, options) => new Spacetime(input, tz, options); + + // set all properties of a given 'today' object + const setToday = function (s) { + let today = s._today || {}; + Object.keys(today).forEach((k) => { + s = s[k](today[k]); + }); + return s + }; + + //some helper functions on the main method + main.now = (tz, options) => { + let s = new Spacetime(new Date().getTime(), tz, options); + s = setToday(s); + return s + }; + main.today = (tz, options) => { + let s = new Spacetime(new Date().getTime(), tz, options); + s = setToday(s); + return s.startOf('day') + }; + main.tomorrow = (tz, options) => { + let s = new Spacetime(new Date().getTime(), tz, options); + s = setToday(s); + return s.add(1, 'day').startOf('day') + }; + main.yesterday = (tz, options) => { + let s = new Spacetime(new Date().getTime(), tz, options); + s = setToday(s); + return s.subtract(1, 'day').startOf('day') + }; + main.extend = function (obj = {}) { + Object.keys(obj).forEach((k) => { + Spacetime.prototype[k] = obj[k]; + }); + return this }; + main.timezones = function () { + let s = new Spacetime(); + return s.timezones + }; + main.max = function (tz, options) { + let s = new Spacetime(null, tz, options); + s.epoch = 8640000000000000; + return s + }; + main.min = function (tz, options) { + let s = new Spacetime(null, tz, options); + s.epoch = -8640000000000000; + return s + }; + + //find tz by time + main.whereIts = whereIts$1; + main.version = version; + + //aliases: + main.plugin = main.extend; + var spacetime = main; const hardCoded = { daybreak: '7:00am', //ergh @@ -326,7 +4389,7 @@ time = time.not('^(at|by|for|before|this|after)'); time = time.not('sharp'); time = time.not('on the dot'); - let s = spacetime__default["default"].now(context.timezone); + let s = spacetime.now(context.timezone); let now = s.clone(); // check for known-times (like 'today') let timeStr = time.text('reduced'); @@ -363,7 +4426,7 @@ m = time.match('[(half|quarter|25|20|15|10|5)] (past|after)'); if (m.found) { let min = m.groups('min').text('reduced'); - let d = spacetime__default["default"](context.today); + let d = spacetime(context.today); // support 'quarter', etc. if (minMap.hasOwnProperty(min)) { min = minMap[min]; @@ -377,7 +4440,7 @@ m = time.match('[(half|quarter|25|20|15|10|5)] to'); if (m.found) { let min = m.groups('min').text('reduced'); - let d = spacetime__default["default"](context.today); + let d = spacetime(context.today); // support 'quarter', etc. if (minMap.hasOwnProperty(min)) { min = minMap[min]; @@ -460,6 +4523,7 @@ } return { result: null, m: doc.none() } }; + var parseTime$1 = parseTime; // interpret 'this halloween' or 'next june' const parseRelative = function (doc) { @@ -489,6 +4553,7 @@ } return { result: null, m: doc.none() } }; + var doRelative = parseRelative; // 'start of october', 'middle of june 1st' const parseSection = function (doc) { @@ -509,6 +4574,7 @@ } return { result: null, m } }; + var doSection = parseSection; // some opinionated-but-common-sense timezone abbreviations @@ -638,7 +4704,7 @@ ndt: america$1 + 'St_Johns', 'brazil time': america$1 + 'Sao_Paulo', brt: america$1 + 'Sao_Paulo', - brasília: america$1 + 'Sao_Paulo', + 'brasília': america$1 + 'Sao_Paulo', brasilia: america$1 + 'Sao_Paulo', 'brazilian time': america$1 + 'Sao_Paulo', 'argentina time': america$1 + 'Buenos_Aires', @@ -759,7 +4825,7 @@ }; //add the official iana zonefile names - let iana$1 = spacetime__default["default"]().timezones; + let iana$1 = spacetime().timezones; let formal$1 = Object.keys(iana$1).reduce((h, k) => { h[k] = k; return h @@ -823,6 +4889,7 @@ return { result: null, m: doc.none() } }; + var doTimezone = parseTimezone; // pull-out 'thurs' from 'thurs next week' const parseWeekday = function (doc) { @@ -836,6 +4903,7 @@ } return { result: null, m: doc.none() } }; + var doWeekday = parseWeekday; const cleanup = function (doc) { // 'the fifth week ..' @@ -853,37 +4921,37 @@ const tokenize = function (doc, context) { // parse 'two weeks after' - let res = parseShift(doc); + let res = doShift(doc); let shift = res.result; doc = doc.not(res.m); // parse 'nth week of june' - res = getCounter(doc); + res = doCounter(doc); let counter = res.result; doc = doc.not(res.m); // parse 'eastern time' - res = parseTimezone(doc); + res = doTimezone(doc); let tz = res.result; doc = doc.not(res.m); // parse '2pm' - res = parseTime(doc, context); + res = parseTime$1(doc, context); let time = res.result; doc = doc.not(res.m); // parse 'tuesday' - res = parseWeekday(doc); + res = doWeekday(doc, context); let weekDay = res.result; doc = doc.not(res.m); // parse 'start of x' - res = parseSection(doc); + res = doSection(doc, context); let section = res.result; doc = doc.not(res.m); // parse 'next x' - res = parseRelative(doc); + res = doRelative(doc); let rel = res.result; doc = doc.not(res.m); @@ -901,6 +4969,7 @@ doc } }; + var tokenize$1 = tokenize; class Unit { constructor(input, unit, context) { @@ -916,7 +4985,7 @@ }; } // set it to the beginning of the given unit - let d = spacetime__default["default"](input, context.timezone, { today: today }); + let d = spacetime(input, context.timezone, { today: today }); Object.defineProperty(this, 'd', { enumerable: false, writable: true, @@ -1076,8 +5145,9 @@ return this } } + var Unit$1 = Unit; - class Day extends Unit { + class Day extends Unit$1 { constructor(input, unit, context) { super(input, unit, context); this.unit = 'day'; @@ -1121,7 +5191,7 @@ this.isWeekDay = true; //cool. // is the input just a weekday? if (typeof input === 'string') { - this.d = spacetime__default["default"](context.today, context.timezone); + this.d = spacetime(context.today, context.timezone); this.d = this.d.day(input); // assume a wednesday in the future if (this.d.isBefore(context.today)) { @@ -1195,7 +5265,7 @@ } } - class Hour extends Unit { + class Hour extends Unit$1 { constructor(input, unit, context) { super(input, unit, context, true); this.unit = 'hour'; @@ -1204,7 +5274,7 @@ } } } - class Minute extends Unit { + class Minute extends Unit$1 { constructor(input, unit, context) { super(input, unit, context, true); this.unit = 'minute'; @@ -1213,7 +5283,7 @@ } } } - class Moment extends Unit { + class Moment extends Unit$1 { constructor(input, unit, context) { super(input, unit, context, true); this.unit = 'millisecond'; @@ -1221,7 +5291,7 @@ } // a specific month, like 'March' - class AnyMonth extends Unit { + class AnyMonth extends Unit$1 { constructor(input, unit, context) { super(input, unit, context); this.unit = 'month'; @@ -1233,7 +5303,7 @@ } // a specific month, like 'March' - class Month extends Unit { + class Month extends Unit$1 { constructor(input, unit, context) { super(input, unit, context); this.unit = 'month'; @@ -1254,7 +5324,7 @@ } } - class AnyQuarter extends Unit { + class AnyQuarter extends Unit$1 { constructor(input, unit, context) { super(input, unit, context); this.unit = 'quarter'; @@ -1270,7 +5340,7 @@ } } - class Quarter extends Unit { + class Quarter extends Unit$1 { constructor(input, unit, context) { super(input, unit, context); this.unit = 'quarter'; @@ -1290,7 +5360,7 @@ return this } } - class Season extends Unit { + class Season extends Unit$1 { constructor(input, unit, context) { super(input, unit, context); this.unit = 'season'; @@ -1310,7 +5380,7 @@ return this } } - class Year extends Unit { + class Year extends Unit$1 { constructor(input, unit, context) { super(input, unit, context); this.unit = 'year'; @@ -1320,7 +5390,7 @@ } } - class Week extends Unit { + class Week extends Unit$1 { constructor(input, unit, context) { super(input, unit, context); this.unit = 'week'; @@ -1343,7 +5413,7 @@ } //may need some work - class WeekEnd extends Unit { + class WeekEnd extends Unit$1 { constructor(input, unit, context) { super(input, unit, context); this.unit = 'week'; @@ -1434,6 +5504,540 @@ } return unit }; + var today$1 = today; + + //yep, + const jan$1 = 'january'; + const feb$1 = 'february'; + const mar$1 = 'march'; + const apr = 'april'; + const may$1 = 'may'; + const jun$1 = 'june'; + const jul = 'july'; + const aug = 'august'; + const sep$1 = 'september'; + const oct$1 = 'october'; + const nov$1 = 'november'; + const dec = 'december'; + + var fixed = { + 'new years eve': [dec, 31], + 'new years': [jan$1, 1], + 'new years day': [jan$1, 1], + 'inauguration day': [jan$1, 20], + 'australia day': [jan$1, 26], + 'national freedom day': [feb$1, 1], + 'groundhog day': [feb$1, 2], + 'rosa parks day': [feb$1, 4], + 'valentines day': [feb$1, 14], + 'saint valentines day': [feb$1, 14], + 'st valentines day ': [feb$1, 14], + 'saint patricks day': [mar$1, 17], + 'st patricks day': [mar$1, 17], + 'april fools': [apr, 1], + 'april fools day': [apr, 1], + 'emancipation day': [apr, 16], + 'tax day': [apr, 15], //US + 'labour day': [may$1, 1], + 'cinco de mayo': [may$1, 5], + 'national nurses day': [may$1, 6], + 'harvey milk day': [may$1, 22], + 'victoria day': [may$1, 24], + juneteenth: [jun$1, 19], + 'canada day': [jul, 1], + 'independence day': [jul, 4], + 'independents day': [jul, 4], + 'bastille day': [jul, 14], + 'purple heart day': [aug, 7], + 'womens equality day': [aug, 26], + '16 de septiembre': [sep$1, 16], + 'dieciseis de septiembre': [sep$1, 16], + 'grito de dolores': [sep$1, 16], + halloween: [oct$1, 31], + 'all hallows eve': [oct$1, 31], + 'day of the dead': [oct$1, 31], // Ranged holiday [nov, 2], + 'dia de muertos': [oct$1, 31], // Ranged holiday [nov, 2], + 'veterans day': [nov$1, 11], + 'st andrews day': [nov$1, 30], + 'saint andrews day': [nov$1, 30], + 'all saints day': [nov$1, 1], + 'all sts day': [nov$1, 1], + 'armistice day': [nov$1, 11], + 'rememberance day': [nov$1, 11], + 'christmas eve': [dec, 24], + christmas: [dec, 25], + xmas: [dec, 25], + 'boxing day': [dec, 26], + 'st stephens day': [dec, 26], + 'saint stephens day': [dec, 26], + + // Fixed religious and cultural holidays + // Catholic + Christian + epiphany: [jan$1, 6], + 'orthodox christmas day': [jan$1, 7], + 'orthodox new year': [jan$1, 14], + 'assumption of mary': [aug, 15], + 'all souls day': [nov$1, 2], + 'feast of the immaculate conception': [dec, 8], + 'feast of our lady of guadalupe': [dec, 12], + + // Kwanzaa + kwanzaa: [dec, 26], // Ranged holiday [jan, 1], + + // Pagan / metal 🤘 + imbolc: [feb$1, 2], + beltaine: [may$1, 1], + lughnassadh: [aug, 1], + samhain: [oct$1, 31] + }; + + // holidays that are the same date every year + const fixedDates$1 = function (str, normal, year, tz) { + if (fixed.hasOwnProperty(str) || fixed.hasOwnProperty(normal)) { + let arr = fixed[str] || fixed[normal] || []; + let s = spacetime.now(tz); + s = s.year(year); + s = s.startOf('year'); + s = s.month(arr[0]); + s = s.date(arr[1]); + if (s.isValid()) { + return s + } + } + return null + }; + var fixedDates$2 = fixedDates$1; + + //these are holidays on the 'nth weekday of month' + const jan = 'january'; + const feb = 'february'; + const mar = 'march'; + // const apr = 'april' + const may = 'may'; + const jun = 'june'; + // const jul = 'july' + // const aug = 'august' + const sep = 'september'; + const oct = 'october'; + const nov = 'november'; + // const dec = 'december' + + const mon = 'monday'; + // const tues = 'tuesday' + // const wed = 'wednesday' + const thurs = 'thursday'; + const fri = 'friday'; + // const sat = 'saturday' + const sun = 'sunday'; + + let holidays$3 = { + 'martin luther king day': [3, mon, jan], //[third monday in january], + 'presidents day': [3, mon, feb], //[third monday in february], + + 'commonwealth day': [2, mon, mar], //[second monday in march], + 'mothers day': [2, sun, may], //[second Sunday in May], + 'fathers day': [3, sun, jun], //[third Sunday in June], + 'labor day': [1, mon, sep], //[first monday in september], + 'columbus day': [2, mon, oct], //[second monday in october], + 'canadian thanksgiving': [2, mon, oct], //[second monday in october], + thanksgiving: [4, thurs, nov], // [fourth Thursday in November], + 'black friday': [4, fri, nov] //[fourth friday in november], + + // 'memorial day': [may], //[last monday in may], + // 'us election': [nov], // [Tuesday following the first Monday in November], + // 'cyber monday': [nov] + // 'advent': [] // fourth Sunday before Christmas + }; + + // add aliases + holidays$3['turday day'] = holidays$3.thanksgiving; + holidays$3['indigenous peoples day'] = holidays$3['columbus day']; + holidays$3['mlk day'] = holidays$3['martin luther king day']; + var calendar = holidays$3; + + // holidays that are the same date every year + const fixedDates = function (str, normal, year, tz) { + if (calendar.hasOwnProperty(str) || calendar.hasOwnProperty(normal)) { + let arr = calendar[str] || calendar[normal] || []; + let s = spacetime.now(tz); + s = s.year(year); + + // [3rd, 'monday', 'january'] + s = s.month(arr[2]); + s = s.startOf('month'); + // make it january + let month = s.month(); + + // make it the 1st monday + s = s.day(arr[1]); + if (s.month() !== month) { + s = s.add(1, 'week'); + } + // make it nth monday + if (arr[0] > 1) { + s = s.add(arr[0] - 1, 'week'); + } + if (s.isValid()) { + return s + } + } + + return null + }; + var nthWeekday = fixedDates; + + // https://www.timeanddate.com/calendar/determining-easter-date.html + + let dates$2 = { + easter: 0, + 'ash wednesday': -46, // (46 days before easter) + 'palm sunday': 7, // (1 week before easter) + 'maundy thursday': -3, // (3 days before easter) + 'good friday': -2, // (2 days before easter) + 'holy saturday': -1, // (1 days before easter) + 'easter saturday': -1, // (1 day before easter) + 'easter monday': 1, // (1 day after easter) + 'ascension day': 39, // (39 days after easter) + 'whit sunday': 49, // / pentecost (49 days after easter) + 'whit monday': 50, // (50 days after easter) + 'trinity sunday': 65, // (56 days after easter) + 'corpus christi': 60, // (60 days after easter) + + 'mardi gras': -47 //(47 days before easter) + }; + dates$2['easter sunday'] = dates$2.easter; + dates$2.pentecost = dates$2['whit sunday']; + dates$2.whitsun = dates$2['whit sunday']; + + var holidays$2 = dates$2; + + // by John Dyer + // based on the algorithm by Oudin (1940) from http://www.tondering.dk/claus/cal/easter.php + const calcEaster = function (year) { + let f = Math.floor, + // Golden Number - 1 + G = year % 19, + C = f(year / 100), + // related to Epact + H = (C - f(C / 4) - f((8 * C + 13) / 25) + 19 * G + 15) % 30, + // number of days from 21 March to the Paschal full moon + I = H - f(H / 28) * (1 - f(29 / (H + 1)) * f((21 - G) / 11)), + // weekday for the Paschal full moon + J = (year + f(year / 4) + I + 2 - C + f(C / 4)) % 7, + // number of days from 21 March to the Sunday on or before the Paschal full moon + L = I - J, + month = 3 + f((L + 40) / 44), + date = L + 28 - 31 * f(month / 4); + + month = month === 4 ? 'April' : 'March'; + return month + ' ' + date + }; + + var calcEaster$1 = calcEaster; + + //calculate any holidays based on easter + const easterDates = function (str, normal, year, tz) { + if (holidays$2.hasOwnProperty(str) || holidays$2.hasOwnProperty(normal)) { + let days = holidays$2[str] || holidays$2[normal] || []; + + let date = calcEaster$1(year); + if (!date) { + return null //no easter for this year + } + let e = spacetime(date, tz); + e = e.year(year); + + let s = e.add(days, 'day'); + if (s.isValid()) { + return s + } + } + return null + }; + var easterDates$1 = easterDates; + + // http://www.astropixels.com/ephemeris/soleq2001.html + + // years 2000-2100 + const exceptions = { + spring: [ + 2003, + 2007, + 2044, + 2048, + 2052, + 2056, + 2060, + 2064, + 2068, + 2072, + 2076, + 2077, + 2080, + 2081, + 2084, + 2085, + 2088, + 2089, + 2092, + 2093, + 2096, + 2097 + ], + summer: [ + 2021, + 2016, + 2020, + 2024, + 2028, + 2032, + 2036, + 2040, + 2041, + 2044, + 2045, + 2048, + 2049, + 2052, + 2053, + 2056, + 2057, + 2060, + 2061, + 2064, + 2065, + 2068, + 2069, + 2070, + 2072, + 2073, + 2074, + 2076, + 2077, + 2078, + 2080, + 2081, + 2082, + 2084, + 2085, + 2086, + 2088, + 2089, + 2090, + 2092, + 2093, + 2094, + 2096, + 2097, + 2098, + 2099 + ], + fall: [ + 2002, + 2003, + 2004, + 2006, + 2007, + 2010, + 2011, + 2014, + 2015, + 2018, + 2019, + 2022, + 2023, + 2026, + 2027, + 2031, + 2035, + 2039, + 2043, + 2047, + 2051, + 2055, + 2059, + 2092, + 2096 + ], + winter: [ + 2002, + 2003, + 2006, + 2007, + 2011, + 2015, + 2019, + 2023, + 2027, + 2031, + 2035, + 2039, + 2043, + 2080, + 2084, + 2088, + 2092, + 2096 + ] + }; + + const winter20th = [2080, 2084, 2088, 2092, 2096]; + + const calcSeasons = function (year) { + // most common defaults + let res = { + spring: 'March 20 ' + year, + summer: 'June 21 ' + year, + fall: 'Sept 22 ' + year, + winter: 'Dec 21 ' + year + }; + if (exceptions.spring.indexOf(year) !== -1) { + res.spring = 'March 19 ' + year; + } + if (exceptions.summer.indexOf(year) !== -1) { + res.summer = 'June 20 ' + year; + } + if (exceptions.fall.indexOf(year) !== -1) { + res.fall = 'Sept 21 ' + year; + } + // winter can be 20th, 21st, or 22nd + if (exceptions.winter.indexOf(year) !== -1) { + res.winter = 'Dec 22 ' + year; + } + if (winter20th.indexOf(year) !== -1) { + res.winter = 'Dec 20 ' + year; + } + return res + }; + var calcSeasons$1 = calcSeasons; + + // these are properly calculated in ./lib/seasons + let dates$1$1 = { + 'spring equinox': 'spring', + 'summer solistice': 'summer', + 'fall equinox': 'fall', + 'winter solstice': 'winter' + }; + + // aliases + dates$1$1['march equinox'] = dates$1$1['spring equinox']; + dates$1$1['vernal equinox'] = dates$1$1['spring equinox']; + dates$1$1['ostara'] = dates$1$1['spring equinox']; + + dates$1$1['june solstice'] = dates$1$1['summer solistice']; + dates$1$1['litha'] = dates$1$1['summer solistice']; + + dates$1$1['autumn equinox'] = dates$1$1['fall equinox']; + dates$1$1['autumnal equinox'] = dates$1$1['fall equinox']; + dates$1$1['september equinox'] = dates$1$1['fall equinox']; + dates$1$1['sept equinox'] = dates$1$1['fall equinox']; + dates$1$1['mabon'] = dates$1$1['fall equinox']; + + dates$1$1['december solstice'] = dates$1$1['winter solistice']; + dates$1$1['dec solstice'] = dates$1$1['winter solistice']; + dates$1$1['yule'] = dates$1$1['winter solistice']; + + var holidays$1 = dates$1$1; + + const astroDates = function (str, normal, year, tz) { + if (holidays$1.hasOwnProperty(str) || holidays$1.hasOwnProperty(normal)) { + let season = holidays$1[str] || holidays$1[normal]; + let seasons = calcSeasons$1(year); + if (!season || !seasons || !seasons[season]) { + return null // couldn't figure it out + } + let s = spacetime(seasons[season], tz); + if (s.isValid()) { + return s + } + } + + return null + }; + var astroDates$1 = astroDates; + + let dates$3 = { + // Muslim holidays + 'isra and miraj': 'april 13', + 'lailat al-qadr': 'june 10', + 'eid al-fitr': 'june 15', + 'id al-Fitr': 'june 15', + 'eid ul-Fitr': 'june 15', + ramadan: 'may 16', // Range holiday + 'eid al-adha': 'sep 22', + muharram: 'sep 12', + 'prophets birthday': 'nov 21' + }; + var holidays$4 = dates$3; + + // (lunar year is 354.36 days) + const dayDiff = -10.64; + + const lunarDates = function (str, normal, year, tz) { + if (holidays$4.hasOwnProperty(str) || holidays$4.hasOwnProperty(normal)) { + let date = holidays$4[str] || holidays$4[normal] || []; + if (!date) { + return null + } + // start at 2018 + let s = spacetime(date + ' 2018', tz); + let diff = year - 2018; + let toAdd = diff * dayDiff; + s = s.add(toAdd, 'day'); + s = s.startOf('day'); + + // now set the correct year + s = s.year(year); + + if (s.isValid()) { + return s + } + } + return null + }; + var lunarDates$1 = lunarDates; + + const nowYear = spacetime.now().year(); + + const spacetimeHoliday = function (str, year, tz) { + year = year || nowYear; + str = str || ''; + str = String(str); + str = str.trim().toLowerCase(); + str = str.replace(/'s/, 's'); // 'mother's day' + + let normal = str.replace(/ day$/, ''); + normal = normal.replace(/^the /, ''); + normal = normal.replace(/^orthodox /, ''); //orthodox good friday + + // try easier, unmoving holidays + let s = fixedDates$2(str, normal, year, tz); + if (s !== null) { + return s + } + // try 'nth monday' holidays + s = nthWeekday(str, normal, year, tz); + if (s !== null) { + return s + } + // easter-based holidays + s = easterDates$1(str, normal, year, tz); + if (s !== null) { + return s + } + // solar-based holidays + s = astroDates$1(str, normal, year, tz); + if (s !== null) { + return s + } + // mostly muslim holidays + s = lunarDates$1(str, normal, year, tz); + if (s !== null) { + return s + } + + return null + }; const parseHoliday = function (doc, context) { let unit = null; @@ -1443,16 +6047,17 @@ year = Number(m.groups('year').text('reduced')) || year; } let str = m.groups('holiday').text('reduced'); - let s = spacetimeHoliday__default["default"](str, year, context.timezone); + let s = spacetimeHoliday(str, year, context.timezone); if (s !== null) { // assume the year in the future.. if (s.isBefore(context.today) && year === context.today.year()) { - s = spacetimeHoliday__default["default"](str, year + 1, context.timezone); + s = spacetimeHoliday(str, year + 1, context.timezone); } unit = new Holiday(s, null, context); } return unit }; + var holiday = parseHoliday; const mapping$1 = { day: Day, @@ -1510,6 +6115,7 @@ // } return null }; + var nextLast$1 = nextLast; const fmtToday = function (context) { return { @@ -1524,7 +6130,7 @@ let m = doc.match('(spring|summer|winter|fall|autumn) [#Year?]'); if (m.found) { let str = doc.text('reduced'); - let s = spacetime__default["default"](str, context.timezone, { today: fmtToday(context) }); + let s = spacetime(str, context.timezone, { today: fmtToday(context) }); let unit = new Season(s, null, context); if (unit.d.isValid() === true) { return unit @@ -1535,7 +6141,7 @@ m = doc.match('[#FinancialQuarter] [#Year?]'); if (m.found) { let str = m.groups('q').text('reduced'); - let s = spacetime__default["default"](str, context.timezone, { today: fmtToday(context) }); + let s = spacetime(str, context.timezone, { today: fmtToday(context) }); if (m.groups('year')) { let year = Number(m.groups('year').text()) || context.today.year(); s = s.year(year); @@ -1549,7 +6155,7 @@ m = doc.match('[#Value] quarter (of|in)? [#Year?]'); if (m.found) { let q = m.groups('q').text('reduced'); - let s = spacetime__default["default"](`q${q}`, context.timezone, { today: fmtToday(context) }); + let s = spacetime(`q${q}`, context.timezone, { today: fmtToday(context) }); if (m.groups('year')) { let year = Number(m.groups('year').text()) || context.today.year(); s = s.year(year); @@ -1563,7 +6169,7 @@ m = doc.match('^#Year$'); if (m.found) { let str = doc.text('reduced'); - let s = spacetime__default["default"](null, context.timezone, { today: fmtToday(context) }); + let s = spacetime(null, context.timezone, { today: fmtToday(context) }); s = s.year(str); let unit = new Year(s, null, context); if (unit.d.isValid() === true) { @@ -1573,6 +6179,7 @@ return null }; + var yearly = parseYearly; // parse things like 'june 5th 2019' // most of this is done in spacetime @@ -1703,22 +6310,24 @@ } return unit }; + var explicit = parseExplicit; - const parse$3 = function (doc, context, parts) { + const parse$4 = function (doc, context, parts) { let unit = null; //'in two days' - unit = unit || today(doc, context, parts); + unit = unit || today$1(doc, context, parts); // 'this haloween' - unit = unit || parseHoliday(doc, context); + unit = unit || holiday(doc, context); // 'this month' - unit = unit || nextLast(doc, context); + unit = unit || nextLast$1(doc, context); // 'q2 2002' - unit = unit || parseYearly(doc, context); + unit = unit || yearly(doc, context); // 'this june 2nd' - unit = unit || parseExplicit(doc, context); + unit = unit || explicit(doc, context); return unit }; + var parse$5 = parse$4; const units = { day: Day, @@ -1761,6 +6370,7 @@ } return unit //fallback }; + var addCounter = applyCounter; // apply all parsed {parts} to our unit date const transform = function (unit, context, parts) { @@ -1805,10 +6415,11 @@ } // apply counter if (parts.counter && parts.counter.unit) { - unit = applyCounter(unit, parts.counter); + unit = addCounter(unit, parts.counter); } return unit }; + var transform$1 = transform; // import spacetime from 'spacetime' @@ -1827,7 +6438,7 @@ const parseDate = function (doc, context) { //parse-out any sections - let parts = tokenize(doc, context); + let parts = tokenize$1(doc, context); doc = parts.doc; // logger log$1(parts); @@ -1840,11 +6451,12 @@ context.today = context.today.goto(context.timezone).set(iso); } // decide on a root date object - let unit = parse$3(doc, context, parts); + let unit = parse$5(doc, context, parts); // apply all our parts - unit = transform(unit, context, parts); + unit = transform$1(unit, context, parts); return unit }; + var parseDate$1 = parseDate; const dayNames = { mon: 'monday', @@ -1959,13 +6571,14 @@ doc = doc.remove(m); let time = m.groups('time'); if (time.found) { - repeat.time = parseTime(time, context); + repeat.time = parseTime$1(time, context); } return { repeat: repeat } } } return null }; + var repeating = parseIntervals; // somewhat-intellegent response to end-before-start situations const reverseMaybe = function (obj) { @@ -1984,6 +6597,7 @@ } return obj }; + var reverseMaybe$1 = reverseMaybe; const moveToPM = function (obj) { let start = obj.start; @@ -2004,7 +6618,7 @@ parse: (m, context) => { let from = m.groups('from'); let to = m.groups('to'); - let end = parseDate(to, context); + let end = parseDate$1(to, context); if (end) { let start = end.clone(); start.applyTime(from.text('implicit')); @@ -2017,7 +6631,7 @@ if (/(am|pm)/.test(to) === false) { obj = moveToPM(obj); } - obj = reverseMaybe(obj); + obj = reverseMaybe$1(obj); return obj } } @@ -2033,7 +6647,7 @@ let main = m.groups('main'); let a = m.groups('a'); let b = m.groups('b'); - main = parseDate(main, context); + main = parseDate$1(main, context); if (main) { main.applyTime(a.text('implicit')); let end = main.clone(); @@ -2047,7 +6661,7 @@ if (/(am|pm)/.test(b.text()) === false) { obj = moveToPM(obj); } - obj = reverseMaybe(obj); + obj = reverseMaybe$1(obj); return obj } } @@ -2061,7 +6675,7 @@ parse: (m, context) => { let from = m.groups('from'); let to = m.groups('to'); - from = parseDate(from, context); + from = parseDate$1(from, context); if (from) { let end = from.clone(); end.applyTime(to.text('implicit')); @@ -2074,7 +6688,7 @@ if (/(am|pm)/.test(to.text()) === false) { obj = moveToPM(obj); } - obj = reverseMaybe(obj); + obj = reverseMaybe$1(obj); return obj } } @@ -2093,7 +6707,7 @@ parse: (m, context) => { let before = m.match('^during? [#Month]', 0); m = m.not('(or|and)'); - let start = parseDate(before, context); + let start = parseDate$1(before, context); if (start) { let result = [ { @@ -2106,7 +6720,7 @@ let more = m.not(before); if (more.found) { more.match('#Month').forEach((month) => { - let s = parseDate(month, context); + let s = parseDate$1(month, context); // s.d = s.d.month(month.text('reduced')) result.push({ start: s, @@ -2136,7 +6750,7 @@ parse: (m, context) => { m = m.not('(or|and)'); let before = m.match('^#Month #Value'); - let start = parseDate(before, context); + let start = parseDate$1(before, context); if (start) { let result = [ { @@ -2178,7 +6792,7 @@ if (year.found) { d.append(year); } - let start = parseDate(d, context); + let start = parseDate$1(d, context); if (start) { results.push({ start: start, @@ -2204,7 +6818,7 @@ if (year.found) { d.append(year); } - let start = parseDate(d, context); + let start = parseDate$1(d, context); if (start) { results.push({ start: start, @@ -2224,8 +6838,8 @@ parse: (m, context) => { let fromDoc = m.groups('from'); let toDoc = m.groups('to'); - let from = parseDate(fromDoc, context); - let to = parseDate(toDoc, context); + let from = parseDate$1(fromDoc, context); + let to = parseDate$1(toDoc, context); if (from && to) { return [ { @@ -2250,9 +6864,9 @@ desc: 'between friday and sunday', parse: (m, context) => { let start = m.groups('start'); - start = parseDate(start, context); + start = parseDate$1(start, context); let end = m.groups('end'); - end = parseDate(end, context); + end = parseDate$1(end, context); if (start && end) { end = end.before(); return { @@ -2274,13 +6888,13 @@ if (res.year) { start = start.append(res.year); } - start = parseDate(start, context); + start = parseDate$1(start, context); if (start) { let end = res.to; if (res.year) { end = end.append(res.year); } - end = parseDate(end, context); + end = parseDate$1(end, context); if (end) { // assume end is after start if (start.d.isAfter(end.d)) { @@ -2304,10 +6918,10 @@ let { month, from, to, year } = m.groups(); let year2 = year.clone(); let start = from.prepend(month).append(year); - start = parseDate(start, context); + start = parseDate$1(start, context); if (start) { let end = to.prepend(month).append(year2); - end = parseDate(end, context); + end = parseDate$1(end, context); return { start: start, end: end.end(), @@ -2322,7 +6936,7 @@ desc: '5 to 7 of january 1998', parse: (m, context) => { let to = m.groups('to'); - to = parseDate(to, context); + to = parseDate$1(to, context); if (to) { let fromDate = m.groups('from'); let from = to.clone(); @@ -2342,7 +6956,7 @@ desc: 'january 5 to 7', parse: (m, context) => { let from = m.groups('from'); - from = parseDate(from, context); + from = parseDate$1(from, context); if (from) { let toDate = m.groups('to'); let to = from.clone(); @@ -2364,8 +6978,8 @@ let from = m.groups('from'); let year = m.groups('year').numbers().get()[0]; let to = m.groups('to'); - from = parseDate(from, context); - to = parseDate(to, context); + from = parseDate$1(from, context); + to = parseDate$1(to, context); from.d = from.d.year(year); to.d = to.d.year(year); if (from && to) { @@ -2374,7 +6988,7 @@ end: to.end(), }; // reverse the order? - obj = reverseMaybe(obj); + obj = reverseMaybe$1(obj); return obj } return null @@ -2395,14 +7009,14 @@ parse: (m, context) => { let from = m.groups('from'); let to = m.groups('to'); - from = parseDate(from, context); - to = parseDate(to, context); + from = parseDate$1(from, context); + to = parseDate$1(to, context); if (from && to) { let obj = { start: from, end: to.end(), }; - obj = reverseMaybe(obj); + obj = reverseMaybe$1(obj); return obj } return null @@ -2415,9 +7029,9 @@ desc: 'before june', parse: (m, context) => { m = m.group(0); - let unit = parseDate(m, context); + let unit = parseDate$1(m, context); if (unit) { - let start = new Unit(context.today, null, context); + let start = new Unit$1(context.today, null, context); if (start.d.isAfter(unit.d)) { start = unit.clone().applyShift({ weeks: -2 }); } @@ -2438,7 +7052,7 @@ desc: 'in june', parse: (m, context) => { m = m.group(0); - let unit = parseDate(m, context); + let unit = parseDate$1(m, context); if (unit) { return { start: unit, end: unit.clone().end(), unit: unit.unit } } @@ -2451,7 +7065,7 @@ desc: 'after june', parse: (m, context) => { m = m.group(0); - let unit = parseDate(m, context); + let unit = parseDate$1(m, context); if (unit) { unit = unit.after(); return { @@ -2468,7 +7082,7 @@ desc: 'middle of', parse: (m, context) => { m = m.group(0); - let unit = parseDate(m, context); + let unit = parseDate$1(m, context); let start = unit.clone().middle(); let end = unit.beforeEnd(); if (unit) { @@ -2485,7 +7099,7 @@ match: '.+ after #Time+$', desc: 'tuesday after 5pm', parse: (m, context) => { - let unit = parseDate(m, context); + let unit = parseDate$1(m, context); if (unit) { let start = unit.clone(); let end = unit.end(); @@ -2503,7 +7117,7 @@ match: '.+ before #Time+$', desc: 'tuesday before noon', parse: (m, context) => { - let unit = parseDate(m, context); + let unit = parseDate$1(m, context); if (unit) { let end = unit.clone(); let start = unit.start(); @@ -2542,7 +7156,7 @@ if (!doc.found) { return res } - let unit = parseDate(doc, context); + let unit = parseDate$1(doc, context); if (unit) { let end = unit.clone().end(); res = { @@ -2578,7 +7192,7 @@ // loop thru each range template const parseRanges = function (m, context) { // parse-out 'every week ..' - let repeats = parseIntervals(m, context) || {}; + let repeats = repeating(m, context) || {}; // try picking-apart ranges let found = tryRanges(m, context); if (!found) { @@ -2594,6 +7208,7 @@ }); return found }; + var parseRange = parseRanges; const normalize = function (doc) { @@ -2621,20 +7236,23 @@ return doc }; - const parse$2 = function (doc, context) { + var normalize$1 = normalize; + + const parse$3 = function (doc, context) { // normalize context context = context || {}; if (context.timezone === false) { context.timezone = 'UTC'; } - context.today = context.today || spacetime__default["default"].now(context.timezone); - context.today = spacetime__default["default"](context.today, context.timezone); + context.today = context.today || spacetime.now(context.timezone); + context.today = spacetime(context.today, context.timezone); - doc = normalize(doc); + doc = normalize$1(doc); - let res = parseRanges(doc, context); + let res = parseRange(doc, context); return res }; + var parseDates = parse$3; const getDuration = function (range) { let end = range.end.d.add(1, 'millisecond'); @@ -2671,8 +7289,9 @@ // range: getRange(diff) } }; + var toJSON$1 = toJSON; - const api$2 = function (View) { + const api$3 = function (View) { class Dates extends View { constructor(document, pointer, groups, opts = {}) { @@ -2684,8 +7303,8 @@ get(n) { let all = []; this.forEach((m) => { - parse$2(m, this.opts).forEach(res => { - all.push(toJSON(res)); + parseDates(m, this.opts).forEach(res => { + all.push(toJSON$1(res)); }); }); if (typeof n === 'number') { @@ -2698,8 +7317,8 @@ return this.map(m => { let json = m.toView().json(opts)[0] || {}; if (opts && opts.dates !== true) { - let parsed = parse$2(m, this.opts); - json.dates = toJSON(parsed[0]); + let parsed = parseDates(m, this.opts); + json.dates = toJSON$1(parsed[0]); } return json }, []) @@ -2708,7 +7327,7 @@ format(fmt) { let found = this; let res = found.map(m => { - let obj = parse$2(m, this.opts)[0] || {}; + let obj = parseDates(m, this.opts)[0] || {}; if (obj.start) { let start = obj.start.d; let str = start.format(fmt); @@ -2727,11 +7346,13 @@ } View.prototype.dates = function (opts) { - let m = findDate(this); + let m = find$1(this); return new Dates(this.document, m.pointer, null, opts) }; }; + var dates$1 = api$3; + // import normalize from './normalize.js' @@ -2739,12 +7360,12 @@ return doc.match('#Time+ (am|pm)?') }; - const parse$1 = function (m, context = {}) { - let res = parseTime(m, context); + const parse$2 = function (m, context = {}) { + let res = parseTime$1(m, context); if (!res.result) { return { time: null, '24h': null } } - let s = spacetime__default["default"].now().time(res.result); + let s = spacetime.now().time(res.result); return { 'time': res.result, '24h': s.format('time-24'), @@ -2755,7 +7376,7 @@ const getNth = (doc, n) => (typeof n === 'number' ? doc.eq(n) : doc); - const api$1 = function (View) { + const api$2 = function (View) { class Times extends View { constructor(document, pointer, groups) { @@ -2764,14 +7385,14 @@ } get(n) { - return getNth(this, n).map(parse$1) + return getNth(this, n).map(parse$2) } json(opts = {}) { return this.map(m => { let json = m.toView().json(opts)[0] || {}; if (opts && opts.times !== true) { - json.time = parse$1(m); + json.time = parse$2(m); } return json }, []) @@ -2785,6 +7406,8 @@ }; }; + var times$1 = api$2; + const known = { century: true, day: true, @@ -2855,6 +7478,7 @@ } return duration }; + var parse$1 = parse; const addDurations = function (View) { /** phrases like '2 months', or '2mins' */ @@ -2868,7 +7492,7 @@ return this.map(m => { let json = m.toView().json(opts)[0] || {}; if (opts && opts.times !== true) { - json.duration = parse(m); + json.duration = parse$1(m); } return json }, []) @@ -2877,7 +7501,7 @@ get(options) { let arr = []; this.forEach((doc) => { - let res = parse(doc); + let res = parse$1(doc); arr.push(res); }); if (typeof options === 'number') { @@ -2902,12 +7526,14 @@ return new Durations(this.document, m.pointer) }; }; + var durations$1 = addDurations; const api = function (View) { - api$2(View); - api$1(View); - addDurations(View); + dates$1(View); + times$1(View); + durations$1(View); }; + var api$1 = api; //ambiguous 'may' and 'march' // const preps = '(in|by|before|during|on|until|after|of|within|all)' //6 @@ -3000,6 +7626,7 @@ return doc }; + var basic = tagDates; // 3-4 can be a time-range, sometimes const tagTimeRange = function (m, reason) { @@ -3062,6 +7689,7 @@ } return doc }; + var time = timeTagger; // timezone abbreviations // (from spencermountain/timezone-soft) @@ -3168,6 +7796,7 @@ } } }; + var timezone = tagTz; const here = 'fix-tagger'; // @@ -3199,6 +7828,7 @@ } return doc }; + var fixup = fixUp; const preps = '(in|by|before|during|on|until|after|of|within|all)'; //6 const thisNext = '(last|next|this|previous|current|upcoming|coming)'; //2 @@ -3443,6 +8073,7 @@ ]; + var matches$1 = matches; let byGroup = null; @@ -3450,7 +8081,7 @@ const doMatches = function (view) { let { document, world } = view; const { methods } = world; - byGroup = byGroup || methods.two.compile(matches, methods); + byGroup = byGroup || methods.two.compile(matches$1, methods); let found = methods.two.bulkMatch(document, byGroup, methods); // console.log(found.length, 'found') methods.two.bulkTagger(found, document, world); @@ -3460,10 +8091,10 @@ const compute = function (view) { view.cache(); doMatches(view); - tagDates(view); - timeTagger(view); - tagTz(view); - fixUp(view); + basic(view); + time(view); + timezone(view); + fixup(view); view.uncache(); return view }; @@ -3683,7 +8314,7 @@ }; //add the official iana zonefile names - let iana = spacetime__default["default"]().timezones; + let iana = spacetime().timezones; let formal = Object.keys(iana).reduce((h, k) => { h[k] = k; return h @@ -3935,6 +8566,8 @@ add(durations, 'Duration'); add(holidays, 'Holiday'); add(times, 'Time'); + // console.log(lex['april fools']) + var words = lex; var regex = [ // 30sec @@ -3952,9 +8585,9 @@ var plugin = { tags, - words: lex, + words, compute: compute$1, - api, + api: api$1, mutate: (world) => { world.model.two.regexNormal = world.model.two.regexNormal.concat(regex); }, diff --git a/plugins/dates/builds/compromise-dates.min.js b/plugins/dates/builds/compromise-dates.min.js index 3d769ee38..04ca0a333 100644 --- a/plugins/dates/builds/compromise-dates.min.js +++ b/plugins/dates/builds/compromise-dates.min.js @@ -1 +1 @@ -var t,e;t=this,e=function(t,e){function a(t){return t&&"object"==typeof t&&"default"in t?t:{default:t}}var n=a(t),r=a(e);const i={second:!0,minute:!0,hour:!0,day:!0,week:!0,weekend:!0,month:!0,season:!0,quarter:!0,year:!0},s={wk:"week",min:"minute",sec:"second",weekend:"week"},o=function(t){let e=t.match("#Duration").text("normal");return e=e.replace(/s$/,""),s.hasOwnProperty(e)&&(e=s[e]),e},u={minute:!0},d={daybreak:"7:00am",breakfast:"8:00am",morning:"9:00am",noon:"12:00pm",midday:"12:00pm",afternoon:"2:00pm",lunchtime:"12:00pm",evening:"6:00pm",dinnertime:"6:00pm",night:"8:00pm",eod:"10:00pm",midnight:"12:00am"},m={quarter:15,half:30},l=function(t){let e=t.time("6:00am");return t.isBefore(e)?t.ampm("pm"):t},h=function(t,e){let a=t.match("(at|by|for|before|this|after)? #Time+");a=a.not("^(at|by|for|before|this|after)"),a=a.not("sharp"),a=a.not("on the dot");let r=n.default.now(e.timezone),i=r.clone(),s=a.text("reduced");if(d.hasOwnProperty(s))return{result:d[s],m:a};let o=a.match("^#Cardinal oclock (am|pm)?");if(o.found&&(r=r.hour(o.text("reduced")),r=r.startOf("hour"),r.isValid()&&!r.isEqual(i))){let t=o.match("(am|pm)");return r=t.found?r.ampm(t.text("reduced")):l(r),{result:r.time(),m:o}}if(o=a.match("(half|quarter|25|20|15|10|5) (past|after|to) #Cardinal"),o.found&&(r=function(t,e){let a=t.match("#Cardinal$"),n=t.not(a).match("(half|quarter|25|20|15|10|5)");a=a.text("reduced");let r=n.text("reduced");m.hasOwnProperty(r)&&(r=m[r]);let i=t.has("to");return e=(e=e.hour(a)).startOf("hour"),a<6&&(e=e.ampm("pm")),i?e.subtract(r,"minutes"):e.add(r,"minutes")}(o,r),r.isValid()&&!r.isEqual(i)))return r=l(r),{result:r.time(),m:o};if(o=a.match("[(half|quarter|25|20|15|10|5)] (past|after)"),o.found){let t=o.groups("min").text("reduced"),a=n.default(e.today);if(m.hasOwnProperty(t)&&(t=m[t]),a=a.next("hour").startOf("hour").minute(t),a.isValid()&&!a.isEqual(i))return{result:a.time(),m:o}}if(o=a.match("[(half|quarter|25|20|15|10|5)] to"),o.found){let t=o.groups("min").text("reduced"),a=n.default(e.today);if(m.hasOwnProperty(t)&&(t=m[t]),a=a.next("hour").startOf("hour").minus(t,"minutes"),a.isValid()&&!a.isEqual(i))return{result:a.time(),m:o}}if(o=a.match("[