diff --git a/README.md b/README.md
index 8ef42fd79..92cca3ebf 100644
--- a/README.md
+++ b/README.md
@@ -105,6 +105,7 @@ Validator | Description
**isDate(str [, options])** | check if the string 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 strings 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` determines 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', 'el-GR', 'en-AU', 'en-GB', 'en-HK', 'en-IN', 'en-NZ', 'en-US', 'en-ZA', 'en-ZM', 'eo', 'es-ES', 'fa', 'fa-AF', 'fa-IR', 'fr-FR', 'fr-CA', 'hu-HU', 'id-ID', 'it-IT', 'ku-IQ', 'nb-NO', 'nl-NL', 'nn-NO', 'pl-PL', 'pl-Pl', 'pt-BR', 'pt-PT', 'ru-RU', 'sl-SI', 'sr-RS', 'sr-RS@latin', 'sv-SE', 'tr-TR', 'uk-UA', 'vi-VN']`.
**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 is divisible by another.
+**isDuration(str)** | check if the string is a valid duration.
**isEAN(str)** | check if the string is an [EAN (European Article Number)][European Article Number].
**isEmail(str [, options])** | check if the string is an email.
`options` is an object which defaults to `{ allow_display_name: false, require_display_name: false, allow_utf8_local_part: true, require_tld: true, allow_ip_domain: false, allow_underscores: false, domain_specific_validation: false, blacklisted_chars: '', host_blacklist: [] }`. If `allow_display_name` is set to true, the validator will also match `Display Name `. If `require_display_name` is set to true, the validator will reject strings without the format `Display Name `. If `allow_utf8_local_part` is set to false, the validator will not allow any non-English UTF8 character in email address' local part. If `require_tld` is set to false, email addresses without a TLD in their domain will also be matched. If `ignore_max_length` is set to true, the validator will not check for the standard max length of an email. If `allow_ip_domain` is set to true, the validator will allow IP addresses in the host part. If `domain_specific_validation` is true, some additional validation will be enabled, e.g. disallowing certain syntactically valid email addresses that are rejected by Gmail. If `blacklisted_chars` receives a string, then the validator will reject emails that include any of the characters in the string, in the name part. If `host_blacklist` is set to an array of strings or regexp, and the part of the email after the `@` symbol matches one of the strings defined in it, the validation fails. If `host_whitelist` is set to an array of strings or regexp, and the part of the email after the `@` symbol matches none of the strings defined in it, the validation fails.
**isEmpty(str [, options])** | check if the string has a length of zero.
`options` is an object which defaults to `{ ignore_whitespace: false }`.
diff --git a/src/index.js b/src/index.js
index 3700cbd6c..25c734d33 100644
--- a/src/index.js
+++ b/src/index.js
@@ -14,6 +14,7 @@ import isIPRange from './lib/isIPRange';
import isFQDN from './lib/isFQDN';
import isDate from './lib/isDate';
import isTime from './lib/isTime';
+import isDuration from './lib/isDuration';
import isBoolean from './lib/isBoolean';
import isLocale from './lib/isLocale';
@@ -242,6 +243,7 @@ const validator = {
isTaxID,
isDate,
isTime,
+ isDuration,
isLicensePlate,
isVAT,
ibanLocales,
diff --git a/src/lib/isDuration.js b/src/lib/isDuration.js
new file mode 100644
index 000000000..cf534e6e9
--- /dev/null
+++ b/src/lib/isDuration.js
@@ -0,0 +1,27 @@
+import assertString from './util/assertString';
+
+const durationRegex = /^(-?(?:\d+)?\.?\d+)(?:\s?([a-zA-Z]+))?$/;
+
+const durationUnits = [
+ 'y', 'yr', 'yrs', 'year', 'years',
+ 'mo', 'month', 'months',
+ 'w', 'week', 'weeks',
+ 'd', 'day', 'days',
+ 'h', 'hr', 'hrs', 'hour', 'hours',
+ 'm', 'min', 'mins', 'minute', 'minutes',
+ 's', 'sec', 'secs', 'second', 'seconds',
+ 'ms', 'msec', 'msecs', 'millisecond', 'milliseconds',
+];
+
+export default function isDuration(str) {
+ assertString(str);
+ const match = str.match(durationRegex);
+ if (!match) {
+ return false;
+ }
+ const unit = match[2];
+ if (unit === undefined) {
+ return true;
+ }
+ return durationUnits.indexOf(unit.toLowerCase()) !== -1;
+}
diff --git a/test/validators.test.js b/test/validators.test.js
index 9c867efae..141043153 100644
--- a/test/validators.test.js
+++ b/test/validators.test.js
@@ -7089,6 +7089,44 @@ describe('Validators', () => {
});
});
+ it('should validate duration strings', () => {
+ test({
+ validator: 'isDuration',
+ valid: [
+ '123',
+ '123Yrs',
+ '123 Yrs',
+ '45min',
+ '45 MIN',
+ '100Ms',
+ '10 Days',
+ '7weeks',
+ '200 SEC',
+ '1d',
+ '-1.5 hours',
+ '0.5s',
+ '.5s',
+ '12.5 Hours',
+ '2h',
+ '1m',
+ '5s',
+ '1y',
+ '100ms',
+ ],
+ invalid: [
+ 'abc',
+ '123bananas',
+ '123 Yards',
+ ' 123 Hrs',
+ '123Hrs ',
+ '123 Hrs',
+ '123-Hrs',
+ '',
+ '0x10Ms',
+ ],
+ });
+ });
+
it('should validate ISINs', () => {
test({
validator: 'isISIN',