diff --git a/README.md b/README.md index becdc74de..0f3393a25 100644 --- a/README.md +++ b/README.md @@ -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.

`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}`.
**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()].

`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()].

`options` is an object which can contain the keys `format`, `strictMode` and/or `delimiters`

`format` is a string and defaults to `YYYY/MM/DD`.

`strictMode` is a boolean and defaults to `false`. If `strictMode` is set to true, the validator will reject inputs different from `format`.

`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.

`options` is an object which defaults to `{force_decimal: false, decimal_digits: '1,', locale: 'en-US'}`

`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']`.
**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). diff --git a/src/lib/isDate.js b/src/lib/isDate.js index ff3df4ecf..110c2193a 100644 --- a/src/lib/isDate.js +++ b/src/lib/isDate.js @@ -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); } @@ -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) { @@ -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; } diff --git a/test/validators.js b/test/validators.js index 700868b87..dcae75229 100644 --- a/test/validators.js +++ b/test/validators.js @@ -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', @@ -8902,11 +8903,27 @@ 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', @@ -8914,11 +8931,12 @@ describe('Validators', () => { 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', @@ -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', ], }); });