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',
],
});
});