Skip to content

Commit

Permalink
First super rough version
Browse files Browse the repository at this point in the history
  • Loading branch information
Michael Garvin committed Jun 25, 2014
0 parents commit e8fed46
Show file tree
Hide file tree
Showing 2 changed files with 188 additions and 0 deletions.
9 changes: 9 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#date-range-parser

So it turns out I couldn't find anything out there that parsed a date
string in nominally plain English and returned a date range. This is a
WIP attempt at doing so. Requires moment, will hopefully eventually be
rewritten to work better with moment, and get a proper package.json and
all that jazz. (Also it needs a better name)

License: MIT
179 changes: 179 additions & 0 deletions lib/main.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,179 @@
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;

0 comments on commit e8fed46

Please sign in to comment.