Skip to content

Commit

Permalink
Provide a way to bias the dates parsed to either the past or the future
Browse files Browse the repository at this point in the history
Biasing is signaled by setting the `bias` property to `past` or `future`
on an object that is passed as the second optional argument to
`Date.parse()`.
  • Loading branch information
eric committed Aug 24, 2011
1 parent 327aa38 commit 7531a3d
Show file tree
Hide file tree
Showing 3 changed files with 211 additions and 14 deletions.
17 changes: 17 additions & 0 deletions README.md
Expand Up @@ -23,6 +23,23 @@ This isn't a comprehesive list, but it hopefully will hit the high points.
* Adding comments to `finish()` in `parser.js` to describe what each condition does
* Remove conditions in `finish()` in `parser.js` that are not used by any tests
* Fix a couple relative-ish cases like `15th at 3pm`
* Added support for "biasing" toward dates in the past or the future


## Biasing ##

There are many times where your users will be tending to want to specify dates
only in the past or the future, so providing a mechanism where, if it is
`3pm` in the afternoon, specifying `1pm` would give you the next day instead
of the current one.

To enable biasing, `Date.parse()` now takes an optional object that is used
as the object that is parsed to set defaults.

To enable biasing, set the `bias` property to either `past` or `future`. For
example:

Date.parse('3pm', { bias: 'future' })


## Original README.txt ##
Expand Down
48 changes: 35 additions & 13 deletions src/parser.js
Expand Up @@ -634,10 +634,10 @@
today = new Date();
}

var expression = !!(this.days && this.days !== null || this.orient || this.operator);
var expression = !!(this.days && this.days !== null || this.orient || this.operator || this.bias);

var gap, mod, orient;
orient = ((this.orient == "past" || this.operator == "subtract") ? -1 : 1);
orient = ((this.orient == "past" || this.operator == "subtract" || this.bias == "past") ? -1 : 1);

// For parsing: "last second", "next minute", "previous hour", "+5 seconds",
// "-5 hours", "5 hours", "7 hours ago"
Expand Down Expand Up @@ -679,18 +679,18 @@

// For parsing: "last january", "prev march", "next july", "today + 1 month",
// "+5 months"
if (expression && (this.month || this.month === 0) && this.unit != "year") {
if ((expression && !this.bias) && (this.month || this.month === 0) && this.unit != "year") {
this.unit = "month";
gap = (this.month - today.getMonth());
mod = 12;
this.months = gap ? ((gap + (orient * mod)) % mod) : (orient * mod);
this.month = null;
}

// For parsing: "Yesterday", "Tomorrow", "last monday", "last friday",
// "previous day", "next week", "next month", "next year",
// For parsing: "last monday", "last friday", "previous day",
// "next week", "next month", "next year",
// "today+", "+", "-", "yesterday at 4:00", "last friday at 20:00"
if (!this.value && expression) {
if (!this.value && expression && !this.bias) {
this.value = 1;
}

Expand All @@ -700,9 +700,9 @@
}

// For parsing: "15th at 20:15", "15th at 8pm"
if (!expression && this.value && (!this.unit || this.unit == "day") && !this.day) {
if ((!expression || this.bias) && this.value && (!this.unit || this.unit == "day") && !this.day) {
this.unit = "day";
this.day = this.value * orient;
this.day = this.value * 1
}

// For parsing: "last minute", "+5 hours", "previous month", "1 year ago tomorrow"
Expand Down Expand Up @@ -741,6 +741,28 @@

today.set(this);

if (this.bias) {
if (this.day) {
this.days = null
}

if (!this.day) {
if ((this.bias == "past" && today > new Date()) || (this.bias == "future" && today < new Date())) {
this.days = 1 * orient
}
} else if (!this.month && !this.months) {
if ((this.bias == "past" && today > new Date()) || (this.bias == "future" && today < new Date())) {
this.months = 1 * orient
}
} else if (!this.year) {
if ((this.bias == "past" && today > new Date()) || (this.bias == "future" && today < new Date())) {
this.years = 1 * orient
}
}

expression = true;
}

if (expression) {
today.add(this);
}
Expand Down Expand Up @@ -1082,12 +1104,12 @@
}
if (!o) {
o = {}
}
// try {
}
try {
r = $D.Grammar.start.call({}, s.replace(/^\s*(\S*(\s+\S+)*)\s*$/, "$1"), o);
// } catch (e) {
// return null;
// }
} catch (e) {
return null;
}
return ((r[1].length === 0) ? r[0] : null);
};

Expand Down
160 changes: 159 additions & 1 deletion test/partial/index.js
Expand Up @@ -1104,7 +1104,165 @@
return false;
}
}
},
'Biasing': {
'biasing to the past, parse 1am': {
run: function() { this.date = Date.parse('12:01am', { bias: 'past' }) },
assert: function() { return this.date < new Date() }
},
'biasing to the past, parse 11pm': {
run: function() { this.date = Date.parse('11:59pm', { bias: 'past' }) },
assert: function() { return this.date < new Date() }
},
'biasing to the past, parse a time an hour ahead of now': {
run: function() {
this.nextHour = new Date().next().hour()
this.date = Date.parse(String(this.nextHour.getHours()) + ":" + String(this.nextHour.getMinutes()), { bias: 'past' })
},
assert: function() {
// The date is less than today
return this.date < new Date() &&
// and the hour is the hour we told it to be
this.date.getHours() == this.nextHour.getHours() &&
// and the day is yesterday
new Date().previous().day().same().day(this.date);
}
},

'biasing to the past, parse a time an hour behind now': {
run: function() {
this.nextHour = new Date().previous().hour()
this.date = Date.parse(String(this.nextHour.getHours()) + ":" + String(this.nextHour.getMinutes()), { bias: 'past' })
},
assert: function() {
// The date is less than today
return this.date < new Date() &&
// and the hour is the hour we told it to be
this.date.getHours() == this.nextHour.getHours() &&
// and the day is yesterday
new Date().same().day(this.date);
}
},

'biasing to the past, parse tomorrows day of the week to last week': {
run: function() {
this.nextDay = new Date().next().day()
this.date = Date.parse(this.nextDay.toString("dddd") + " at 3pm", { bias: 'past' })
},
assert: function() {
// The date is less than now
return this.date < new Date() &&
// and the hour is 3pm
this.date.getHours() == 15;
}
},

'biasing to the past, parse the day of the month tomorrow to last month': {
run: function() {
this.nextDay = new Date().next().day()
this.date = Date.parse(this.nextDay.toString("dS") + " at 3pm", { bias: 'past' })
},
assert: function() {
// The date is less than now
return this.date < new Date() &&
// and the hour is 3pm
this.date.getHours() == 15 &&
// make sure the day of the month is right
this.date.getDate() == this.nextDay.getDate();
}
},

'biasing to the past, parse the day and the month of next month to a year ago': {
run: function() {
this.nextMonth = new Date().next().month()
this.date = Date.parse(this.nextMonth.toString("MMMM dS") + " at 3pm", { bias: 'past' })
},
assert: function() {
// The date is less than now
return this.date < new Date() &&
// and the hour is 3pm
this.date.getHours() == 15 &&
// make sure the day of the month is right
this.date.getDate() == this.nextMonth.getDate();
}
},

'biasing to the future, parse a time an hour behind now': {
run: function() {
this.previousHour = new Date().previous().hour()
this.date = Date.parse(String(this.previousHour.getHours()) + ":" + String(this.previousHour.getMinutes()), { bias: 'future' })
},
assert: function() {
// The date is greater than now
return this.date > new Date() &&
// and the hour is the hour we told it to be
this.date.getHours() == this.previousHour.getHours() &&
// and the day is yesterday
new Date().next().day().same().day(this.date);
}
},

'biasing to the future, parse a time an hour ahead of now': {
run: function() {
this.nextHour = new Date().next().hour()
this.date = Date.parse(String(this.nextHour.getHours()) + ":" + String(this.nextHour.getMinutes()), { bias: 'future' })
},
assert: function() {
// The date is greater than now
return this.date > new Date() &&
// and the hour is the hour we told it to be
this.date.getHours() == this.nextHour.getHours() &&
// and the day is yesterday
new Date().same().day(this.date);
}
},


'biasing to the future, parse yesterdays day of the week to next week': {
run: function() {
this.previousDay = new Date().previous().day()
this.date = Date.parse(this.previousDay.toString("dddd") + " at 3pm", { bias: 'future' })
},
assert: function() {
// The date is greater than now
return this.date > new Date() &&
// and the hour is 3pm
this.date.getHours() == 15;
}
},

'biasing to the future, parse the day of the month for yesterday to next month': {
run: function() {
this.previousDay = new Date().previous().day()
this.query = this.previousDay.toString("dS") + " at 3pm"
this.date = Date.parse(this.query, { bias: 'future' })
},
assert: function() {
// The date is greater than now
return this.date > new Date() &&
// and the hour is 3pm
this.date.getHours() == 15 &&
// make sure the day of the month is right
this.date.getDate() == this.previousDay.getDate();
}
},

'biasing to the future, parse the day and the month of last month to a year from now': {
run: function() {
this.lastMonth = new Date().last().month()
this.query = this.lastMonth.toString("MMMM dS") + " at 3pm"
this.date = Date.parse(this.query, { bias: 'future' })
},
assert: function() {
// The date is greater than now
return this.date > new Date() &&
// and the hour is 3pm
this.date.getHours() == 15 &&
// make sure the day of the month is right
this.date.getDate() == this.lastMonth.getDate();
}
},

}

});

0 comments on commit 7531a3d

Please sign in to comment.