Repository: chaijs/chai (js)

Issue:
Error or not updated docs on assert.isAbove and isBelow
Hi.

`assert.isAbove` and `assert.isBelow` used to take Dates as possible inputs and worked fine, but since v4.0.0 it errors with `expected *date* to be a number` resulting in numerous failed test-cases throughout my codebase after an update from 3.5.

So my guess is either the documentation was not updated (as it currently lists {Mixed} type for an input) or it's a bug - either way there seems to be a problem. Just wanted to ask which one is it.

It's reproducible both in Mocha (Node) and in the browser (dev console on the official website with `4.0.0-canary.2` at the moment of this issue)

Is there is indeed a problem or have I misunderstood something?
And how can I help?

Pull request description:
Allow dates for isBelow and isAbove assertions
Hi guys.

This closes #980. I took #692 as an "inspiration" and extended the code to support dates as discussed in the issue.

I have encountered some questions along the way and left comments (off course will delete them once we decided what to do at those moments) - I will expand on them within GitHub, to make them easier to notice. So it's a WIP for now, just need a bit of your guidance.

This is my first attempt at actually PR-ing anything open-source, so sorry for mistakes.
I'd really like to contribute :)

Also need to fix up Phantom tests, but wanted to get some feedback and ask these questions first, to understand the general direction and the scope of this PR better.

Patch:
diff --git a/lib/chai/core/assertions.js b/lib/chai/core/assertions.js
index d1330e4..1f6adfe 100644
--- a/lib/chai/core/assertions.js
+++ b/lib/chai/core/assertions.js
@@ -1024,7 +1024,7 @@ module.exports = function (chai, _) {
   /**
    * ### .above(n[, msg])
    *
-   * Asserts that the target is a number greater than the given number `n`.
+   * Asserts that the target is a number or a date greater than the given number or date `n` respectively.
    * However, it's often best to assert that the target is equal to its expected
    * value.
    *
@@ -1069,21 +1069,29 @@ module.exports = function (chai, _) {
     var obj = flag(this, 'object')
       , doLength = flag(this, 'doLength')
       , flagMsg = flag(this, 'message')
-      , ssfi = flag(this, 'ssfi');
+      , msgPrefix = ((flagMsg) ? flagMsg + ': ' : '')
+      , ssfi = flag(this, 'ssfi')
+      , objType = _.type(obj).toLowerCase()
+      , nType = _.type(n).toLowerCase()
+      , shouldThrow = true;
 
     if (doLength) {
       new Assertion(obj, flagMsg, ssfi, true).to.have.property('length');
+    }
+    
+    if (!doLength && (objType === 'date' && nType !== 'date')) {
+      errorMessage = msgPrefix + 'the argument to above must be a date';
+    } else if (nType !== 'number' && (doLength || objType === 'number')) {
+      errorMessage = msgPrefix + 'the argument to above must be a number';
+    } else if (!doLength && (objType !== 'date' && objType !== 'number')) {
+      var printObj = (objType === 'string') ? "'" + obj + "'" : obj;
+      errorMessage = msgPrefix + 'expected ' + printObj + ' to be a number or a date';
     } else {
-      new Assertion(obj, flagMsg, ssfi, true).is.a('number');
+      shouldThrow = false;
     }
 
-    if (typeof n !== 'number') {
-      flagMsg = flagMsg ? flagMsg + ': ' : '';
-      throw new AssertionError(
-        flagMsg + 'the argument to above must be a number',
-        undefined,
-        ssfi
-      );
+    if (shouldThrow) {
+      throw new AssertionError(errorMessage, undefined, ssfi);
     }
 
     if (doLength) {
@@ -1098,8 +1106,9 @@ module.exports = function (chai, _) {
     } else {
       this.assert(
           obj > n
-        , 'expected #{this} to be above ' + n
-        , 'expected #{this} to be at most ' + n
+        , 'expected #{this} to be above #{exp}'
+        , 'expected #{this} to be at most #{exp}'
+        , n
       );
     }
   }
@@ -1111,8 +1120,8 @@ module.exports = function (chai, _) {
   /**
    * ### .least(n[, msg])
    *
-   * Asserts that the target is a number greater than or equal to the given
-   * number `n`. However, it's often best to assert that the target is equal to
+   * Asserts that the target is a number or a date greater than or equal to the given
+   * number or date `n` respectively. However, it's often best to assert that the target is equal to
    * its expected value.
    *
    *     expect(2).to.equal(2); // Recommended
@@ -1156,21 +1165,29 @@ module.exports = function (chai, _) {
     var obj = flag(this, 'object')
       , doLength = flag(this, 'doLength')
       , flagMsg = flag(this, 'message')
-      , ssfi = flag(this, 'ssfi');
+      , msgPrefix = ((flagMsg) ? flagMsg + ': ' : '')
+      , ssfi = flag(this, 'ssfi')
+      , objType = _.type(obj).toLowerCase()
+      , nType = _.type(n).toLowerCase()
+      , shouldThrow = true;
 
     if (doLength) {
       new Assertion(obj, flagMsg, ssfi, true).to.have.property('length');
+    }
+
+    if (!doLength && (objType === 'date' && nType !== 'date')) {
+      errorMessage = msgPrefix + 'the argument to least must be a date';
+    } else if (nType !== 'number' && (doLength || objType === 'number')) {
+      errorMessage = msgPrefix + 'the argument to least must be a number';
+    } else if (!doLength && (objType !== 'date' && objType !== 'number')) {
+      var printObj = (objType === 'string') ? "'" + obj + "'" : obj;
+      errorMessage = msgPrefix + 'expected ' + printObj + ' to be a number or a date';
     } else {
-      new Assertion(obj, flagMsg, ssfi, true).is.a('number');
+      shouldThrow = false;
     }
 
-    if (typeof n !== 'number') {
-      flagMsg = flagMsg ? flagMsg + ': ' : '';
-      throw new AssertionError(
-        flagMsg + 'the argument to least must be a number',
-        undefined,
-        ssfi
-      );
+    if (shouldThrow) {
+      throw new AssertionError(errorMessage, undefined, ssfi);
     }
 
     if (doLength) {
@@ -1185,8 +1202,9 @@ module.exports = function (chai, _) {
     } else {
       this.assert(
           obj >= n
-        , 'expected #{this} to be at least ' + n
-        , 'expected #{this} to be below ' + n
+        , 'expected #{this} to be at least #{exp}'
+        , 'expected #{this} to be below #{exp}'
+        , n
       );
     }
   }
@@ -1197,7 +1215,7 @@ module.exports = function (chai, _) {
   /**
    * ### .below(n[, msg])
    *
-   * Asserts that the target is a number less than the given number `n`.
+   * Asserts that the target is a number or a date less than the given number or date `n` respectively.
    * However, it's often best to assert that the target is equal to its expected
    * value.
    *
@@ -1242,21 +1260,29 @@ module.exports = function (chai, _) {
     var obj = flag(this, 'object')
       , doLength = flag(this, 'doLength')
       , flagMsg = flag(this, 'message')
-      , ssfi = flag(this, 'ssfi');
+      , msgPrefix = ((flagMsg) ? flagMsg + ': ' : '')
+      , ssfi = flag(this, 'ssfi')
+      , objType = _.type(obj).toLowerCase()
+      , nType = _.type(n).toLowerCase()
+      , shouldThrow = true;
 
     if (doLength) {
       new Assertion(obj, flagMsg, ssfi, true).to.have.property('length');
+    }
+
+    if (!doLength && (objType === 'date' && nType !== 'date')) {
+      errorMessage = msgPrefix + 'the argument to below must be a date';
+    } else if (nType !== 'number' && (doLength || objType === 'number')) {
+      errorMessage = msgPrefix + 'the argument to below must be a number';
+    } else if (!doLength && (objType !== 'date' && objType !== 'number')) {
+      var printObj = (objType === 'string') ? "'" + obj + "'" : obj;
+      errorMessage = msgPrefix + 'expected ' + printObj + ' to be a number or a date';
     } else {
-      new Assertion(obj, flagMsg, ssfi, true).is.a('number');
+      shouldThrow = false;
     }
 
-    if (typeof n !== 'number') {
-      flagMsg = flagMsg ? flagMsg + ': ' : '';
-      throw new AssertionError(
-        flagMsg + 'the argument to below must be a number',
-        undefined,
-        ssfi
-      );
+    if (shouldThrow) {
+      throw new AssertionError(errorMessage, undefined, ssfi);
     }
 
     if (doLength) {
@@ -1271,8 +1297,9 @@ module.exports = function (chai, _) {
     } else {
       this.assert(
           obj < n
-        , 'expected #{this} to be below ' + n
-        , 'expected #{this} to be at least ' + n
+        , 'expected #{this} to be below #{exp}'
+        , 'expected #{this} to be at least #{exp}'
+        , n
       );
     }
   }
@@ -1284,8 +1311,8 @@ module.exports = function (chai, _) {
   /**
    * ### .most(n[, msg])
    *
-   * Asserts that the target is a number less than or equal to the given number
-   * `n`. However, it's often best to assert that the target is equal to its
+   * Asserts that the target is a number or a date less than or equal to the given number
+   * or date `n` respectively. However, it's often best to assert that the target is equal to its
    * expected value.
    *
    *     expect(1).to.equal(1); // Recommended
@@ -1328,21 +1355,29 @@ module.exports = function (chai, _) {
     var obj = flag(this, 'object')
       , doLength = flag(this, 'doLength')
       , flagMsg = flag(this, 'message')
-      , ssfi = flag(this, 'ssfi');
+      , msgPrefix = ((flagMsg) ? flagMsg + ': ' : '')
+      , ssfi = flag(this, 'ssfi')
+      , objType = _.type(obj).toLowerCase()
+      , nType = _.type(n).toLowerCase()
+      , shouldThrow = true;
 
     if (doLength) {
       new Assertion(obj, flagMsg, ssfi, true).to.have.property('length');
+    }
+    
+    if (!doLength && (objType === 'date' && nType !== 'date')) {
+      errorMessage = msgPrefix + 'the argument to most must be a date';
+    } else if (nType !== 'number' && (doLength || objType === 'number')) {
+      errorMessage = msgPrefix + 'the argument to most must be a number';
+    } else if (!doLength && (objType !== 'date' && objType !== 'number')) {
+      var printObj = (objType === 'string') ? "'" + obj + "'" : obj;
+      errorMessage = msgPrefix + 'expected ' + printObj + ' to be a number or a date';
     } else {
-      new Assertion(obj, flagMsg, ssfi, true).is.a('number');
+      shouldThrow = false;
     }
 
-    if (typeof n !== 'number') {
-      flagMsg = flagMsg ? flagMsg + ': ' : '';
-      throw new AssertionError(
-        flagMsg + 'the argument to most must be a number',
-        undefined,
-        ssfi
-      );
+    if (shouldThrow) {
+      throw new AssertionError(errorMessage, undefined, ssfi);
     }
 
     if (doLength) {
@@ -1357,8 +1392,9 @@ module.exports = function (chai, _) {
     } else {
       this.assert(
           obj <= n
-        , 'expected #{this} to be at most ' + n
-        , 'expected #{this} to be above ' + n
+        , 'expected #{this} to be at most #{exp}'
+        , 'expected #{this} to be above #{exp}'
+        , n
       );
     }
   }
@@ -1369,8 +1405,8 @@ module.exports = function (chai, _) {
   /**
    * ### .within(start, finish[, msg])
    *
-   * Asserts that the target is a number greater than or equal to the given
-   * number `start`, and less than or equal to the given number `finish`.
+   * Asserts that the target is a number or a date greater than or equal to the given
+   * number or date `start`, and less than or equal to the given number or date `finish` respectively.
    * However, it's often best to assert that the target is equal to its expected
    * value.
    *
@@ -1412,24 +1448,35 @@ module.exports = function (chai, _) {
   Assertion.addMethod('within', function (start, finish, msg) {
     if (msg) flag(this, 'message', msg);
     var obj = flag(this, 'object')
-      , range  = start + '..' + finish
       , doLength = flag(this, 'doLength')
       , flagMsg = flag(this, 'message')
-      , ssfi = flag(this, 'ssfi');
+      , msgPrefix = ((flagMsg) ? flagMsg + ': ' : '')
+      , ssfi = flag(this, 'ssfi')
+      , objType = _.type(obj).toLowerCase()
+      , startType = _.type(start).toLowerCase()
+      , finishType = _.type(finish).toLowerCase()
+      , shouldThrow = true
+      , range = (startType === 'date' && finishType === 'date')
+          ? start.toUTCString() + '..' + finish.toUTCString()
+          : start + '..' + finish;
 
     if (doLength) {
       new Assertion(obj, flagMsg, ssfi, true).to.have.property('length');
+    }
+
+    if (!doLength && (objType === 'date' && (startType !== 'date' || finishType !== 'date'))) {
+      errorMessage = msgPrefix + 'the arguments to within must be dates';
+    } else if ((startType !== 'number' || finishType !== 'number') && (doLength || objType === 'number')) {
+      errorMessage = msgPrefix + 'the arguments to within must be numbers';
+    } else if (!doLength && (objType !== 'date' && objType !== 'number')) {
+      var printObj = (objType === 'string') ? "'" + obj + "'" : obj;
+      errorMessage = msgPrefix + 'expected ' + printObj + ' to be a number or a date';
     } else {
-      new Assertion(obj, flagMsg, ssfi, true).is.a('number');
+      shouldThrow = false;
     }
 
-    if (typeof start !== 'number' || typeof finish !== 'number') {
-      flagMsg = flagMsg ? flagMsg + ': ' : '';
-      throw new AssertionError(
-        flagMsg + 'the arguments to within must be numbers',
-        undefined,
-        ssfi
-      );
+    if (shouldThrow) {
+      throw new AssertionError(errorMessage, undefined, ssfi);
     }
 
     if (doLength) {
diff --git a/lib/chai/utils/expectTypes.js b/lib/chai/utils/expectTypes.js
index 17e26c2..6293db7 100644
--- a/lib/chai/utils/expectTypes.js
+++ b/lib/chai/utils/expectTypes.js
@@ -32,7 +32,7 @@ module.exports = function expectTypes(obj, types) {
   types = types.map(function (t) { return t.toLowerCase(); });
   types.sort();
 
-  // Transforms ['lorem', 'ipsum'] into 'a lirum, or an ipsum'
+  // Transforms ['lorem', 'ipsum'] into 'a lorem, or an ipsum'
   var str = types.map(function (t, index) {
     var art = ~[ 'a', 'e', 'i', 'o', 'u' ].indexOf(t.charAt(0)) ? 'an' : 'a';
     var or = types.length > 1 && index === types.length - 1 ? 'or ' : '';
diff --git a/package.json b/package.json
index fadaceb..5e65d6e 100644
--- a/package.json
+++ b/package.json
@@ -41,7 +41,7 @@
     "type-detect": "^4.0.0"
   },
   "devDependencies": {
-    "browserify": "^13.0.1",
+    "browserify": "^14.4.0",
     "bump-cli": "^1.1.3",
     "istanbul": "^0.4.3",
     "karma": "^1.0.0",


Test patch:
diff --git a/test/assert.js b/test/assert.js
index 9a36f59..87f7588 100644
--- a/test/assert.js
+++ b/test/assert.js
@@ -1952,13 +1952,43 @@ describe('assert', function () {
 
     err(function() {
       assert.isAbove(null, 1, 'blah');
-    }, 'blah: expected null to be a number');
+    }, 'blah: expected null to be a number or a date');
 
     err(function() {
       assert.isAbove(1, null, 'blah');
     }, 'blah: the argument to above must be a number');
   });
 
+  it('above (dates)', function() {
+    var now = new Date();
+    var oneSecondAgo = new Date(now.getTime() - 1000);
+    assert.isAbove(now, oneSecondAgo, 'Now should be above 1 second ago');
+
+    err(function() {
+      assert.isAbove(oneSecondAgo, now, 'blah');
+    }, 'blah: expected ' + oneSecondAgo.toUTCString() + ' to be above ' + now.toUTCString());
+
+    err(function() {
+      assert.isAbove(now, now, 'blah');
+    }, 'blah: expected ' + now.toUTCString() + ' to be above ' + now.toUTCString());
+    
+    err(function() {
+      assert.isAbove(null, now);
+    }, 'expected null to be a number or a date');
+
+    err(function() {
+      assert.isAbove(now, null, 'blah');
+    }, 'blah: the argument to above must be a date');
+
+    err(function() {
+      assert.isAbove(now, 1, 'blah');
+    }, 'blah: the argument to above must be a date');
+
+    err(function() {
+      assert.isAbove(1, now, 'blah');
+    }, 'blah: the argument to above must be a number');
+  });
+
   it('atLeast', function() {
     assert.isAtLeast(5, 2, '5 should be above 2');
     assert.isAtLeast(1, 1, '1 should be equal to 1');
@@ -1969,13 +1999,42 @@ describe('assert', function () {
 
     err(function() {
       assert.isAtLeast(null, 1, 'blah');
-    }, 'blah: expected null to be a number');
+    }, 'blah: expected null to be a number or a date');
 
     err(function() {
       assert.isAtLeast(1, null, 'blah');
     }, 'blah: the argument to least must be a number');
   });
 
+  it('atLeast (dates)', function() {
+    var now = new Date();
+    var oneSecondAgo = new Date(now.getTime() - 1000);
+    var oneSecondAfter = new Date(now.getTime() + 1000);
+
+    assert.isAtLeast(now, oneSecondAgo, 'Now should be above one second ago');
+    assert.isAtLeast(now, now, 'Now should be equal to now');
+
+    err(function() {
+      assert.isAtLeast(now, oneSecondAfter, 'blah');
+    }, 'blah: expected ' + now.toUTCString() + ' to be at least ' + oneSecondAfter.toUTCString());
+
+    err(function() {
+      assert.isAtLeast(null, now, 'blah');
+    }, 'blah: expected null to be a number or a date');
+
+    err(function() {
+      assert.isAtLeast(now, null, 'blah');
+    }, 'blah: the argument to least must be a date');
+
+    err(function() {
+      assert.isAtLeast(1, now, 'blah');
+    }, 'blah: the argument to least must be a number');
+
+    err(function() {
+      assert.isAtLeast(now, 1, 'blah');
+    }, 'blah: the argument to least must be a date');
+  });
+
   it('below', function() {
     assert.isBelow(2, 5, '2 should be below 5');
 
@@ -1989,13 +2048,43 @@ describe('assert', function () {
 
     err(function() {
       assert.isBelow(null, 1, 'blah');
-    }, 'blah: expected null to be a number');
+    }, 'blah: expected null to be a number or a date');
 
     err(function() {
       assert.isBelow(1, null, 'blah');
     }, 'blah: the argument to below must be a number');
   });
 
+  it('below (dates)', function() {
+    var now = new Date();
+    var oneSecondAgo = new Date(now.getTime() - 1000);
+    assert.isBelow(oneSecondAgo, now, 'One second ago should be below now');
+
+    err(function() {
+      assert.isBelow(now, oneSecondAgo, 'blah');
+    }, 'blah: expected ' + now.toUTCString() + ' to be below ' + oneSecondAgo.toUTCString());
+
+    err(function() {
+      assert.isBelow(now, now);
+    }, 'expected ' + now.toUTCString() + ' to be below ' + now.toUTCString());
+
+    err(function() {
+      assert.isBelow(null, now, 'blah');
+    }, 'blah: expected null to be a number or a date');
+
+    err(function() {
+      assert.isBelow(now, null, 'blah');
+    }, 'blah: the argument to below must be a date');
+
+    err(function() {
+      assert.isBelow(now, 1, 'blah');
+    }, 'blah: the argument to below must be a date');
+
+    err(function() {
+      assert.isBelow(1, now, 'blah');
+    }, 'blah: the argument to below must be a number');
+  });
+
   it('atMost', function() {
     assert.isAtMost(2, 5, '2 should be below 5');
     assert.isAtMost(1, 1, '1 should be equal to 1');
@@ -2006,13 +2095,42 @@ describe('assert', function () {
 
     err(function() {
       assert.isAtMost(null, 1, 'blah');
-    }, 'blah: expected null to be a number');
+    }, 'blah: expected null to be a number or a date');
 
     err(function() {
       assert.isAtMost(1, null, 'blah');
     }, 'blah: the argument to most must be a number');
   });
 
+  it('atMost (dates)', function() {
+    var now = new Date();
+    var oneSecondAgo = new Date(now.getTime() - 1000);
+    var oneSecondAfter = new Date(now.getTime() + 1000);
+
+    assert.isAtMost(oneSecondAgo, now, 'Now should be below one second ago');
+    assert.isAtMost(now, now, 'Now should be equal to now');
+
+    err(function() {
+      assert.isAtMost(oneSecondAfter, now, 'blah');
+    }, 'blah: expected ' + oneSecondAfter.toUTCString() + ' to be at most ' + now.toUTCString());
+
+    err(function() {
+      assert.isAtMost(null, now, 'blah');
+    }, 'blah: expected null to be a number or a date');
+
+    err(function() {
+      assert.isAtMost(now, null, 'blah');
+    }, 'blah: the argument to most must be a date');
+
+    err(function() {
+      assert.isAtMost(now, 1, 'blah');
+    }, 'blah: the argument to most must be a date');
+
+    err(function() {
+      assert.isAtMost(1, now, 'blah');
+    }, 'blah: the argument to most must be a number');
+  });
+
   it('change', function() {
     var obj = { value: 10, str: 'foo' },
         heroes = ['spiderman', 'superman'],
diff --git a/test/expect.js b/test/expect.js
index 6f3f45d..dddb27a 100644
--- a/test/expect.js
+++ b/test/expect.js
@@ -454,53 +454,53 @@ describe('expect', function () {
 
   it('within(start, finish)', function(){
     expect(5).to.be.within(5, 10);
-    expect(5).to.be.within(3,6);
-    expect(5).to.be.within(3,5);
-    expect(5).to.not.be.within(1,3);
-    expect('foo').to.have.length.within(2,4);
-    expect('foo').to.have.lengthOf.within(2,4);
-    expect([ 1, 2, 3 ]).to.have.length.within(2,4);
-    expect([ 1, 2, 3 ]).to.have.lengthOf.within(2,4);
+    expect(5).to.be.within(3, 6);
+    expect(5).to.be.within(3, 5);
+    expect(5).to.not.be.within(1, 3);
+    expect('foo').to.have.length.within(2, 4);
+    expect('foo').to.have.lengthOf.within(2, 4);
+    expect([ 1, 2, 3 ]).to.have.length.within(2, 4);
+    expect([ 1, 2, 3 ]).to.have.lengthOf.within(2, 4);
 
     err(function(){
-      expect(5).to.not.be.within(4,6, 'blah');
+      expect(5).to.not.be.within(4, 6, 'blah');
     }, "blah: expected 5 to not be within 4..6");
 
     err(function(){
-      expect(5, 'blah').to.not.be.within(4,6);
+      expect(5, 'blah').to.not.be.within(4, 6);
     }, "blah: expected 5 to not be within 4..6");
 
     err(function(){
-      expect(10).to.be.within(50,100, 'blah');
+      expect(10).to.be.within(50, 100, 'blah');
     }, "blah: expected 10 to be within 50..100");
 
     err(function () {
-      expect('foo').to.have.length.within(5,7, 'blah');
+      expect('foo').to.have.length.within(5, 7, 'blah');
     }, "blah: expected \'foo\' to have a length within 5..7");
 
     err(function () {
-      expect('foo', 'blah').to.have.length.within(5,7);
+      expect('foo', 'blah').to.have.length.within(5, 7);
     }, "blah: expected \'foo\' to have a length within 5..7");
 
     err(function () {
-      expect('foo').to.have.lengthOf.within(5,7, 'blah');
+      expect('foo').to.have.lengthOf.within(5, 7, 'blah');
     }, "blah: expected \'foo\' to have a length within 5..7");
 
     err(function () {
-      expect([ 1, 2, 3 ]).to.have.length.within(5,7, 'blah');
+      expect([ 1, 2, 3 ]).to.have.length.within(5, 7, 'blah');
     }, "blah: expected [ 1, 2, 3 ] to have a length within 5..7");
 
     err(function () {
-      expect([ 1, 2, 3 ]).to.have.lengthOf.within(5,7, 'blah');
+      expect([ 1, 2, 3 ]).to.have.lengthOf.within(5, 7, 'blah');
     }, "blah: expected [ 1, 2, 3 ] to have a length within 5..7");
 
     err(function () {
       expect(null).to.be.within(0, 1, 'blah');
-    }, "blah: expected null to be a number");
+    }, "blah: expected null to be a number or a date");
 
     err(function () {
       expect(null, 'blah').to.be.within(0, 1);
-    }, "blah: expected null to be a number");
+    }, "blah: expected null to be a number or a date");
 
     err(function () {
       expect(1).to.be.within(null, 1, 'blah');
@@ -520,7 +520,7 @@ describe('expect', function () {
 
     err(function () {
       expect(null).to.not.be.within(0, 1, 'blah');
-    }, "blah: expected null to be a number");
+    }, "blah: expected null to be a number or a date");
 
     err(function () {
       expect(1).to.not.be.within(null, 1, 'blah');
@@ -531,18 +531,72 @@ describe('expect', function () {
     }, "blah: the arguments to within must be numbers");
 
     err(function () {
-      expect(1).to.have.length.within(5,7, 'blah');
+      expect(1).to.have.length.within(5, 7, 'blah');
     }, "blah: expected 1 to have property 'length'");
 
     err(function () {
-      expect(1, 'blah').to.have.length.within(5,7);
+      expect(1, 'blah').to.have.length.within(5, 7);
     }, "blah: expected 1 to have property 'length'");
 
     err(function () {
-      expect(1).to.have.lengthOf.within(5,7, 'blah');
+      expect(1).to.have.lengthOf.within(5, 7, 'blah');
     }, "blah: expected 1 to have property 'length'");
   });
 
+  it('within(start, finish) (dates)', function(){
+    var now = new Date();
+    var oneSecondAgo = new Date(now.getTime() - 1000);
+    var oneSecondAfter = new Date(now.getTime() + 1000);
+    var nowUTC = now.toUTCString();
+    var beforeUTC = oneSecondAgo.toUTCString();
+    var afterUTC = oneSecondAfter.toUTCString();
+    
+    expect(now).to.be.within(oneSecondAgo, oneSecondAfter);
+    expect(now).to.be.within(now, oneSecondAfter);
+    expect(now).to.be.within(now, now);
+    expect(oneSecondAgo).to.not.be.within(now, oneSecondAfter);
+
+    err(function(){
+      expect(now).to.not.be.within(now, oneSecondAfter, 'blah');
+    }, "blah: expected " + nowUTC + " to not be within " + nowUTC + ".." + afterUTC);
+
+    err(function(){
+      expect(now, 'blah').to.not.be.within(oneSecondAgo, oneSecondAfter);
+    }, "blah: expected " + nowUTC + " to not be within " + beforeUTC + ".." + afterUTC);
+
+    err(function () {
+      expect(now).to.have.length.within(5, 7, 'blah');
+    }, "blah: expected " + nowUTC + " to have property 'length'");
+
+    err(function () {
+      expect('foo').to.have.lengthOf.within(now, 7, 'blah');
+    }, "blah: the arguments to within must be numbers");
+
+    err(function () {
+      expect(now).to.be.within(now, 1, 'blah');
+    }, "blah: the arguments to within must be dates");
+
+    err(function () {
+      expect(now).to.be.within(null, now, 'blah');
+    }, "blah: the arguments to within must be dates");
+
+    err(function () {
+      expect(now).to.be.within(now, undefined, 'blah');
+    }, "blah: the arguments to within must be dates");
+    
+    err(function () {
+      expect(now, 'blah').to.be.within(1, now);
+    }, "blah: the arguments to within must be dates");
+
+    err(function () {
+      expect(now, 'blah').to.be.within(now, 1);
+    }, "blah: the arguments to within must be dates");
+
+    err(function () {
+      expect(null).to.not.be.within(now, oneSecondAfter, 'blah');
+    }, "blah: expected null to be a number or a date");
+  });
+
   it('above(n)', function(){
     expect(5).to.be.above(2);
     expect(5).to.be.greaterThan(2);
@@ -587,11 +641,11 @@ describe('expect', function () {
 
     err(function () {
       expect(null).to.be.above(0, 'blah');
-    }, "blah: expected null to be a number");
+    }, "blah: expected null to be a number or a date");
 
     err(function () {
       expect(null, 'blah').to.be.above(0);
-    }, "blah: expected null to be a number");
+    }, "blah: expected null to be a number or a date");
 
     err(function () {
       expect(1).to.be.above(null, 'blah');
@@ -603,7 +657,7 @@ describe('expect', function () {
 
     err(function () {
       expect(null).to.not.be.above(0, 'blah');
-    }, "blah: expected null to be a number");
+    }, "blah: expected null to be a number or a date");
 
     err(function () {
       expect(1).to.not.be.above(null, 'blah');
@@ -622,6 +676,45 @@ describe('expect', function () {
     }, "blah: expected 1 to have property 'length'");
   });
 
+  it('above(n) (dates)', function(){
+    var now = new Date();
+    var oneSecondAgo = new Date(now.getTime() - 1000);
+    var oneSecondAfter = new Date(now.getTime() + 1000);
+
+    expect(now).to.be.above(oneSecondAgo);
+    expect(now).to.be.greaterThan(oneSecondAgo);
+    expect(now).to.not.be.above(now);
+    expect(now).to.not.be.above(oneSecondAfter);
+
+    err(function(){
+      expect(now).to.be.above(oneSecondAfter, 'blah');
+    }, "blah: expected " + now.toUTCString() + " to be above " + oneSecondAfter.toUTCString());
+
+    err(function(){
+      expect(10).to.not.be.above(6, 'blah');
+    }, "blah: expected 10 to be at most 6");
+
+    err(function () {
+      expect(now).to.have.length.above(4, 'blah');
+    }, "blah: expected " +  now.toUTCString() + " to have property 'length'");
+
+    err(function () {
+      expect([ 1, 2, 3 ]).to.have.length.above(now, 'blah');
+    }, "blah: the argument to above must be a number");
+
+    err(function () {
+      expect(null).to.be.above(now, 'blah');
+    }, "blah: expected null to be a number or a date");
+
+    err(function () {
+      expect(now).to.be.above(null, 'blah');
+    }, "blah: the argument to above must be a date");
+
+    err(function () {
+      expect(null).to.have.length.above(0, 'blah');
+    }, "blah: Target cannot be null or undefined.");
+  });
+
   it('least(n)', function(){
     expect(5).to.be.at.least(2);
     expect(5).to.be.at.least(5);
@@ -673,11 +766,11 @@ describe('expect', function () {
 
     err(function () {
       expect(null).to.be.at.least(0, 'blah');
-    }, "blah: expected null to be a number");
+    }, "blah: expected null to be a number or a date");
 
     err(function () {
       expect(null, 'blah').to.be.at.least(0);
-    }, "blah: expected null to be a number");
+    }, "blah: expected null to be a number or a date");
 
     err(function () {
       expect(1).to.be.at.least(null, 'blah');
@@ -689,7 +782,7 @@ describe('expect', function () {
 
     err(function () {
       expect(null).to.not.be.at.least(0, 'blah');
-    }, "blah: expected null to be a number");
+    }, "blah: expected null to be a number or a date");
 
     err(function () {
       expect(1).to.not.be.at.least(null, 'blah');
@@ -752,11 +845,11 @@ describe('expect', function () {
 
     err(function () {
       expect(null).to.be.below(0, 'blah');
-    }, "blah: expected null to be a number");
+    }, "blah: expected null to be a number or a date");
 
     err(function () {
       expect(null, 'blah').to.be.below(0);
-    }, "blah: expected null to be a number");
+    }, "blah: expected null to be a number or a date");
 
     err(function () {
       expect(1).to.be.below(null, 'blah');
@@ -768,7 +861,7 @@ describe('expect', function () {
 
     err(function () {
       expect(null).to.not.be.below(0, 'blah');
-    }, "blah: expected null to be a number");
+    }, "blah: expected null to be a number or a date");
 
     err(function () {
       expect(1).to.not.be.below(null, 'blah');
@@ -787,11 +880,53 @@ describe('expect', function () {
     }, "blah: expected 1 to have property 'length'");
   });
 
+  it('below(n) (dates)', function(){
+    var now = new Date();
+    var oneSecondAgo = new Date(now.getTime() - 1000);
+    var oneSecondAfter = new Date(now.getTime() + 1000);
+
+    expect(now).to.be.below(oneSecondAfter);
+    expect(oneSecondAgo).to.be.lessThan(now);
+    expect(now).to.not.be.below(oneSecondAgo);
+    expect(oneSecondAfter).to.not.be.below(oneSecondAgo);
+
+    err(function(){
+      expect(now).to.be.below(oneSecondAgo, 'blah');
+    }, "blah: expected " + now.toUTCString() + " to be below " + oneSecondAgo.toUTCString());
+
+    err(function(){
+      expect(now).to.not.be.below(oneSecondAfter, 'blah');
+    }, "blah: expected " + now.toUTCString() + " to be at least " + oneSecondAfter.toUTCString());
+
+    err(function () {
+      expect('foo').to.have.length.below(2, 'blah');
+    }, "blah: expected \'foo\' to have a length below 2 but got 3");
+
+    err(function () {
+      expect(null).to.be.below(now, 'blah');
+    }, "blah: expected null to be a number or a date");
+
+    err(function () {
+      expect(1).to.be.below(null, 'blah');
+    }, "blah: the argument to below must be a number");
+
+    err(function () {
+      expect(now).to.not.be.below(null, 'blah');
+    }, "blah: the argument to below must be a date");
+
+    err(function () {
+      expect(now).to.have.length.below(0, 'blah');
+    }, "blah: expected " + now.toUTCString() + " to have property 'length'");
+
+    err(function () {
+      expect('asdasd').to.have.length.below(now, 'blah');
+    }, "blah: the argument to below must be a number");
+  });
+
   it('most(n)', function(){
     expect(2).to.be.at.most(5);
     expect(2).to.be.at.most(2);
     expect(2).to.not.be.at.most(1);
-    expect(2).to.not.be.at.most(1);
     expect('foo').to.have.length.of.at.most(4);
     expect('foo').to.have.lengthOf.at.most(4);
     expect([ 1, 2, 3 ]).to.have.length.of.at.most(4);
@@ -839,11 +974,11 @@ describe('expect', function () {
 
     err(function () {
       expect(null).to.be.at.most(0, 'blah');
-    }, "blah: expected null to be a number");
+    }, "blah: expected null to be a number or a date");
 
     err(function () {
       expect(null, 'blah').to.be.at.most(0);
-    }, "blah: expected null to be a number");
+    }, "blah: expected null to be a number or a date");
 
     err(function () {
       expect(1).to.be.at.most(null, 'blah');
@@ -855,7 +990,7 @@ describe('expect', function () {
 
     err(function () {
       expect(null).to.not.be.at.most(0, 'blah');
-    }, "blah: expected null to be a number");
+    }, "blah: expected null to be a number or a date");
 
     err(function () {
       expect(1).to.not.be.at.most(null, 'blah');
@@ -874,6 +1009,59 @@ describe('expect', function () {
     }, "blah: expected 1 to have property 'length'");
   });
 
+  it('most(n) (dates)', function(){
+    var now = new Date();
+    var oneSecondBefore = new Date(now.getTime() - 1000);
+    var oneSecondAfter = new Date(now.getTime() + 1000);
+    var nowUTC = now.toUTCString();
+    var beforeUTC = oneSecondBefore.toUTCString();
+    var afterUTC = oneSecondAfter.toUTCString();
+
+    expect(now).to.be.at.most(oneSecondAfter);
+    expect(now).to.be.at.most(now);
+    expect(now).to.not.be.at.most(oneSecondBefore);
+
+    err(function(){
+      expect(now).to.be.at.most(oneSecondBefore, 'blah');
+    }, "blah: expected " + nowUTC + " to be at most " + beforeUTC);
+
+    err(function(){
+      expect(now).to.not.be.at.most(now, 'blah');
+    }, "blah: expected " + nowUTC + " to be above " + nowUTC);
+
+    err(function () {
+      expect(now).to.have.length.of.at.most(2, 'blah');
+    }, "blah: expected " + nowUTC + " to have property 'length'");
+
+    err(function () {
+      expect('foo', 'blah').to.have.length.of.at.most(now);
+    }, "blah: the argument to most must be a number");
+
+    err(function () {
+      expect([ 1, 2, 3 ]).to.not.have.length.of.at.most(now, 'blah');
+    }, "blah: the argument to most must be a number");
+
+    err(function () {
+      expect(null).to.be.at.most(now, 'blah');
+    }, "blah: expected null to be a number or a date");
+
+    err(function () {
+      expect(now, 'blah').to.be.at.most(null);
+    }, "blah: the argument to most must be a date");
+
+    err(function () {
+      expect(1).to.be.at.most(now, 'blah');
+    }, "blah: the argument to most must be a number");
+
+    err(function () {
+      expect(now, 'blah').to.be.at.most(1);
+    }, "blah: the argument to most must be a date");
+
+    err(function () {
+      expect(now).to.not.be.at.most(undefined, 'blah');
+    }, "blah: the argument to most must be a date");
+  });
+
   it('match(regexp)', function(){
     expect('foobar').to.match(/^foo/)
     expect('foobar').to.matches(/^foo/)
diff --git a/test/should.js b/test/should.js
index e10a39b..e26a642 100644
--- a/test/should.js
+++ b/test/should.js
@@ -514,7 +514,7 @@ describe('should', function() {
 
     err(function () {
       ('string').should.be.within(0, 1, 'blah');
-    }, "blah: expected 'string' to be a number");
+    }, "blah: expected 'string' to be a number or a date");
 
     err(function () {
       (1).should.be.within(null, 1, 'blah');
@@ -526,7 +526,7 @@ describe('should', function() {
 
     err(function () {
       ('string').should.not.be.within(0, 1, 'blah');
-    }, "blah: expected 'string' to be a number");
+    }, "blah: expected 'string' to be a number or a date");
 
     err(function () {
       (1).should.not.be.within(null, 1, 'blah');
@@ -545,6 +545,64 @@ describe('should', function() {
     }, "blah: expected 1 to have property 'length'");
   });
 
+  it('within(start, finish) (dates)', function(){
+    var now = new Date();
+    var oneSecondBefore = new Date(now.getTime() - 1000);
+    var oneSecondAfter = new Date(now.getTime() + 1000);
+    var nowUTC = now.toUTCString();
+    var beforeUTC = oneSecondBefore.toUTCString();
+    var afterUTC = oneSecondAfter.toUTCString();
+
+    now.should.be.within(oneSecondBefore, oneSecondAfter);
+    now.should.be.within(now, oneSecondAfter);
+    now.should.be.within(now, now);
+    oneSecondAfter.should.not.be.within(oneSecondAfter, oneSecondBefore);
+
+    err(function(){
+      now.should.not.be.within(now, oneSecondAfter, 'blah');
+    }, "blah: expected " + nowUTC + " to not be within " + nowUTC + ".." + afterUTC);
+
+    err(function(){
+      oneSecondBefore.should.be.within(now, oneSecondAfter, 'blah');
+    }, "blah: expected " + beforeUTC + " to be within " + nowUTC + ".." + afterUTC);
+
+    err(function(){
+      ([]).should.have.length.within(now, 100, 'blah');
+    }, "blah: the arguments to within must be numbers");
+
+    err(function(){
+      now.should.have.lengthOf.within(50, now, 'blah');
+    }, "blah: expected " + nowUTC + " to have property 'length'");
+
+    err(function () {
+      now.should.have.length.within(5, 7);
+    }, "expected " + nowUTC + " to have property 'length'");
+
+    err(function () {
+      (0).should.be.within(0, now, 'blah');
+    }, "blah: the arguments to within must be numbers");
+
+    err(function () {
+      (1).should.be.within(now, 1, 'blah');
+    }, "blah: the arguments to within must be numbers");
+
+    err(function () {
+      now.should.be.within(1, now, 'blah');
+    }, "blah: the arguments to within must be dates");
+
+    err(function () {
+      now.should.not.be.within(now, 1, 'blah');
+    }, "blah: the arguments to within must be dates");
+
+    err(function () {
+      now.should.not.be.within(null, now, 'blah');
+    }, "blah: the arguments to within must be dates");
+
+    err(function () {
+      now.should.not.be.within(now, null, 'blah');
+    }, "blah: the arguments to within must be dates");
+  });
+
   it('above(n)', function(){
     (5).should.be.above(2);
     (5).should.be.greaterThan(2);
@@ -569,7 +627,7 @@ describe('should', function() {
 
     err(function () {
       ('string').should.be.above(0, 'blah');
-    }, "blah: expected 'string' to be a number");
+    }, "blah: expected 'string' to be a number or a date");
 
     err(function () {
       (1).should.be.above(null, 'blah');
@@ -577,7 +635,7 @@ describe('should', function() {
 
     err(function () {
       ('string').should.not.be.above(0, 'blah');
-    }, "blah: expected 'string' to be a number");
+    }, "blah: expected 'string' to be a number or a date");
 
     err(function () {
       (1).should.not.be.above(null, 'blah');
@@ -592,6 +650,49 @@ describe('should', function() {
     }, "blah: expected 1 to have property 'length'");
   });
 
+  it('above(n) (dates)', function(){
+    var now = new Date();
+    var oneSecondAgo = new Date(now.getTime() - 1000);
+    var oneSecondAfter = new Date(now.getTime() + 1000);
+
+    now.should.be.above(oneSecondAgo);
+    oneSecondAfter.should.be.greaterThan(now);
+    now.should.not.be.above(oneSecondAfter);
+    oneSecondAgo.should.not.be.above(oneSecondAfter);
+
+    err(function(){
+      now.should.be.above(oneSecondAfter, 'blah');
+    }, "blah: expected " +  now.toUTCString() + " to be above " + oneSecondAfter.toUTCString());
+
+    err(function(){
+      now.should.not.be.above(oneSecondAgo, 'blah');
+    }, "blah: expected " + now.toUTCString() + " to be at most " + oneSecondAgo.toUTCString());
+
+    err(function(){
+      now.should.have.length.above(3, 'blah');
+    }, "blah: expected " + now.toUTCString() + " to have property 'length'");
+
+    err(function(){
+      ('string').should.have.length.above(now, 'blah');
+    }, "blah: the argument to above must be a number");
+
+    err(function () {
+      now.should.be.above(1, 'blah');
+    }, "blah: the argument to above must be a date");
+
+    err(function () {
+      now.should.be.above(null, 'blah');
+    }, "blah: the argument to above must be a date");
+
+    err(function () {
+      (1).should.not.be.above(now, 'blah');
+    }, "blah: the argument to above must be a number");
+
+    err(function () {
+      ([]).should.have.length.above(now, 'blah');
+    }, "blah: the argument to above must be a number");
+  });
+
   it('least(n)', function(){
     (5).should.be.at.least(5);
     (5).should.not.be.at.least(6);
@@ -614,7 +715,7 @@ describe('should', function() {
 
     err(function () {
       ('string').should.be.at.least(0, 'blah');
-    }, "blah: expected 'string' to be a number");
+    }, "blah: expected 'string' to be a number or a date");
 
     err(function () {
       (1).should.be.at.least(null, 'blah');
@@ -622,7 +723,7 @@ describe('should', function() {
 
     err(function () {
       ('string').should.not.be.at.least(0, 'blah');
-    }, "blah: expected 'string' to be a number");
+    }, "blah: expected 'string' to be a number or a date");
 
     err(function () {
       (1).should.not.be.at.least(null, 'blah');
@@ -653,7 +754,7 @@ describe('should', function() {
 
     err(function () {
       ('string').should.be.below(0, 'blah');
-    }, "blah: expected 'string' to be a number");
+    },  "blah: expected 'string' to be a number or a date");
 
     err(function () {
       (1).should.be.below(null, 'blah');
@@ -661,7 +762,7 @@ describe('should', function() {
 
     err(function () {
       ('string').should.not.be.below(0, 'blah');
-    }, "blah: expected 'string' to be a number");
+    }, "blah: expected 'string' to be a number or a date");
 
     err(function () {
       (1).should.not.be.below(null, 'blah');
@@ -676,6 +777,49 @@ describe('should', function() {
     }, "blah: expected 1 to have property 'length'");
   });
 
+  it('below(n) (dates)', function(){
+    var now = new Date();
+    var oneSecondAgo = new Date(now.getTime() - 1000);
+    var oneSecondAfter = new Date(now.getTime() + 1000);
+
+    now.should.be.below(oneSecondAfter);
+    oneSecondAgo.should.be.lessThan(now);
+    now.should.not.be.below(oneSecondAgo);
+    oneSecondAfter.should.not.be.below(oneSecondAgo);
+
+    err(function(){
+      now.should.be.below(now, 'blah');
+    }, "blah: expected " + now.toUTCString() + " to be below " + now.toUTCString());
+
+    err(function(){
+      now.should.not.be.below(oneSecondAfter, 'blah');
+    }, "blah: expected " + now.toUTCString() + " to be at least " + oneSecondAfter.toUTCString());
+
+    err(function(){
+      now.should.have.length.below(3, 'blah');
+    }, "blah: expected " + now.toUTCString() + " to have property 'length'");
+
+    err(function () {
+      now.should.be.below(null, 'blah');
+    }, "blah: the argument to below must be a date");
+
+    err(function () {
+      (1).should.not.be.below(now, 'blah');
+    }, "blah: the argument to below must be a number");
+
+    err(function () {
+      now.should.not.be.below(1, 'blah');
+    }, "blah: the argument to below must be a date");
+
+    err(function () {
+      now.should.not.be.below(null, 'blah');
+    }, "blah: the argument to below must be a date");
+
+    err(function () {
+      ('string').should.have.length.below(now, 'blah');
+    }, "blah: the argument to below must be a number");
+  });
+
   it('most(n)', function(){
     (2).should.be.at.most(2);
     (2).should.not.be.at.most(1);
@@ -698,7 +842,7 @@ describe('should', function() {
 
     err(function () {
       ('string').should.be.at.most(0, 'blah');
-    }, "blah: expected 'string' to be a number");
+    }, "blah: expected 'string' to be a number or a date");
 
     err(function () {
       (1).should.be.at.most(null, 'blah');
@@ -706,7 +850,7 @@ describe('should', function() {
 
     err(function () {
       ('string').should.not.be.at.most(0, 'blah');
-    }, "blah: expected 'string' to be a number");
+    }, "blah: expected 'string' to be a number or a date");
 
     err(function () {
       (1).should.not.be.at.most(null, 'blah');
@@ -721,6 +865,59 @@ describe('should', function() {
     }, "blah: expected 1 to have property 'length'");
   });
 
+  it('most(n) (dates)', function(){
+    var now = new Date();
+    var oneSecondBefore = new Date(now.getTime() - 1000);
+    var oneSecondAfter = new Date(now.getTime() + 1000);
+    var nowUTC = now.toUTCString();
+    var beforeUTC = oneSecondBefore.toUTCString();
+    var afterUTC = oneSecondAfter.toUTCString();
+
+    now.should.be.at.most(now);
+    now.should.be.at.most(oneSecondAfter);
+    now.should.not.be.at.most(oneSecondBefore);
+
+    err(function(){
+      now.should.be.at.most(oneSecondBefore, 'blah');
+    }, "blah: expected " + nowUTC + " to be at most " + beforeUTC);
+
+    err(function(){
+      now.should.not.be.at.most(oneSecondAfter, 'blah');
+    }, "blah: expected " + nowUTC + " to be above " + afterUTC);
+
+    err(function(){
+      ([]).should.have.length.of.at.most(now, 'blah');
+    }, "blah: the argument to most must be a number");
+
+    err(function(){
+      ('').should.not.have.lengthOf.at.most(now, 'blah');
+    }, "blah: the argument to most must be a number");
+
+    err(function () {
+      now.should.have.length.of.at.most(0, 'blah');
+    }, "blah: expected " + nowUTC + " to have property 'length'");
+
+    err(function () {
+      now.should.not.have.lengthOf.at.most(0, 'blah');
+    }, "blah: expected " + nowUTC + " to have property 'length'");
+
+    err(function () {
+      now.should.be.at.most(0, 'blah');
+    }, "blah: the argument to most must be a date");
+
+    err(function () {
+      now.should.be.at.most(null, 'blah');
+    }, "blah: the argument to most must be a date");
+
+    err(function () {
+      (1).should.not.be.at.most(now, 'blah');
+    }, "blah: the argument to most must be a number");
+
+    err(function () {
+      now.should.not.be.at.most(undefined);
+    }, "the argument to most must be a date");
+  });
+
   it('match(regexp)', function(){
     'foobar'.should.match(/^foo/)
     'foobar'.should.not.match(/^bar/)