Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Initial commit

  • Loading branch information...
commit 3a06973b2de4b0096e070d48230b3ffc600733f7 1 parent 5deb74f
@ForbesLindesay authored
View
3  .gitignore
@@ -0,0 +1,3 @@
+components
+build
+node_modules
View
13 component.json
@@ -0,0 +1,13 @@
+{
+ "name": "dehumanize-date",
+ "repo": "ForbesLindesay/dehumanize-date",
+ "description": "Parse dates in all the formats humans like to use.",
+ "version": "0.0.1",
+ "keywords": [],
+ "dependencies": {},
+ "development": {},
+ "license": "MIT",
+ "scripts": [
+ "index.js"
+ ]
+}
View
124 index.js
@@ -0,0 +1,124 @@
+var MONTH_NAMES = ["january", "february", "march",
+ "april", "may", "june",
+ "july", "august", "september",
+ "october", "november", "december"];
+
+var DAY_NAMES = ["sunday", "monday", "tuesday", "wednesday", "thursday", "friday", "saturday"];
+
+exports = module.exports = function parse(str, usa) {
+ var now = new Date();
+ //strip time information
+ now = new Date(now.getFullYear(), now.getMonth(), now.getDate());
+
+ str = str.trim().toLowerCase();
+
+ return parseNearbyDays(str, now) ||
+ parseLastThisNext(str, now) ||
+ parseNumberDate(str, usa) ||
+ parseWordyDate(str, now) ||
+ parseIso8601Date(str);
+};
+
+var NUMBER = /^[0-9]+$/;
+var NUMBER_WITH_ORDINAL = /^([0-9]+)(st|nd|rd|th)?$/;
+var NUMBER_DATE = /^(3[0-1]|[1-2][0-9]|0?[1-9])[,\|\\\/\-\. ]+(1[0-2]|0?[1-9])[,\|\\\/\-\. ]+([0-9]{4})$/;
+var NUMBER_DATE_USA = /^(1[0-2]|0?[1-9])[,\|\\\/\-\. ]+(3[0-1]|[1-2][0-9]|0?[1-9])[,\|\\\/\-\. ]+([0-9]{4})$/;
+var ISO_8601_DATE = /^([0-9]{4})-?(1[0-2]|0?[1-9])-?(3[0-1]|[1-2][0-9]|0?[1-9])$/;
+
+function addDays(date, numberOfDays) {
+ return new Date(date * 1 + numberOfDays * 60 * 60 * 24 * 1000);
+}
+
+
+exports.parseNearbyDays = parseNearbyDays;
+function parseNearbyDays(string, today) {
+ if (string == 'today') {
+ return today;
+ } else if (string == 'yesterday') {
+ return addDays(today, -1);
+ } else if (string == 'tomorrow') {
+ return addDays(today, +1);
+ } else {
+ return null;
+ }
+}
+
+exports.parseLastThisNext = parseLastThisNext;
+function parseLastThisNext(string, today) {
+ var tokens = string.split(/[,\s]+/);
+
+ if (['last', 'this', 'next'].indexOf(tokens[0]) >= 0 &&
+ DAY_NAMES.indexOf(tokens[1]) >= 0 &&
+ tokens.length === 2) {
+ var dayDiff = DAY_NAMES.indexOf(tokens[1]) - today.getDay();
+ if (dayDiff < 0) dayDiff += 7;
+
+ if (tokens[0] === 'last') return addDays(today, dayDiff - 7);
+ if (tokens[0] === 'this') return addDays(today, dayDiff);
+ if (tokens[0] === 'next') return addDays(today, dayDiff + 7);
+ } else {
+ return null;
+ }
+}
+
+exports.parseNumberDate = parseNumberDate;
+function parseNumberDate(str, usa) {
+ var match = usa ? NUMBER_DATE_USA.exec(str) : NUMBER_DATE.exec(str);
+ if (match) {
+ return usa ? new Date(+match[3], match[1] - 1, +match[2]) : new Date(+match[3], match[2] - 1, +match[1]);
+ } else {
+ return null;
+ }
+}
+
+exports.parseWordyDate = parseWordyDate;
+function parseWordyDate(string, today) {
+ var tokens = string.split(/[,\s]+/);
+ if (tokens.length >= 2) {
+ var match;
+ if (match = tokens[0].match(NUMBER_WITH_ORDINAL)) {
+ return parseWordyDateParts(match[1], tokens[1], tokens[2], today);
+ } else if (match = tokens[1].match(NUMBER_WITH_ORDINAL)) {
+ return parseWordyDateParts(match[1], tokens[0], tokens[2], today);
+ } else {
+ return null;
+ }
+ }
+}
+
+function parseWordyDateParts(rawDay, rawMonth, rawYear, today) {
+ var day = +rawDay;
+ var month = monthFromName(rawMonth);
+ var year;
+
+ if (rawYear)
+ year = rawYear.match(NUMBER) ? rawYear * 1 : null;
+ else
+ year = today.getFullYear();
+
+ if (!(day && month !== null && year))
+ return null;
+
+ var result = new Date(year, month, day);
+
+ // Date constructor will happily accept invalid dates
+ // so we're checking that day existed in the given month
+ if (result.getMonth() != month || result.getDate() != day)
+ return null;
+
+ return result;
+}
+
+function parseIso8601Date(string) {
+ var match;
+ if (match = ISO_8601_DATE.exec(string)) {
+ return new Date(+match[1], match[2] - 1, +match[3]);
+ }
+}
+
+var monthAbreviations = MONTH_NAMES.map(function (name) { return name.substr(0, 3); });
+exports.monthFromName = monthFromName;
+function monthFromName(month) {
+ var monthIndex = month.length === 3 ? monthAbreviations.indexOf(month) : MONTH_NAMES.indexOf(month);
+ return monthIndex >= 0 ? monthIndex : null;
+}
View
27 package.json
@@ -0,0 +1,27 @@
+{
+ "name": "dehumanize-date",
+ "version": "0.0.0",
+ "description": "Parse dates in all the formats humans like to use.",
+ "main": "index.js",
+ "scripts": {
+ "test": "mocha -R spec"
+ },
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/ForbesLindesay/dehumanize-date.git"
+ },
+ "keywords": [
+ "date",
+ "parse",
+ "dates",
+ "humanize",
+ "humanise",
+ "human"
+ ],
+ "author": "ForbesLindesay",
+ "license": "MIT",
+ "devDependencies": {
+ "better-assert": "~1.0.0",
+ "mocha": "~1.8.1"
+ }
+}
View
123 test/tests.js
@@ -0,0 +1,123 @@
+var assert = require('better-assert');
+var dehumanize = require('../');
+
+var day = 60 * 60 * 24 * 1000;
+
+function equal(d, year, month, day) {
+ return d.valueOf() === new Date(year, month, day).valueOf() || console.dir(d);
+}
+
+describe('parseNearbyDays', function () {
+ it('understands "today"', function () {
+ assert(equal(dehumanize.parseNearbyDays('today', new Date(2000, 0, 5)), 2000, 0, 5));
+ });
+ it('understands "yesterday"', function () {
+ assert(equal(dehumanize.parseNearbyDays('yesterday', new Date(2000, 0, 5)), 2000, 0, 4));
+ });
+ it('understands "tomorrow"', function () {
+ assert(equal(dehumanize.parseNearbyDays('tomorrow', new Date(2000, 0, 5)), 2000, 0, 6));
+ });
+ it('returns `null` when it doesn\'t understand', function () {
+ assert(dehumanize.parseNearbyDays('foo bar', new Date(2000, 0, 5)) === null);
+ });
+});
+
+
+describe('parseLastThisNext', function () {
+ it('understands "next monday"', function () {
+ assert(equal(dehumanize.parseLastThisNext('next monday', new Date(2000, 0, 5)), 2000, 0, 17));
+ });
+ it('understands "last tuesday"', function () {
+ assert(equal(dehumanize.parseLastThisNext('last tuesday', new Date(2000, 0, 5)), 2000, 0, 4));
+ });
+ it('understands "this wednesday"', function () {
+ assert(equal(dehumanize.parseLastThisNext('this thursday', new Date(2000, 0, 5)), 2000, 0, 6));
+ });
+ it('returns `null` when it doesn\'t understand', function () {
+ assert(dehumanize.parseLastThisNext('foo bar', new Date(2000, 0, 5)) === null);
+ });
+});
+
+
+describe('parseNumberDate', function () {
+ it('understands "2-2-2012"', function () {
+ assert(equal(dehumanize.parseNumberDate('2-2-2012'), 2012, 1, 2));
+ });
+ it('understands "2/8/2012"', function () {
+ assert(equal(dehumanize.parseNumberDate('2/8/2012'), 2012, 7, 2));
+ });
+ it('understands "2\\8\\2012"', function () {
+ assert(equal(dehumanize.parseNumberDate('2\\8\\2012'), 2012, 7, 2));
+ });
+ it('understands "2,4,2012"', function () {
+ assert(equal(dehumanize.parseNumberDate('2,4,2012'), 2012, 3, 2));
+ });
+ it('understands "2 4 2012"', function () {
+ assert(equal(dehumanize.parseNumberDate('2 4 2012'), 2012, 3, 2));
+ });
+ it('understands "02-02-2012"', function () {
+ assert(equal(dehumanize.parseNumberDate('02-02-2012'), 2012, 1, 2));
+ });
+ it('returns `null` when it doesn\'t understand', function () {
+ assert(dehumanize.parseNumberDate('80-80-2012') === null);
+ });
+});
+
+describe('monthFromName', function () {
+ it('understands "june"', function () {
+ assert(dehumanize.monthFromName('june') === 5);
+ });
+ it('understands "jun', function () {
+ assert(dehumanize.monthFromName('jun') === 5);
+ });
+ it('understands "january"', function () {
+ assert(dehumanize.monthFromName('january') === 0);
+ });
+ it('understands "jan', function () {
+ assert(dehumanize.monthFromName('jan') === 0);
+ });
+ it('returns `null` when it doesn\'t understand', function () {
+ assert(dehumanize.monthFromName('foo') === null);
+ assert(dehumanize.monthFromName('foobar') === null);
+ });
+});
+
+describe('parseWordyDate', function () {
+ it('understands "june 1st 2012"', function () {
+ assert(equal(dehumanize.parseWordyDate('june 1st 2012', new Date(2012, 1, 1)), 2012, 5, 1));
+ });
+ it('understands "1st june 2012', function () {
+ assert(equal(dehumanize.parseWordyDate('1st june 2012', new Date(2012, 1, 1)), 2012, 5, 1));
+ });
+ it('understands "june 1 2012"', function () {
+ assert(equal(dehumanize.parseWordyDate('june 1 2012', new Date(2012, 1, 1)), 2012, 5, 1));
+ });
+ it('understands "1 june 2012', function () {
+ assert(equal(dehumanize.parseWordyDate('1 june 2012', new Date(2012, 1, 1)), 2012, 5, 1));
+ });
+ it('understands "12th june 2012"', function () {
+ assert(equal(dehumanize.parseWordyDate('12th june 2012', new Date(2012, 1, 1)), 2012, 5, 12));
+ });
+ it('understands "12 june 2012', function () {
+ assert(equal(dehumanize.parseWordyDate('12 june 2012', new Date(2012, 1, 1)), 2012, 5, 12));
+ });
+ it('understands "12th june"', function () {
+ assert(equal(dehumanize.parseWordyDate('12th june', new Date(2012, 1, 1)), 2012, 5, 12));
+ });
+ it('returns `null` when it doesn\'t understand', function () {
+ assert(dehumanize.parseWordyDate('12th ju', new Date(2012, 1, 1)) === null);
+ assert(dehumanize.parseWordyDate('12th ju 2012', new Date(2012, 1, 1)) === null);
+ assert(dehumanize.monthFromName('36th june', new Date(2012, 1, 1)) === null);
+ assert(dehumanize.monthFromName('36th june 2012', new Date(2012, 1, 1)) === null);
+ });
+});
+
+describe('dehumanize', function () {
+ it('understands " 14th July 2012 "', function () {
+ assert(equal(dehumanize(' 14th July 2012 '), 2012, 6, 14))
+ });
+ it('returns `null` when it doesn\'t understand', function () {
+ assert(dehumanize.monthFromName('foo') === null);
+ assert(dehumanize.monthFromName('foobar') === null);
+ });
+});
Please sign in to comment.
Something went wrong with that request. Please try again.