-
-
Notifications
You must be signed in to change notification settings - Fork 2.3k
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
feat: add new isDate() validator #1270
Changes from all commits
2f76d92
5583076
656cc97
47d3edc
6cd63d9
f82011f
4a8f8db
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
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.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } | ||
|
||
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; } | ||
|
||
function _createForOfIteratorHelper(o) { if (typeof Symbol === "undefined" || o[Symbol.iterator] == null) { if (Array.isArray(o) || (o = _unsupportedIterableToArray(o))) { var i = 0; var F = function F() {}; return { s: F, n: function n() { if (i >= o.length) return { done: true }; return { done: false, value: o[i++] }; }, e: function e(_e2) { throw _e2; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var it, normalCompletion = true, didErr = false, err; return { s: function s() { it = o[Symbol.iterator](); }, n: function n() { var step = it.next(); normalCompletion = step.done; return step; }, e: function e(_e3) { didErr = true; err = _e3; }, f: function f() { try { if (!normalCompletion && it["return"] != null) it["return"](); } finally { if (didErr) throw err; } } }; } | ||
|
||
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 isValidFormat(format) { | ||
return /(^(y{4}|y{2})[\/-](m{1,2})[\/-](d{1,2})$)|(^(m{1,2})[\/-](d{1,2})[\/-]((y{4}|y{2})$))|(^(d{1,2})[\/-](m{1,2})[\/-]((y{4}|y{2})$))/gi.test(format); | ||
} | ||
|
||
function zip(date, format) { | ||
var zippedArr = [], | ||
len = Math.min(date.length, format.length); | ||
|
||
for (var i = 0; i < len; i++) { | ||
zippedArr.push([date[i], format[i]]); | ||
} | ||
|
||
return zippedArr; | ||
} | ||
|
||
export default function isDate(input) { | ||
var format = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 'YYYY/MM/DD'; | ||
|
||
if (typeof input === 'string' && isValidFormat(format)) { | ||
var splitter = /[-/]/, | ||
dateAndFormat = zip(input.split(splitter), format.toLowerCase().split(splitter)), | ||
dateObj = {}; | ||
|
||
var _iterator = _createForOfIteratorHelper(dateAndFormat), | ||
_step; | ||
|
||
try { | ||
for (_iterator.s(); !(_step = _iterator.n()).done;) { | ||
var _step$value = _slicedToArray(_step.value, 2), | ||
dateWord = _step$value[0], | ||
formatWord = _step$value[1]; | ||
|
||
if (dateWord.length !== formatWord.length) { | ||
return false; | ||
} | ||
|
||
dateObj[formatWord.charAt(0)] = dateWord; | ||
} | ||
} catch (err) { | ||
_iterator.e(err); | ||
} finally { | ||
_iterator.f(); | ||
} | ||
|
||
return new Date("".concat(dateObj.m, "/").concat(dateObj.d, "/").concat(dateObj.y)).getDate() === +dateObj.d; | ||
} | ||
|
||
return Object.prototype.toString.call(input) === '[object Date]' && isFinite(input); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
"use strict"; | ||
|
||
Object.defineProperty(exports, "__esModule", { | ||
value: true | ||
}); | ||
exports.default = isDate; | ||
|
||
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.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } | ||
|
||
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; } | ||
|
||
function _createForOfIteratorHelper(o) { if (typeof Symbol === "undefined" || o[Symbol.iterator] == null) { if (Array.isArray(o) || (o = _unsupportedIterableToArray(o))) { var i = 0; var F = function F() {}; return { s: F, n: function n() { if (i >= o.length) return { done: true }; return { done: false, value: o[i++] }; }, e: function e(_e2) { throw _e2; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var it, normalCompletion = true, didErr = false, err; return { s: function s() { it = o[Symbol.iterator](); }, n: function n() { var step = it.next(); normalCompletion = step.done; return step; }, e: function e(_e3) { didErr = true; err = _e3; }, f: function f() { try { if (!normalCompletion && it.return != null) it.return(); } finally { if (didErr) throw err; } } }; } | ||
|
||
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 isValidFormat(format) { | ||
return /(^(y{4}|y{2})[\/-](m{1,2})[\/-](d{1,2})$)|(^(m{1,2})[\/-](d{1,2})[\/-]((y{4}|y{2})$))|(^(d{1,2})[\/-](m{1,2})[\/-]((y{4}|y{2})$))/gi.test(format); | ||
} | ||
|
||
function zip(date, format) { | ||
var zippedArr = [], | ||
len = Math.min(date.length, format.length); | ||
|
||
for (var i = 0; i < len; i++) { | ||
zippedArr.push([date[i], format[i]]); | ||
} | ||
|
||
return zippedArr; | ||
} | ||
|
||
function isDate(input) { | ||
var format = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 'YYYY/MM/DD'; | ||
|
||
if (typeof input === 'string' && isValidFormat(format)) { | ||
var splitter = /[-/]/, | ||
dateAndFormat = zip(input.split(splitter), format.toLowerCase().split(splitter)), | ||
dateObj = {}; | ||
|
||
var _iterator = _createForOfIteratorHelper(dateAndFormat), | ||
_step; | ||
|
||
try { | ||
for (_iterator.s(); !(_step = _iterator.n()).done;) { | ||
var _step$value = _slicedToArray(_step.value, 2), | ||
dateWord = _step$value[0], | ||
formatWord = _step$value[1]; | ||
|
||
if (dateWord.length !== formatWord.length) { | ||
return false; | ||
} | ||
|
||
dateObj[formatWord.charAt(0)] = dateWord; | ||
} | ||
} catch (err) { | ||
_iterator.e(err); | ||
} finally { | ||
_iterator.f(); | ||
} | ||
|
||
return new Date("".concat(dateObj.m, "/").concat(dateObj.d, "/").concat(dateObj.y)).getDate() === +dateObj.d; | ||
} | ||
|
||
return Object.prototype.toString.call(input) === '[object Date]' && isFinite(input); | ||
} | ||
|
||
module.exports = exports.default; | ||
module.exports.default = exports.default; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
function isValidFormat(format) { | ||
return /(^(y{4}|y{2})[\/-](m{1,2})[\/-](d{1,2})$)|(^(m{1,2})[\/-](d{1,2})[\/-]((y{4}|y{2})$))|(^(d{1,2})[\/-](m{1,2})[\/-]((y{4}|y{2})$))/gi.test(format); | ||
} | ||
|
||
function zip(date, format) { | ||
const zippedArr = [], | ||
len = Math.min(date.length, format.length); | ||
|
||
for (let i = 0; i < len; i++) { | ||
zippedArr.push([date[i], format[i]]); | ||
} | ||
|
||
return zippedArr; | ||
} | ||
|
||
export default function isDate(input, format = 'YYYY/MM/DD') { | ||
if (typeof input === 'string' && isValidFormat(format)) { | ||
const splitter = /[-/]/, | ||
dateAndFormat = zip(input.split(splitter), format.toLowerCase().split(splitter)), | ||
dateObj = {}; | ||
|
||
for (const [dateWord, formatWord] of dateAndFormat) { | ||
if (dateWord.length !== formatWord.length) { | ||
return false; | ||
} | ||
|
||
dateObj[formatWord.charAt(0)] = dateWord; | ||
} | ||
|
||
return new Date(`${dateObj.m}/${dateObj.d}/${dateObj.y}`).getDate() === +dateObj.d; | ||
} | ||
|
||
return Object.prototype.toString.call(input) === '[object Date]' && isFinite(input); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 |
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -8025,4 +8025,67 @@ describe('Validators', () => { | |
], | ||
}); | ||
}); | ||
|
||
it('should validate date', () => { | ||
test({ | ||
validator: 'isDate', | ||
valid: [ | ||
new Date(), | ||
new Date([2014, 2, 15]), | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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('2014-03-15'), | ||
'2020/02/29', | ||
], | ||
invalid: [ | ||
'', | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Try add the following test cases:
I presume all those will pass as true (or fail as false). We will need to handle these cases. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Since:
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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
we can do that! my bad
any better way ? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'd not thought of that way, I think I like it! 👍 There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 we need to keep things simple and general any suggestion? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sure, for now let's just support those that are supported by However, I noticed something else, look at this?
Perhaps this are some of the intricacies that led to deprecating the first What if we allowed the user to supply the format from a list of predefined formats?
Then transform this to a There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. |
||
'15072002', | ||
null, | ||
undefined, | ||
{ year: 2002, month: 7, day: 15 }, | ||
42, | ||
{ toString() { return '[object Date]'; } }, // faking | ||
'2020-02-30', // invalid date | ||
'2019-02-29', // non-leap year | ||
'2020-04-31', // invalid date | ||
], | ||
}); | ||
test({ | ||
validator: 'isDate', | ||
args: ['DD/MM/YYYY'], | ||
valid: [ | ||
'15-07-2002', | ||
'15/07/2002', | ||
], | ||
invalid: [ | ||
'15/7/2002', | ||
'15-7-2002', | ||
'15/7/02', | ||
'15-7-02', | ||
], | ||
}); | ||
test({ | ||
validator: 'isDate', | ||
args: ['DD/MM/YY'], | ||
valid: [ | ||
'15-07-02', | ||
'15/07/02', | ||
], | ||
invalid: [ | ||
'15/7/2002', | ||
'15-7-2002', | ||
], | ||
}); | ||
test({ | ||
validator: 'isDate', | ||
args: ['D/M/YY'], | ||
valid: [ | ||
'5-7-02', | ||
'5/7/02', | ||
], | ||
invalid: [ | ||
'5/07/02', | ||
'15/7/02', | ||
'15-7-02', | ||
], | ||
}); | ||
}); | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd thought we'll come up with multiple regex depending with the format provided, starting with
YYYY/MM/DD
as a default.We said we'll keep it simple as just date string only, nothing else.