diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3c3629e --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +node_modules diff --git a/.jshintignore b/.jshintignore new file mode 100644 index 0000000..3c3629e --- /dev/null +++ b/.jshintignore @@ -0,0 +1 @@ +node_modules diff --git a/.jshintrc b/.jshintrc new file mode 100644 index 0000000..997b3f7 --- /dev/null +++ b/.jshintrc @@ -0,0 +1,10 @@ +{ + "node": true, + + "curly": true, + "latedef": true, + "quotmark": true, + "undef": true, + "unused": true, + "trailing": true +} diff --git a/.npmignore b/.npmignore new file mode 100644 index 0000000..fcc2a01 --- /dev/null +++ b/.npmignore @@ -0,0 +1,3 @@ +.gitignore +.jshintignore +.jshintrc diff --git a/ambit.js b/ambit.js new file mode 100644 index 0000000..3c06a2b --- /dev/null +++ b/ambit.js @@ -0,0 +1,194 @@ +var moment = require('moment'); + +/* + * Remove deprication warning since we're literally in the one + * use case they describe as valid for doing this here: + * https://github.com/moment/moment/issues/1407 + */ +moment.createFromInputFallback = function (config) { + config._d = new Date(config._i); +}; + +var SEASONS = { + spring: [2, 20, 0, 5, 19, 0], + summer: [5, 20, 0, 8, 23, 0], + fall: [8, 23, 0, 11, 21, 0], + autumn: [8, 23, 0, 11, 21, 0], + winter: [11, 21, 0, 2, 19, 1] +}; + +var MONTHS = ['jan', 'feb', 'mar', 'apr', 'may', 'jun', 'jul', 'aug', 'sep', 'oct', 'nov', 'dec']; + +var YEAR_MATCH = /^(20)?[0-9]{2}$/; + +function getSeason(now, season, year) { + var startDate, endDate; + var boundaries = SEASONS[season]; + if (!year) { + year = new Date().getFullYear(); + endDate = new Date(Number(year) + boundaries[5], boundaries[3], boundaries[4]); + if (endDate <= now) { + year = year + 1; + } + } + startDate = new Date(Number(year) + boundaries[2], boundaries[0], boundaries[1]); + endDate = new Date(Number(year) + boundaries[5], boundaries[3], boundaries[4]); + return { + startDate: moment(startDate).format('MM-DD-YYYY'), + endDate: moment(endDate).format('MM-DD-YYYY') + }; +} + +function parseYear(tokens) { + var startDate, endDate, year; + tokens.forEach(function findYear(token) { + if (token.match(YEAR_MATCH)) { + year = token; + } + }); + if (!year) { + return; + } + if (year < 100) { + year = Number(year) + 2000; + } + startDate = new Date(Number(year), 0, 1); + endDate = new Date(Number(year), 11, 31); + return { + startDate: moment(startDate).format('MM-DD-YYYY'), + endDate: moment(endDate).format('MM-DD-YYYY') + }; +} + +function parseMonth(tokens, now) { + var parsed, startDate, endDate, monthIndex, month, year; + tokens.forEach(function findMonth(token, index) { + var found = MONTHS.indexOf(token.slice(0, 3)); + if (found > -1) { + month = found; + monthIndex = index; + } + }); + if (!month) { + return; + } + if (tokens.length === monthIndex + 1) { + //If the month was the last thing we try to guess the year + year = new Date().getFullYear(); + endDate = new Date(Number(year), month); + if (endDate <= now) { + year = year + 1; + } + } else { + //Otherwise we try to find it in our tokens + tokens.forEach(function findYear(token) { + if (token.match(YEAR_MATCH)) { + year = token; + if (year < 100) { + year = Number(year) + 2000; + } + } + }); + } + if (!year) { + return; + } + startDate = new Date(Number(year), month); + endDate = moment(startDate).add('months', 1).subtract('days', 1); + parsed = { + startDate: moment(startDate).format('MM-DD-YYYY'), + endDate: moment(endDate).format('MM-DD-YYYY') + }; + return parsed; +} + +function parseSeasons(tokens, now) { + var parsed, season, seasonIndex, year; + tokens.forEach(function findSeason(token, index) { + var found = Object.keys(SEASONS).indexOf(token); + if (found > -1) { + season = token; + seasonIndex = index; + } + }); + if (!season) { + return; + } + if (tokens.length !== seasonIndex + 1) { + //If the season wasn't the last thing we try to find it + tokens.forEach(function findYear(token) { + if (token.match(YEAR_MATCH)) { + year = token; + if (year < 100) { + year = Number(year) + 2000; + } + } + }); + if (!year) { + return; + } + } + parsed = getSeason(now, season, year); + return getSeason(now, season, year); +} + +function parseDate(tokens) { + var dateString = tokens.join(' '); + dateString = moment(dateString).format('MM-DD-YYYY'); + if (dateString === 'Invalid date') { + return; + } + return { + startDate: dateString, + endDate: dateString + }; +} + +function tryAll(tokens, now) { + return parseSeasons(tokens, now) || + parseMonth(tokens, now) || + parseYear(tokens, now) || + parseDate(tokens); //parseDate always last +} + +//TODO "Oct to Sep" for example needs to know sep is the year after +//TODO format parameter +moment.ambit = function ambit(str) { + var result, startRange, endRange; + //var now = new Date(); + var tokens = str.trim().toLowerCase().split(/[,\s]+/); + var direction = 'right'; + var current = []; + var lastAttempt, currentAttempt; + + //Come at it left to right + while (direction === 'right' && tokens.length > 0) { + current.push(tokens.shift()); + currentAttempt = tryAll(current); + if (currentAttempt) { + lastAttempt = currentAttempt; + startRange = currentAttempt; + } else { + if (lastAttempt) { //We don't parse now but we used to, assume we're into the separator + //We're either still processing or are done + tokens.unshift(current.pop()); //Undo the last shift + direction = 'left'; + startRange = lastAttempt; + } + } + } + current = []; + endRange = tryAll(tokens); + if (startRange) { + result = { + startDate: startRange.startDate, + endDate: startRange.endDate + }; + if (endRange) { + result.endDate = endRange.endDate; + } + return result; + } +}; + +module.exports = moment; diff --git a/lib/main.js b/lib/main.js deleted file mode 100644 index 4fa7697..0000000 --- a/lib/main.js +++ /dev/null @@ -1,179 +0,0 @@ -var _ = require('underscore'); -var moment = require('moment'); - -//Removes deprication warning since we're literally in the one -//use case they describe as valid for doing this - -moment.createFromInputFallback = function (config) { - config._d = new Date(config._i); -}; -var SEASONS = { - spring: [2, 20, 0, 5, 19, 0], - summer: [5, 20, 0, 8, 23, 0], - fall: [8, 23, 0, 11, 21, 0], - autumn: [8, 23, 0, 11, 21, 0], - winter: [11, 21, 0, 2, 19, 1] -}; - -var MONTHS = ['jan', 'feb', 'mar', 'apr', 'may', 'jun', 'jul', 'aug', 'sep', 'oct', 'nov', 'dec']; - -var YEAR_MATCH = /^(20)?[0-9]{2}$/; - -function getSeason(now, season, year) { - var startDate, endDate; - var boundaries = SEASONS[season]; - if (!year) { - year = new Date().getFullYear(); - endDate = new Date(Number(year) + boundaries[5], boundaries[3], boundaries[4]); - if (endDate <= now) { - year = year + 1; - } - } - startDate = new Date(Number(year) + boundaries[2], boundaries[0], boundaries[1]); - endDate = new Date(Number(year) + boundaries[5], boundaries[3], boundaries[4]); - return { - startDate: moment(startDate).format('MM-DD-YYYY'), - endDate: moment(endDate).format('MM-DD-YYYY') - }; -} - -function parseYear(tokens) { - var startDate, endDate; - var year = _.find(tokens, function (token) { return token.match(YEAR_MATCH); }); - if (!year) { - return; - } - if (year < 100) { - year = Number(year) + 2000; - } - startDate = new Date(Number(year), 0, 1); - endDate = new Date(Number(year), 11, 31); - return { - startDate: moment(startDate).format('MM-DD-YYYY'), - endDate: moment(endDate).format('MM-DD-YYYY') - }; -} - -function parseMonth(tokens, now) { - var parsed, startDate, endDate, monthIndex, month, year; - _.each(tokens, function (token, index) { - var found = MONTHS.indexOf(token.slice(0, 3)); - if (found > -1) { - month = found; - monthIndex = index; - } - }); - if (!month) { - return; - } - if (tokens.length === monthIndex + 1) { - //If the month was the last thing we try to guess the year - year = new Date().getFullYear(); - endDate = new Date(Number(year), month); - if (endDate <= now) { - year = year + 1; - } - } else { - //Otherwise we try to find it in our tokens - year = _.find(tokens, function (token) { return token.match(YEAR_MATCH); }); - if (year < 100) { - year = Number(year) + 2000; - } - } - if (!year) { - return; - } - startDate = new Date(Number(year), month); - endDate = moment(startDate).add('months', 1).subtract('days', 1); - parsed = { - startDate: moment(startDate).format('MM-DD-YYYY'), - endDate: moment(endDate).format('MM-DD-YYYY') - }; - return parsed; -} - -function parseSeasons(tokens, now) { - var parsed, season, seasonIndex, year; - _.each(tokens, function (token, index) { - var found = Object.keys(SEASONS).indexOf(token); - if (found > -1) { - season = token; - seasonIndex = index; - } - }); - if (!season) { - return; - } - if (tokens.length !== seasonIndex + 1) { - //If the season wasn't the last thing we try to find it - year = _.find(tokens, function (token) { return token.match(YEAR_MATCH); }); - if (!year) { - return; - } - if (year < 100) { - year = Number(year) + 2000; - } - } - parsed = getSeason(now, season, year); - return getSeason(now, season, year); -} - -function parseDate(tokens) { - var dateString = tokens.join(' '); - dateString = moment(dateString).format('MM-DD-YYYY'); - if (dateString === 'Invalid date') { - return; - } - return { - startDate: dateString, - endDate: dateString - }; -} - -function tryAll(tokens, now) { - return parseSeasons(tokens, now) || - parseMonth(tokens, now) || - parseYear(tokens, now) || - parseDate(tokens); //parseDate always last -} - -//TODO "Oct to Sep" for example needs to know sep is the year after -function parse(str) { - var result, startRange, endRange; - //var now = new Date(); - var tokens = str.trim().toLowerCase().split(/[,\s]+/); - var direction = 'right'; - var current = []; - var lastAttempt, currentAttempt; - - //Come at it left to right - while (direction === 'right' && tokens.length > 0) { - current.push(tokens.shift()); - currentAttempt = tryAll(current); - if (currentAttempt) { - lastAttempt = currentAttempt; - startRange = currentAttempt; - } else { - if (lastAttempt) { //We don't parse now but we used to, assume we're into the separator - //We're either still processing or are done - tokens.unshift(current.pop()); //Undo the last shift - direction = 'left'; - startRange = lastAttempt; - } - } - } - current = []; - endRange = tryAll(tokens); - if (startRange) { - result = { - startDate: startRange.startDate, - endDate: startRange.endDate - }; - if (endRange) { - result.endDate = endRange.endDate; - } - return result; - } -} - -exports = module.exports = parse; diff --git a/package.json b/package.json new file mode 100644 index 0000000..703cba9 --- /dev/null +++ b/package.json @@ -0,0 +1,33 @@ +{ + "name": "ambit", + "description": "Date parser that returns a range instead of a single value", + "version": "0.0.0", + "author": "Michael Garvin ", + "bugs": { + "url": "https://github.com/wraithgar/date-range-parser/issues" + }, + "dependencies": { + "jshint": "^2.5.1", + "moment": "^2.7.0" + }, + "devDependencies": { + "precommit-hook": "^1.0.2" + }, + "homepage": "https://github.com/wraithgar/ambit", + "keywords": [ + "date", + "parse", + "range", + "node", + "commonjs" + ], + "license": "MIT", + "main": "ambit.js", + "repository": { + "type": "git", + "url": "git://github.com/wraithgar/date-range-parser.git" + }, + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + } +}