Skip to content

Commit

Permalink
refactor(isDate): add strictMode and prevent mixed delimiters (#1402)
Browse files Browse the repository at this point in the history
* fix(IsDate): prevent using mixed delimiter

* refactor(isDate): normalize date options and enforce strictMode
  • Loading branch information
tux-tn authored Sep 20, 2020
1 parent d6fff37 commit c94bfde
Show file tree
Hide file tree
Showing 3 changed files with 111 additions and 10 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ Validator | Description
**isCreditCard(str)** | check if the string is a credit card.
**isCurrency(str [, options])** | check if the string is a valid currency amount.<br/><br/>`options` is an object which defaults to `{symbol: '$', require_symbol: false, allow_space_after_symbol: false, symbol_after_digits: false, allow_negatives: true, parens_for_negatives: false, negative_sign_before_digits: false, negative_sign_after_digits: false, allow_negative_sign_placeholder: false, thousands_separator: ',', decimal_separator: '.', allow_decimal: true, require_decimal: false, digits_after_decimal: [2], allow_space_after_digits: false}`.<br/>**Note:** The array `digits_after_decimal` is filled with the exact number of digits allowed not a range, for example a range 1 to 3 will be given as [1, 2, 3].
**isDataURI(str)** | check if the string is a [data uri format](https://developer.mozilla.org/en-US/docs/Web/HTTP/data_URIs).
**isDate(input [, format])** | Check if the input is a valid date. e.g. [`2002-07-15`, new Date()].<br/><br/>`format` is a string and defaults to `YYYY/MM/DD`
**isDate(input [, options])** | Check if the input is a valid date. e.g. [`2002-07-15`, new Date()].<br/><br/> `options` is an object which can contain the keys `format`, `strictMode` and/or `delimiters`<br/><br/>`format` is a string and defaults to `YYYY/MM/DD`.<br/><br/>`strictMode` is a boolean and defaults to `false`. If `strictMode` is set to true, the validator will reject inputs different from `format`.<br/><br/> `delimiters` is an array of allowed date delimiters and defaults to `['/', '-']`.
**isDecimal(str [, options])** | check if the string represents a decimal number, such as 0.1, .3, 1.1, 1.00003, 4.0, etc.<br/><br/>`options` is an object which defaults to `{force_decimal: false, decimal_digits: '1,', locale: 'en-US'}`<br/><br/>`locale` determine the decimal separator and is one of `['ar', 'ar-AE', 'ar-BH', 'ar-DZ', 'ar-EG', 'ar-IQ', 'ar-JO', 'ar-KW', 'ar-LB', 'ar-LY', 'ar-MA', 'ar-QA', 'ar-QM', 'ar-SA', 'ar-SD', 'ar-SY', 'ar-TN', 'ar-YE', 'bg-BG', 'cs-CZ', 'da-DK', 'de-DE', 'en-AU', 'en-GB', 'en-HK', 'en-IN', 'en-NZ', 'en-US', 'en-ZA', 'en-ZM', 'es-ES', 'fr-FR', 'hu-HU', 'it-IT', 'ku-IQ', nb-NO', 'nl-NL', 'nn-NO', 'pl-PL', 'pt-BR', 'pt-PT', 'ru-RU', 'sl-SI', 'sr-RS', 'sr-RS@latin', 'sv-SE', 'tr-TR', 'uk-UA']`.<br/>**Note:** `decimal_digits` is given as a range like '1,3', a specific value like '3' or min like '1,'.
**isDivisibleBy(str, number)** | check if the string is a number that's divisible by another.
**isEAN(str)** | check if the string is an EAN (European Article Number).
Expand Down
36 changes: 30 additions & 6 deletions src/lib/isDate.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
import merge from './util/merge';

const default_date_options = {
format: 'YYYY/MM/DD',
delimiters: ['/', '-'],
strictMode: false,
};

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);
}
Expand All @@ -13,11 +21,23 @@ function zip(date, format) {
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 = {};
export default function isDate(input, options) {
if (typeof options === 'string') { // Allow backward compatbility for old format isDate(input [, format])
options = merge({ format: options }, default_date_options);
} else {
options = merge(options, default_date_options);
}
if (typeof input === 'string' && isValidFormat(options.format)) {
const formatDelimiter = options.delimiters
.find(delimiter => options.format.indexOf(delimiter) !== -1);
const dateDelimiter = options.strictMode
? formatDelimiter
: options.delimiters.find(delimiter => input.indexOf(delimiter) !== -1);
const dateAndFormat = zip(
input.split(dateDelimiter),
options.format.toLowerCase().split(formatDelimiter)
);
const dateObj = {};

for (const [dateWord, formatWord] of dateAndFormat) {
if (dateWord.length !== formatWord.length) {
Expand All @@ -30,5 +50,9 @@ export default function isDate(input, format = 'YYYY/MM/DD') {
return new Date(`${dateObj.m}/${dateObj.d}/${dateObj.y}`).getDate() === +dateObj.d;
}

return Object.prototype.toString.call(input) === '[object Date]' && isFinite(input);
if (!options.strictMode) {
return Object.prototype.toString.call(input) === '[object Date]' && isFinite(input);
}

return false;
}
83 changes: 80 additions & 3 deletions test/validators.js
Original file line number Diff line number Diff line change
Expand Up @@ -8888,11 +8888,12 @@ describe('Validators', () => {
'2020-02-30', // invalid date
'2019-02-29', // non-leap year
'2020-04-31', // invalid date
'2020/03-15', // mixed delimiter
],
});
test({
validator: 'isDate',
args: ['DD/MM/YYYY'],
args: ['DD/MM/YYYY'], // old format for backward compatibility
valid: [
'15-07-2002',
'15/07/2002',
Expand All @@ -8902,23 +8903,40 @@ describe('Validators', () => {
'15-7-2002',
'15/7/02',
'15-7-02',
'15-07/2002',
],
});
test({
validator: 'isDate',
args: ['DD/MM/YY'],
args: [{ format: 'DD/MM/YYYY' }],
valid: [
'15-07-2002',
'15/07/2002',
],
invalid: [
'15/7/2002',
'15-7-2002',
'15/7/02',
'15-7-02',
'15-07/2002',
],
});
test({
validator: 'isDate',
args: [{ format: 'DD/MM/YY' }],
valid: [
'15-07-02',
'15/07/02',
],
invalid: [
'15/7/2002',
'15-7-2002',
'15/07-02',
],
});
test({
validator: 'isDate',
args: ['D/M/YY'],
args: [{ format: 'D/M/YY' }],
valid: [
'5-7-02',
'5/7/02',
Expand All @@ -8927,6 +8945,65 @@ describe('Validators', () => {
'5/07/02',
'15/7/02',
'15-7-02',
'5/7-02',
],
});
test({
validator: 'isDate',
args: [{ format: 'DD/MM/YYYY', strictMode: true }],
valid: [
'15/07/2002',
],
invalid: [
'15-07-2002',
'15/7/2002',
'15-7-2002',
'15/7/02',
'15-7-02',
'15-07/2002',
],
});
test({
validator: 'isDate',
args: [{ strictMode: true }],
valid: [
'2020/01/15',
'2014/02/15',
'2014/03/15',
'2020/02/29',
],
invalid: [
'2014-02-15',
'2020-02-29',
'15-07/2002',
new Date(),
new Date([2014, 2, 15]),
new Date('2014-03-15'),
],
});
test({
validator: 'isDate',
args: [{ delimiters: ['/', ' '] }],
valid: [
new Date(),
new Date([2014, 2, 15]),
new Date('2014-03-15'),
'2020/02/29',
'2020 02 29',
],
invalid: [
'2020-02-29',
'',
'15072002',
null,
undefined,
{ year: 2002, month: 7, day: 15 },
42,
{ toString() { return '[object Date]'; } },
'2020/02/30',
'2019/02/29',
'2020/04/31',
'2020/03-15',
],
});
});
Expand Down

0 comments on commit c94bfde

Please sign in to comment.