Skip to content

feat: add new isDate() validator #1270

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,7 @@ Sanitizer | Description
**trim(input [, chars])** | trim characters (whitespace by default) from both sides of the input.
**whitelist(input, chars)** | remove characters that do not appear in the whitelist. The characters are used in a RegExp and so you will need to escape some chars, e.g. `whitelist(input, '\\[\\]')`.
**isSlug** | Check if the string is of type slug. `Options` allow a single hyphen between string. e.g. [`cn-cn`, `cn-c-c`]
**isDate** | Check if the input is a valid date. e.g. [`2002-07-15`, new Date()]

### XSS Sanitization

Expand Down
4 changes: 3 additions & 1 deletion es/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import isMACAddress from './lib/isMACAddress';
import isIP from './lib/isIP';
import isIPRange from './lib/isIPRange';
import isFQDN from './lib/isFQDN';
import isDate from './lib/isDate';
import isBoolean from './lib/isBoolean';
import isLocale from './lib/isLocale';
import isAlpha, { locales as isAlphaLocales } from './lib/isAlpha';
Expand Down Expand Up @@ -176,6 +177,7 @@ var validator = {
isWhitelisted: isWhitelisted,
normalizeEmail: normalizeEmail,
toString: toString,
isSlug: isSlug
isSlug: isSlug,
isDate: isDate
};
export default validator;
7 changes: 7 additions & 0 deletions es/lib/isDate.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export default function isDate(input) {
if (typeof input === 'string') {
return isFinite(Date.parse(input));
}

return Object.prototype.toString.call(input) === '[object Date]' && isFinite(input);
}
10 changes: 7 additions & 3 deletions es/lib/isEmail.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
function _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _nonIterableRest(); }
function _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest(); }
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove this file from the diff since it's unrelated to the PR.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this was auto-generated

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just remove it before committing. This is because of the difference in the build system.


function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance"); }
function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }

function _iterableToArrayLimit(arr, i) { if (!(Symbol.iterator in Object(arr) || Object.prototype.toString.call(arr) === "[object Arguments]")) { return; } var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"] != null) _i["return"](); } finally { if (_d) throw _e; } } return _arr; }
function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(n); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }

function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) { arr2[i] = arr[i]; } return arr2; }

function _iterableToArrayLimit(arr, i) { if (typeof Symbol === "undefined" || !(Symbol.iterator in Object(arr))) return; var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"] != null) _i["return"](); } finally { if (_d) throw _e; } } return _arr; }

function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; }

Expand Down
5 changes: 4 additions & 1 deletion index.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ var _isIPRange = _interopRequireDefault(require("./lib/isIPRange"));

var _isFQDN = _interopRequireDefault(require("./lib/isFQDN"));

var _isDate = _interopRequireDefault(require("./lib/isDate"));

var _isBoolean = _interopRequireDefault(require("./lib/isBoolean"));

var _isLocale = _interopRequireDefault(require("./lib/isLocale"));
Expand Down Expand Up @@ -276,7 +278,8 @@ var validator = {
isWhitelisted: _isWhitelisted.default,
normalizeEmail: _normalizeEmail.default,
toString: toString,
isSlug: _isSlug.default
isSlug: _isSlug.default,
isDate: _isDate.default
};
var _default = validator;
exports.default = _default;
Expand Down
17 changes: 17 additions & 0 deletions lib/isDate.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
"use strict";

Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = isDate;

function isDate(input) {
if (typeof input === 'string') {
return isFinite(Date.parse(input));
}

return Object.prototype.toString.call(input) === '[object Date]' && isFinite(input);
}

module.exports = exports.default;
module.exports.default = exports.default;
10 changes: 7 additions & 3 deletions lib/isEmail.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,15 @@ var _isIP = _interopRequireDefault(require("./isIP"));

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

function _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _nonIterableRest(); }
function _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest(); }

function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance"); }
function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }

function _iterableToArrayLimit(arr, i) { if (!(Symbol.iterator in Object(arr) || Object.prototype.toString.call(arr) === "[object Arguments]")) { return; } var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"] != null) _i["return"](); } finally { if (_d) throw _e; } } return _arr; }
function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(n); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }

function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) { arr2[i] = arr[i]; } return arr2; }

function _iterableToArrayLimit(arr, i) { if (typeof Symbol === "undefined" || !(Symbol.iterator in Object(arr))) return; var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"] != null) _i["return"](); } finally { if (_d) throw _e; } } return _arr; }

function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; }

Expand Down
2 changes: 2 additions & 0 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import isMACAddress from './lib/isMACAddress';
import isIP from './lib/isIP';
import isIPRange from './lib/isIPRange';
import isFQDN from './lib/isFQDN';
import isDate from './lib/isDate';

import isBoolean from './lib/isBoolean';
import isLocale from './lib/isLocale';
Expand Down Expand Up @@ -206,6 +207,7 @@ const validator = {
normalizeEmail,
toString,
isSlug,
isDate,
};

export default validator;
6 changes: 6 additions & 0 deletions src/lib/isDate.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export default function isDate(input) {
if (typeof input === 'string') {
return isFinite(Date.parse(input));
}
return Object.prototype.toString.call(input) === '[object Date]' && isFinite(input);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What this last call for? The assumption is that input will always be a string. You need to call assertString() at the top of the function...

}
30 changes: 30 additions & 0 deletions test/validators.js
Original file line number Diff line number Diff line change
Expand Up @@ -8025,4 +8025,34 @@ describe('Validators', () => {
],
});
});

it('should validate date', () => {
test({
validator: 'isDate',
valid: [
new Date(),
new Date([2014, 2, 15]),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This means we allow users to send created dates using the new Date() method?
I thought, we just need to pass a string. But, it is a good idea.

new Date('2014-03-15'),
'2002-07-15',
'2002/07/15',
'07/15/2002',
'07-15-2002',
'2015-07-15T07:00:00+0000',
],
invalid: [
'',
Copy link
Member

@profnandaa profnandaa Mar 22, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Try add the following test cases:

2020-02-30, // invalid date
2019-02-29, // non-leap year
2020-04-31, // invalid date

I presume all those will pass as true (or fail as false). We will need to handle these cases.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since:

> new Date('2020-02-31')
2020-03-02T00:00:00.000Z
> new Date('2020-04-31')
2020-05-01T00:00:00.000Z

Copy link
Contributor Author

@mum-never-proud mum-never-proud Mar 22, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nice catch!

a simple validation would be

> new Date('2020-02-30').getUTCDate()
1
> new Date('2019-02-29').getUTCDate()
1
> new Date('2020-02-29').getUTCDate()
29

we can compare it with the date supplied

the problem here is with the date format some may give as YYYY-MM-DD or MM-DD-YYYY

we can do that! my bad

any suggestion?

any better way ?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd not thought of that way, I think I like it! 👍

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hmm looks like there are many edge cases to be considered in this case,

what if a user supplies

MM-DD-YYYY
YYYY-MM-DD
YY-MM-DD
MM-DD-YY

we need to keep things simple and general

any suggestion?

Copy link
Member

@profnandaa profnandaa Mar 22, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure, for now let's just support those that are supported by Date(), and strictly YYYY. Need to add that note on the README.

However, I noticed something else, look at this?

> new Date('02-02-2019')
2019-02-01T21:00:00.000Z // .getUTCDate() = 1
> new Date('2019-02-02')
2019-02-02T00:00:00.000Z // .getUTCDate() = 2

Perhaps this are some of the intricacies that led to deprecating the first isDate() from the library. 🤔

What if we allowed the user to supply the format from a list of predefined formats?

MM-DD-YYYY
DD-MM-YYYY
YYYY-MM-DD

Then transform this to a YYYY-MM-DD format before running it through new Date()

Copy link
Contributor Author

@mum-never-proud mum-never-proud Mar 23, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

check out the fiddle

https://jsfiddle.net/hvrwp07e/1/

I have done some tweaking, I have some stuff to do so right now I don't have time to think about edge cases

feel free to introduce some edge case or enhance the code

let me know if you are aware of any edge cases

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking forward to that we can do more addition on edge cases in the future and update the readme accordingly.

'1000033',
'foo',
'15/07/2015', // DD-MM-YYYY invalid
'15-07-2015', // DD-MM-YYYY invalid
new Date('not a valid date'),
{ toString() { return '[object Date]'; } },
null,
undefined,
42,
[2002, 7, 15],
{ year: 2002, month: 7, day: 15 },
],
});
});
});
37 changes: 30 additions & 7 deletions validator.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,18 +43,15 @@ function _typeof(obj) {
}

function _slicedToArray(arr, i) {
return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _nonIterableRest();
return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest();
}

function _arrayWithHoles(arr) {
if (Array.isArray(arr)) return arr;
}

function _iterableToArrayLimit(arr, i) {
if (!(Symbol.iterator in Object(arr) || Object.prototype.toString.call(arr) === "[object Arguments]")) {
return;
}

if (typeof Symbol === "undefined" || !(Symbol.iterator in Object(arr))) return;
var _arr = [];
var _n = true;
var _d = false;
Expand All @@ -80,8 +77,25 @@ function _iterableToArrayLimit(arr, i) {
return _arr;
}

function _unsupportedIterableToArray(o, minLen) {
if (!o) return;
if (typeof o === "string") return _arrayLikeToArray(o, minLen);
var n = Object.prototype.toString.call(o).slice(8, -1);
if (n === "Object" && o.constructor) n = o.constructor.name;
if (n === "Map" || n === "Set") return Array.from(n);
if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen);
}

function _arrayLikeToArray(arr, len) {
if (len == null || len > arr.length) len = arr.length;

for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i];

return arr2;
}

function _nonIterableRest() {
throw new TypeError("Invalid attempt to destructure non-iterable instance");
throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
}

function assertString(input) {
Expand Down Expand Up @@ -846,6 +860,14 @@ function isIPRange(str) {
return isIP(parts[0], 4) && parts[1] <= 32 && parts[1] >= 0;
}

function isDate(input) {
if (typeof input === 'string') {
return isFinite(Date.parse(input));
}

return Object.prototype.toString.call(input) === '[object Date]' && isFinite(input);
}

function isBoolean(str) {
assertString(str);
return ['true', 'false', '1', '0'].indexOf(str) >= 0;
Expand Down Expand Up @@ -2589,7 +2611,8 @@ var validator = {
isWhitelisted: isWhitelisted,
normalizeEmail: normalizeEmail,
toString: toString,
isSlug: isSlug
isSlug: isSlug,
isDate: isDate
};

return validator;
Expand Down
2 changes: 1 addition & 1 deletion validator.min.js

Large diffs are not rendered by default.