Repository: validatorjs/validator.js
Pull request #2381 (2024-03-24 01:15:39)

Add validation Hyphen before year is not allowed
<!--
Add a descriptive title textbox above, e.g.
feat(validatorName): brief title of what has been done
-->

<!--- briefly describe what you have done in this PR --->

<!--- provide some (credible) references showing the structure of the data to be validated, if applicable --->

## Checklist

- [ ] PR contains only changes related; no stray files, etc.
- [ ] README updated (where applicable)
- [ ] Tests written (where applicable)
- [ ] References provided in PR (where applicable)


codecov[bot]: ## [Codecov](https://app.codecov.io/gh/validatorjs/validator.js/pull/2381?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=validatorjs) Report
All modified and coverable lines are covered by tests :white_check_mark:
> Project coverage is 100.00%. Comparing base [(`4197b86`)](https://app.codecov.io/gh/validatorjs/validator.js/commit/4197b8632522818164cc05d8d6cb44c98eb3decd?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=validatorjs) to head [(`8a49a17`)](https://app.codecov.io/gh/validatorjs/validator.js/pull/2381?dropdown=coverage&src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=validatorjs).


<details><summary>Additional details and impacted files</summary>


```diff
@@            Coverage Diff            @@
##            master     #2381   +/-   ##
=========================================
  Coverage   100.00%   100.00%           
=========================================
  Files          107       107           
  Lines         2449      2451    +2     
  Branches       619       620    +1     
=========================================
+ Hits          2449      2451    +2     
```



</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/validatorjs/validator.js/pull/2381?dropdown=coverage&src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=validatorjs).   
:loudspeaker: Have feedback on the report? [Share it here](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=validatorjs).

Sumit-tech-joshi: Hi @WikiRik  Can you explain what test case should i add as adding test case for this scenario as below will fail the test case.

'-2020-03-15'
michiwo1:  I think it is good except for the tests.
Sumit-tech-joshi: Added test cases as requested
Sumit-tech-joshi: @WikiRik , When I added the new Date('-2014-03-15') to the test cases, it seems to be failing. Upon investigation, I found that the new Date('-2014-03-15') function returns a valid "full date string" (Sat Mar 15 2014 00:00:00 GMT-0700 (Pacific Daylight Saving Time)).  Thank you for your attention to this matter.

Please see the screenshots:
<img width="898" alt="Screenshot 2024-03-29 at 11 13 18 AM" src="https://github.com/validatorjs/validator.js/assets/47482270/d2e9e53a-3cbb-479a-8ea9-643924e692f5">
<img width="613" alt="Screenshot 2024-03-29 at 11 13 31 AM" src="https://github.com/validatorjs/validator.js/assets/47482270/f1f405f7-a459-4ac9-b722-5ba3c0658708">


Patch:
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 21686d8..b4e5329 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -9,7 +9,7 @@ jobs:
     runs-on: ubuntu-20.04
     strategy:
       matrix:
-        node-version: [14, 12, 10, 8, 6]
+        node-version: [20, 18, 16, 14, 12, 10, 8, 6]
     name: Run tests on Node.js ${{ matrix.node-version }}
     steps:
     - name: Setup Node.js ${{ matrix.node-version }}
@@ -20,10 +20,10 @@ jobs:
     - name: Checkout repository
       uses: actions/checkout@v2
     - name: Install dependencies
-      run: npm install
+      run:  npm install --legacy-peer-deps
     - name: Run tests
       run: npm test
-    - if: matrix.node-version == 14
+    - if: matrix.node-version == 20
       name: Send coverage info to Codecov
       uses: codecov/codecov-action@v1
       with:
diff --git a/.github/workflows/npm-publish.yml b/.github/workflows/npm-publish.yml
index b4b62f1..ccc202c 100644
--- a/.github/workflows/npm-publish.yml
+++ b/.github/workflows/npm-publish.yml
@@ -5,20 +5,23 @@ on:
 jobs:
   publish:
     runs-on: ubuntu-20.04
+    permissions:
+      contents: read
+      id-token: write
     steps:
-    - name: Setup Node.js 14
-      uses: actions/setup-node@v2-beta
+    - name: Setup Node.js 18
+      uses: actions/setup-node@v3
       with:
-        node-version: 14
+        node-version: 18
         check-latest: true
         registry-url: https://registry.npmjs.org/
     - name: Checkout Repository
-      uses: actions/checkout@v2
+      uses: actions/checkout@v3
     - name: Install Dependencies
       run: npm install
     - name: Run Tests
       run: npm test
     - name: Publish Package to NPM Registry
-      run: npm publish
+      run: npm publish --provenance
       env:
         NODE_AUTH_TOKEN: ${{secrets.NPM_SECRET}}
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
new file mode 100644
index 0000000..c7da041
--- /dev/null
+++ b/CONTRIBUTING.md
@@ -0,0 +1,37 @@
+# Contributing to validator.js
+Welcome to validator.js repository!! We appreciate your interest in contributing to this open library and for helping our community grow. 
+
+## How to Contribute
+### Code Contribution
+In general, we follow the "fork-and-pull" Git workflow.
+
+1. [Fork](https://docs.github.com/en/get-started/exploring-projects-on-github/contributing-to-a-project) the repository on GitHub
+2. Clone the project to your local machine
+3. Work on your fork
+    * Make your changes and additions
+        - Most of your changes should be focused on src/ and test/ folders and/or [README.md](https://github.com/validatorjs/validator.js/blob/master/README.md).
+        - Files such as validator.js, validator.min.js and files in lib/ folder are autogenerated when running tests (npm test) and need not to be changed **manually**.
+    * Change or add tests if needed
+    * Run tests and make sure they pass
+    * Add changes to README.md if needed
+4. Commit changes to your own branch
+5. **Make sure** you merge the latest from "upstream" and resolve conflicts if there is any
+6. Repeat step 3(3) above
+7. Push your work back up to your fork
+8. Submit a Pull request so that we can review your changes
+
+#### Run Tests
+Tests are using mocha. To run the tests use:
+
+```sh
+$ npm test
+```
+
+### Financial Contribution
+We welcome financial contributions on our [open collective](https://opencollective.com/validatorjs).
+
+You can opt to become a [backer](https://opencollective.com/validatorjs#backer) or a [sponsor](https://opencollective.com/validatorjs#sponsor) and help our project sustain over time.
+
+Thank you to the people who have already contributed:
+
+<a href="https://github.com/validatorjs/validator.js/graphs/contributors"><img src="https://opencollective.com/validatorjs/contributors.svg?width=890" /></a>
\ No newline at end of file
diff --git a/README.md b/README.md
index 4dfd259..0b66ca7 100644
--- a/README.md
+++ b/README.md
@@ -72,16 +72,6 @@ CDN
 <script src="https://unpkg.com/validator@latest/validator.min.js"></script>
 ```
 
-## Contributors
-
-[Become a backer](https://opencollective.com/validatorjs#backer)
-
-[Become a sponsor](https://opencollective.com/validatorjs#sponsor)
-
-Thank you to the people who have already contributed:
-
-<a href="https://github.com/validatorjs/validator.js/graphs/contributors"><img src="https://opencollective.com/validatorjs/contributors.svg?width=890" /></a>
-
 ## Validators
 
 Here is a list of the validators currently available.
@@ -90,16 +80,17 @@ Validator                               | Description
 --------------------------------------- | --------------------------------------
 **contains(str, seed [, options])**    | check if the string contains the seed.<br/><br/>`options` is an object that defaults to `{ ignoreCase: false, minOccurrences: 1 }`.<br />Options: <br/> `ignoreCase`: Ignore case when doing comparison, default false.<br/>`minOccurences`: Minimum number of occurrences for the seed in the string. Defaults to 1.
 **equals(str, comparison)**             | check if the string matches the comparison.
+**isAbaRouting(str)**               | check if the string is an ABA routing number for US bank account / cheque.
 **isAfter(str [, options])**            | check if the string is a date that is after the specified date.<br/><br/>`options` is an object that defaults to `{ comparisonDate: Date().toString() }`.<br/>**Options:**<br/>`comparisonDate`: Date to compare to. Defaults to `Date().toString()` (now).
-**isAlpha(str [, locale, options])**    | check if the string contains only letters (a-zA-Z).<br/><br/>`locale` 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', 'bn', 'cs-CZ', 'da-DK', 'de-DE', 'el-GR', 'en-AU', 'en-GB', 'en-HK', 'en-IN', 'en-NZ', 'en-US', 'en-ZA', 'en-ZM', 'es-ES', 'fa-IR', 'fi-FI', 'fr-CA', 'fr-FR', 'he', 'hi-IN', 'hu-HU', 'it-IT', 'kk-KZ', 'ko-KR', 'ja-JP', 'ku-IQ', 'nb-NO', 'nl-NL', 'nn-NO', 'pl-PL', 'pt-BR', 'pt-PT', 'ru-RU', 'si-LK', 'sl-SI', 'sk-SK', 'sr-RS', 'sr-RS@latin', 'sv-SE', 'th-TH', 'tr-TR', 'uk-UA']` and defaults to `en-US`. Locale list is `validator.isAlphaLocales`. `options` is an optional object that can be supplied with the following key(s): `ignore` which can either be a String or RegExp of characters to be ignored e.g. " -" will ignore spaces and -'s.
-**isAlphanumeric(str [, locale, options])**      | check if the string contains only letters and numbers (a-zA-Z0-9).<br/><br/>`locale` 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', 'bn', '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', 'es-ES', 'fa-IR', 'fi-FI', 'fr-CA', 'fr-FR', 'he', 'hi-IN', 'hu-HU', 'it-IT', 'kk-KZ', 'ko-KR', 'ja-JP','ku-IQ', 'nb-NO', 'nl-NL', 'nn-NO', 'pl-PL', 'pt-BR', 'pt-PT', 'ru-RU', 'si-LK', 'sl-SI', 'sk-SK', 'sr-RS', 'sr-RS@latin', 'sv-SE', 'th-TH', 'tr-TR', 'uk-UA']`) and defaults to `en-US`. Locale list is `validator.isAlphanumericLocales`. `options` is an optional object that can be supplied with the following key(s): `ignore` which can either be a String or RegExp of characters to be ignored e.g. " -" will ignore spaces and -'s.
+**isAlpha(str [, locale, options])**    | check if the string contains only letters (a-zA-Z).<br/><br/>`locale` 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', 'bn', '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-IR', 'fi-FI', 'fr-CA', 'fr-FR', 'he', 'hi-IN', 'hu-HU', 'it-IT', 'kk-KZ', 'ko-KR', 'ja-JP', 'ku-IQ', 'nb-NO', 'nl-NL', 'nn-NO', 'pl-PL', 'pt-BR', 'pt-PT', 'ru-RU', 'si-LK', 'sl-SI', 'sk-SK', 'sr-RS', 'sr-RS@latin', 'sv-SE', 'th-TH', 'tr-TR', 'uk-UA']` and defaults to `en-US`. Locale list is `validator.isAlphaLocales`. `options` is an optional object that can be supplied with the following key(s): `ignore` which can either be a String or RegExp of characters to be ignored e.g. " -" will ignore spaces and -'s.
+**isAlphanumeric(str [, locale, options])**      | check if the string contains only letters and numbers (a-zA-Z0-9).<br/><br/>`locale` 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', 'bn', '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-IR', 'fi-FI', 'fr-CA', 'fr-FR', 'he', 'hi-IN', 'hu-HU', 'it-IT', 'kk-KZ', 'ko-KR', 'ja-JP','ku-IQ', 'nb-NO', 'nl-NL', 'nn-NO', 'pl-PL', 'pt-BR', 'pt-PT', 'ru-RU', 'si-LK', 'sl-SI', 'sk-SK', 'sr-RS', 'sr-RS@latin', 'sv-SE', 'th-TH', 'tr-TR', 'uk-UA']`) and defaults to `en-US`. Locale list is `validator.isAlphanumericLocales`. `options` is an optional object that can be supplied with the following key(s): `ignore` which can either be a String or RegExp of characters to be ignored e.g. " -" will ignore spaces and -'s.
 **isAscii(str)**                        | check if the string contains ASCII chars only.
 **isBase32(str [, options])**           | check if the string is base32 encoded. `options` is optional and defaults to `{ crockford: false }`.<br/> When `crockford` is true it tests the given base32 encoded string using [Crockford's base32 alternative][Crockford Base32].
 **isBase58(str)**                       | check if the string is base58 encoded.
 **isBase64(str [, options])**          | check if the string is base64 encoded. `options` is optional and defaults to `{ urlSafe: false }`<br/> when `urlSafe` is true it tests the given base64 encoded string is [url safe][Base64 URL Safe].
 **isBefore(str [, date])**              | check if the string is a date that is before the specified date.
 **isBIC(str)**                          | check if the string is a BIC (Bank Identification Code) or SWIFT code.
-**isBoolean(str [, options])**          | check if the string is a boolean.<br/>`options` is an object which defaults to `{ loose: false }`. If `loose` is is set to false, the validator will strictly match ['true', 'false', '0', '1']. If `loose` is set to true, the validator will also match 'yes', 'no', and will match a valid boolean string of any case. (e.g.: ['true', 'True', 'TRUE']).
+**isBoolean(str [, options])**          | check if the string is a boolean.<br/>`options` is an object which defaults to `{ loose: false }`. If `loose` is set to false, the validator will strictly match ['true', 'false', '0', '1']. If `loose` is set to true, the validator will also match 'yes', 'no', and will match a valid boolean string of any case. (e.g.: ['true', 'True', 'TRUE']).
 **isBtcAddress(str)**            | check if the string is a valid BTC address.
 **isByteLength(str [, options])**          | check if the string's length (in UTF-8 bytes) falls in a range.<br/><br/>`options` is an object which defaults to `{ min: 0, max: undefined }`.
 **isCreditCard(str [, options])**                   | check if the string is a credit card number.<br/><br/> `options` is an optional object that can be supplied with the following key(s): `provider` is an optional key whose value should be a string, and defines the company issuing the credit card. Valid values include `['amex', 'dinersclub', 'discover', 'jcb', 'mastercard', 'unionpay', 'visa']` or blank will check for any provider. 
@@ -142,7 +133,7 @@ Validator                               | Description
 **isJWT(str)**                         | check if the string is valid JWT token.
 **isLatLong(str [, options])**                      | check if the string is a valid latitude-longitude coordinate in the format `lat,long` or `lat, long`.<br/><br/>`options` is an object that defaults to `{ checkDMS: false }`. Pass `checkDMS` as `true` to validate DMS(degrees, minutes, and seconds) latitude-longitude format.
 **isLength(str [, options])**              | check if the string's length falls in a range.<br/><br/>`options` is an object which defaults to `{ min: 0, max: undefined }`. Note: this function takes into account surrogate pairs.
-**isLicensePlate(str, locale)**     | check if the string matches the format of a country's license plate.<br/><br/>`locale` is one of `['cs-CZ', 'de-DE', 'de-LI', 'en-IN', 'es-AR', 'hu-HU', 'pt-BR', 'pt-PT', 'sq-AL', 'sv-SE']` or `'any'`.
+**isLicensePlate(str, locale)**     | check if the string matches the format of a country's license plate.<br/><br/>`locale` is one of `['cs-CZ', 'de-DE', 'de-LI', 'en-IN', 'en-PK', 'es-AR', 'hu-HU', 'pt-BR', 'pt-PT', 'sq-AL', 'sv-SE']` or `'any'`.
 **isLocale(str)**                       | check if the string is a locale.
 **isLowercase(str)**                    | check if the string is lowercase.
 **isLuhnNumber(str)**                    | check if the string passes the [Luhn algorithm check](https://en.wikipedia.org/wiki/Luhn_algorithm).
@@ -201,33 +192,6 @@ XSS sanitization was removed from the library in [2d5d6999](https://github.com/v
 
 For an alternative, have a look at Yahoo's [xss-filters library](https://github.com/yahoo/xss-filters) or at [DOMPurify](https://github.com/cure53/DOMPurify).
 
-## Contributing
-
-In general, we follow the "fork-and-pull" Git workflow.
-
-1. Fork the repo on GitHub
-2. Clone the project to your own machine
-3. Work on your fork
-    1. Make your changes and additions
-        - Most of your changes should be focused on `src/` and `test/` folders and/or `README.md`.
-        - Files such as `validator.js`, `validator.min.js` and files in `lib/` folder are autogenerated when running tests (`npm test`) and need not to be changed **manually**.
-    2. Change or add tests if needed
-    3. Run tests and make sure they pass
-    4. Add changes to README.md if needed
-4. Commit changes to your own branch
-5. **Make sure** you merge the latest from "upstream" and resolve conflicts if there is any
-6. Repeat step 3(3) above
-7. Push your work back up to your fork
-8. Submit a Pull request so that we can review your changes
-
-## Tests
-
-Tests are using mocha, to run the tests use:
-
-```sh
-$ npm test
-```
-
 ## Maintainers
 
 - [chriso](https://github.com/chriso) - **Chris O'Hara** (author)
diff --git a/src/index.js b/src/index.js
index bef4cff..0938110 100644
--- a/src/index.js
+++ b/src/index.js
@@ -18,6 +18,7 @@ import isTime from './lib/isTime';
 import isBoolean from './lib/isBoolean';
 import isLocale from './lib/isLocale';
 
+import isAbaRouting from './lib/isAbaRouting';
 import isAlpha, { locales as isAlphaLocales } from './lib/isAlpha';
 import isAlphanumeric, { locales as isAlphanumericLocales } from './lib/isAlphanumeric';
 import isNumeric from './lib/isNumeric';
@@ -146,6 +147,7 @@ const validator = {
   isBoolean,
   isIBAN,
   isBIC,
+  isAbaRouting,
   isAlpha,
   isAlphaLocales,
   isAlphanumeric,
diff --git a/src/lib/alpha.js b/src/lib/alpha.js
index d540ed1..8c37934 100644
--- a/src/lib/alpha.js
+++ b/src/lib/alpha.js
@@ -35,6 +35,7 @@ export const alpha = {
   he: /^[א-ת]+$/,
   fa: /^['آاءأؤئبپتثجچحخدذرزژسشصضطظعغفقکگلمنوهةی']+$/i,
   bn: /^['ঀঁংঃঅআইঈউঊঋঌএঐওঔকখগঘঙচছজঝঞটঠডঢণতথদধনপফবভমযরলশষসহ়ঽািীুূৃৄেৈোৌ্ৎৗড়ঢ়য়ৠৡৢৣৰৱ৲৳৴৵৶৷৸৹৺৻']+$/,
+  eo: /^[ABCĈD-GĜHĤIJĴK-PRSŜTUŬVZ]+$/i,
   'hi-IN': /^[\u0900-\u0961]+[\u0972-\u097F]*$/i,
   'si-LK': /^[\u0D80-\u0DFF]+$/,
 };
@@ -75,6 +76,7 @@ export const alphanumeric = {
   he: /^[0-9א-ת]+$/,
   fa: /^['0-9آاءأؤئبپتثجچحخدذرزژسشصضطظعغفقکگلمنوهةی۱۲۳۴۵۶۷۸۹۰']+$/i,
   bn: /^['ঀঁংঃঅআইঈউঊঋঌএঐওঔকখগঘঙচছজঝঞটঠডঢণতথদধনপফবভমযরলশষসহ়ঽািীুূৃৄেৈোৌ্ৎৗড়ঢ়য়ৠৡৢৣ০১২৩৪৫৬৭৮৯ৰৱ৲৳৴৵৶৷৸৹৺৻']+$/,
+  eo: /^[0-9ABCĈD-GĜHĤIJĴK-PRSŜTUŬVZ]+$/i,
   'hi-IN': /^[\u0900-\u0963]+[\u0966-\u097F]*$/i,
   'si-LK': /^[0-9\u0D80-\u0DFF]+$/,
 };
@@ -125,7 +127,7 @@ for (let locale, i = 0; i < bengaliLocales.length; i++) {
 // Source: https://en.wikipedia.org/wiki/Decimal_mark
 export const dotDecimal = ['ar-EG', 'ar-LB', 'ar-LY'];
 export const commaDecimal = [
-  'bg-BG', 'cs-CZ', 'da-DK', 'de-DE', 'el-GR', 'en-ZM', 'es-ES', 'fr-CA', 'fr-FR',
+  'bg-BG', 'cs-CZ', 'da-DK', 'de-DE', 'el-GR', 'en-ZM', 'eo', 'es-ES', 'fr-CA', 'fr-FR',
   'id-ID', 'it-IT', 'ku-IQ', 'hi-IN', 'hu-HU', 'nb-NO', 'nn-NO', 'nl-NL', 'pl-PL', 'pt-PT',
   'ru-RU', 'kk-KZ', 'si-LK', 'sl-SI', 'sr-RS@latin', 'sr-RS', 'sv-SE', 'tr-TR', 'uk-UA', 'vi-VN',
 ];
diff --git a/src/lib/isAbaRouting.js b/src/lib/isAbaRouting.js
new file mode 100644
index 0000000..0c6fd7f
--- /dev/null
+++ b/src/lib/isAbaRouting.js
@@ -0,0 +1,20 @@
+import assertString from './util/assertString';
+
+// http://www.brainjar.com/js/validation/
+// https://www.aba.com/news-research/research-analysis/routing-number-policy-procedures
+// series reserved for future use are excluded
+const isRoutingReg = /^(?!(1[3-9])|(20)|(3[3-9])|(4[0-9])|(5[0-9])|(60)|(7[3-9])|(8[1-9])|(9[0-2])|(9[3-9]))[0-9]{9}$/;
+
+export default function isAbaRouting(str) {
+  assertString(str);
+
+  if (!isRoutingReg.test(str)) return false;
+
+  let checkSumVal = 0;
+  for (let i = 0; i < str.length; i++) {
+    if (i % 3 === 0) checkSumVal += str[i] * 3;
+    else if (i % 3 === 1) checkSumVal += str[i] * 7;
+    else checkSumVal += str[i] * 1;
+  }
+  return (checkSumVal % 10 === 0);
+}
diff --git a/src/lib/isDate.js b/src/lib/isDate.js
index f0b2891..dc69b3b 100644
--- a/src/lib/isDate.js
+++ b/src/lib/isDate.js
@@ -22,7 +22,7 @@ function zip(date, format) {
 }
 
 export default function isDate(input, options) {
-  if (typeof options === 'string') { // Allow backward compatbility for old format isDate(input [, format])
+  if (typeof options === 'string') { // Allow backward compatibility for old format isDate(input [, format])
     options = merge({ format: options }, default_date_options);
   } else {
     options = merge(options, default_date_options);
@@ -49,6 +49,11 @@ export default function isDate(input, options) {
 
     let fullYear = dateObj.y;
 
+    // Check if the year starts with a hyphen
+    if (fullYear.startsWith('-')) {
+      return false; // Hyphen before year is not allowed
+    }
+
     if (dateObj.y.length === 2) {
       const parsedYear = parseInt(dateObj.y, 10);
 
diff --git a/src/lib/isIBAN.js b/src/lib/isIBAN.js
index dd226b1..5edcf83 100644
--- a/src/lib/isIBAN.js
+++ b/src/lib/isIBAN.js
@@ -24,6 +24,7 @@ const ibanRegexThroughCountryCode = {
   DE: /^(DE[0-9]{2})\d{18}$/,
   DK: /^(DK[0-9]{2})\d{14}$/,
   DO: /^(DO[0-9]{2})[A-Z]{4}\d{20}$/,
+  DZ: /^(DZ\d{24})$/,
   EE: /^(EE[0-9]{2})\d{16}$/,
   EG: /^(EG[0-9]{2})\d{25}$/,
   ES: /^(ES[0-9]{2})\d{20}$/,
diff --git a/src/lib/isISO4217.js b/src/lib/isISO4217.js
index 0738614..076d5a6 100644
--- a/src/lib/isISO4217.js
+++ b/src/lib/isISO4217.js
@@ -20,7 +20,7 @@ const validISO4217CurrencyCodes = new Set([
   'PAB', 'PEN', 'PGK', 'PHP', 'PKR', 'PLN', 'PYG',
   'QAR',
   'RON', 'RSD', 'RUB', 'RWF',
-  'SAR', 'SBD', 'SCR', 'SDG', 'SEK', 'SGD', 'SHP', 'SLL', 'SOS', 'SRD', 'SSP', 'STN', 'SVC', 'SYP', 'SZL',
+  'SAR', 'SBD', 'SCR', 'SDG', 'SEK', 'SGD', 'SHP', 'SLE', 'SLL', 'SOS', 'SRD', 'SSP', 'STN', 'SVC', 'SYP', 'SZL',
   'THB', 'TJS', 'TMT', 'TND', 'TOP', 'TRY', 'TTD', 'TWD', 'TZS',
   'UAH', 'UGX', 'USD', 'USN', 'UYI', 'UYU', 'UYW', 'UZS',
   'VES', 'VND', 'VUV',
diff --git a/src/lib/isInt.js b/src/lib/isInt.js
index 8047a69..edd67f7 100644
--- a/src/lib/isInt.js
+++ b/src/lib/isInt.js
@@ -9,10 +9,7 @@ export default function isInt(str, options) {
 
   // Get the regex to use for testing, based on whether
   // leading zeroes are allowed or not.
-  let regex = (
-    options.hasOwnProperty('allow_leading_zeroes') && !options.allow_leading_zeroes ?
-      int : intLeadingZeroes
-  );
+  const regex = options.allow_leading_zeroes === false ? int : intLeadingZeroes;
 
   // Check min/max/lt/gt
   let minCheckPassed = (!options.hasOwnProperty('min') || str >= options.min);
diff --git a/src/lib/isLicensePlate.js b/src/lib/isLicensePlate.js
index 48f8ebe..8476f2f 100644
--- a/src/lib/isLicensePlate.js
+++ b/src/lib/isLicensePlate.js
@@ -18,6 +18,7 @@ const validators = {
     /^[A-Z]{2}[- ]?((\d{3}[- ]?(([A-Z]{2})|T))|(R[- ]?\d{3}))$/.test(str),
   'sv-SE': str =>
     /^[A-HJ-PR-UW-Z]{3} ?[\d]{2}[A-HJ-PR-UW-Z1-9]$|(^[A-ZÅÄÖ ]{2,7}$)/.test(str.trim()),
+  'en-PK': str => /(^[A-Z]{2}((\s|-){0,1})[0-9]{3,4}((\s|-)[0-9]{2}){0,1}$)|(^[A-Z]{3}((\s|-){0,1})[0-9]{3,4}((\s|-)[0-9]{2}){0,1}$)|(^[A-Z]{4}((\s|-){0,1})[0-9]{3,4}((\s|-)[0-9]{2}){0,1}$)|(^[A-Z]((\s|-){0,1})[0-9]{4}((\s|-)[0-9]{2}){0,1}$)/.test(str.trim()),
 };
 
 export default function isLicensePlate(str, locale) {
diff --git a/src/lib/isMobilePhone.js b/src/lib/isMobilePhone.js
index 7c9b420..4d81921 100644
--- a/src/lib/isMobilePhone.js
+++ b/src/lib/isMobilePhone.js
@@ -2,7 +2,7 @@ import assertString from './util/assertString';
 
 /* eslint-disable max-len */
 const phones = {
-  'am-AM': /^(\+?374|0)((10|[9|7][0-9])\d{6}$|[2-4]\d{7}$)/,
+  'am-AM': /^(\+?374|0)(33|4[134]|55|77|88|9[13-689])\d{6}$/,
   'ar-AE': /^((\+?971)|0)?5[024568]\d{7}$/,
   'ar-BH': /^(\+?973)?(3|6)\d{7}$/,
   'ar-DZ': /^(\+?213|0)(5|6|7)\d{8}$/,
diff --git a/src/lib/isPassportNumber.js b/src/lib/isPassportNumber.js
index 4763d65..c1803fb 100644
--- a/src/lib/isPassportNumber.js
+++ b/src/lib/isPassportNumber.js
@@ -11,7 +11,7 @@ const passportRegexByCountryCode = {
   AR: /^[A-Z]{3}\d{6}$/, // ARGENTINA
   AT: /^[A-Z]\d{7}$/, // AUSTRIA
   AU: /^[A-Z]\d{7}$/, // AUSTRALIA
-  AZ: /^[A-Z]{2,3}\d{7,8}$/, // AZERBAIJAN
+  AZ: /^[A-Z]{1}\d{8}$/, // AZERBAIJAN
   BE: /^[A-Z]{2}\d{6}$/, // BELGIUM
   BG: /^\d{9}$/, // BULGARIA
   BR: /^[A-Z]{2}\d{6}$/, // BRAZIL
diff --git a/src/lib/isPort.js b/src/lib/isPort.js
index 0b316b7..0a9ddce 100644
--- a/src/lib/isPort.js
+++ b/src/lib/isPort.js
@@ -1,5 +1,5 @@
 import isInt from './isInt';
 
 export default function isPort(str) {
-  return isInt(str, { min: 0, max: 65535 });
+  return isInt(str, { allow_leading_zeroes: false, min: 0, max: 65535 });
 }
diff --git a/src/lib/isPostalCode.js b/src/lib/isPostalCode.js
index e621391..99cba29 100644
--- a/src/lib/isPostalCode.js
+++ b/src/lib/isPostalCode.js
@@ -52,7 +52,7 @@ const patterns = {
   MX: fiveDigit,
   MT: /^[A-Za-z]{3}\s{0,1}\d{4}$/,
   MY: fiveDigit,
-  NL: /^\d{4}\s?[a-z]{2}$/i,
+  NL: /^[1-9]\d{3}\s?(?!sa|sd|ss)[a-z]{2}$/i,
   NO: fourDigit,
   NP: /^(10|21|22|32|33|34|44|45|56|57)\d{3}$|^(977)$/i,
   NZ: fourDigit,
diff --git a/src/lib/isStrongPassword.js b/src/lib/isStrongPassword.js
index 5db901f..8fe9223 100644
--- a/src/lib/isStrongPassword.js
+++ b/src/lib/isStrongPassword.js
@@ -4,7 +4,7 @@ import assertString from './util/assertString';
 const upperCaseRegex = /^[A-Z]$/;
 const lowerCaseRegex = /^[a-z]$/;
 const numberRegex = /^[0-9]$/;
-const symbolRegex = /^[-#!$@£%^&*()_+|~=`{}\[\]:";'<>?,.\/ ]$/;
+const symbolRegex = /^[-#!$@£%^&*()_+|~=`{}\[\]:";'<>?,.\/\\ ]$/;
 
 const defaultOptions = {
   minLength: 8,
diff --git a/src/lib/isUUID.js b/src/lib/isUUID.js
index c026ca7..512141d 100644
--- a/src/lib/isUUID.js
+++ b/src/lib/isUUID.js
@@ -6,6 +6,7 @@ const uuid = {
   3: /^[0-9A-F]{8}-[0-9A-F]{4}-3[0-9A-F]{3}-[0-9A-F]{4}-[0-9A-F]{12}$/i,
   4: /^[0-9A-F]{8}-[0-9A-F]{4}-4[0-9A-F]{3}-[89AB][0-9A-F]{3}-[0-9A-F]{12}$/i,
   5: /^[0-9A-F]{8}-[0-9A-F]{4}-5[0-9A-F]{3}-[89AB][0-9A-F]{3}-[0-9A-F]{12}$/i,
+  7: /^[0-9A-F]{8}-[0-9A-F]{4}-7[0-9A-F]{3}-[89AB][0-9A-F]{3}-[0-9A-F]{12}$/i,
   all: /^[0-9A-F]{8}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{12}$/i,
 };
 
diff --git a/src/lib/isVAT.js b/src/lib/isVAT.js
index ece7d85..a8e2611 100644
--- a/src/lib/isVAT.js
+++ b/src/lib/isVAT.js
@@ -75,7 +75,7 @@ export const vatMatchers = {
   IN: str => /^(IN)?\d{15}$/.test(str),
   ID: str => /^(ID)?(\d{15}|(\d{2}.\d{3}.\d{3}.\d{1}-\d{3}.\d{3}))$/.test(str),
   IL: str => /^(IL)?\d{9}$/.test(str),
-  KZ: str => /^(KZ)?\d{9}$/.test(str),
+  KZ: str => /^(KZ)?\d{12}$/.test(str),
   NZ: str => /^(NZ)?\d{9}$/.test(str),
   NG: str => /^(NG)?(\d{12}|(\d{8}-\d{4}))$/.test(str),
   NO: str => /^(NO)?\d{9}MVA$/.test(str),


Test patch:
diff --git a/test/validators.test.js b/test/validators.test.js
index 6c68cd7..11f511a 100644
--- a/test/validators.test.js
+++ b/test/validators.test.js
@@ -2048,6 +2048,25 @@ describe('Validators', () => {
     });
   });
 
+  it('should validate Esperanto alpha strings', () => {
+    test({
+      validator: 'isAlpha',
+      args: ['eo'],
+      valid: [
+        'saluton',
+        'eĥoŝanĝoĉiuĵaŭde',
+        'EĤOŜANĜOĈIUĴAŬDE',
+        'Esperanto',
+        'LaŭLudovikoZamenhofBongustasFreŝaĈeĥaManĝaĵoKunSpicoj',
+      ],
+      invalid: [
+        'qwxyz',
+        '1887',
+        'qwxyz 1887',
+      ],
+    });
+  });
+
   it('should error on invalid locale', () => {
     test({
       validator: 'isAlpha',
@@ -2735,6 +2754,24 @@ describe('Validators', () => {
     });
   });
 
+  it('should validate Esperanto alphanumeric strings', () => {
+    test({
+      validator: 'isAlphanumeric',
+      args: ['eo'],
+      valid: [
+        'saluton',
+        'eĥoŝanĝoĉiuĵaŭde0123456789',
+        'EĤOŜANĜOĈIUĴAŬDE0123456789',
+        'Esperanto1887',
+        'LaŭLudovikoZamenhofBongustasFreŝaĈeĥaManĝaĵoKunSpicoj',
+      ],
+      invalid: [
+        'qwxyz',
+        'qwxyz 1887',
+      ],
+    });
+  });
+
   it('should error on invalid locale', () => {
     test({
       validator: 'isAlphanumeric',
@@ -2855,6 +2892,7 @@ describe('Validators', () => {
         '',
         '-1',
         '65536',
+        '0080',
       ],
     });
   });
@@ -2926,11 +2964,11 @@ describe('Validators', () => {
       validator: 'isPassportNumber',
       args: ['AZ'],
       valid: [
-        'AZE16175905',
-        'AA1617595',
+        'A16175905',
+        'A16175958',
       ],
       invalid: [
-        'A12345843',
+        'AZ1234584',
       ],
     });
 
@@ -3648,6 +3686,21 @@ describe('Validators', () => {
         '7903699371',
       ],
     });
+
+    test({
+      validator: 'isPassportNumber',
+      args: ['ZA'],
+      valid: [
+        'T12345678',
+        'A12345678',
+        'M12345678',
+        'D12345678',
+      ],
+      invalid: [
+        '123456789',
+        'Z12345678',
+      ],
+    });
   });
 
   it('should validate decimal numbers', () => {
@@ -5021,6 +5074,7 @@ describe('Validators', () => {
         'A987FBC9-4BED-3078-CF07-9141BA07C9F3',
         'A987FBC9-4BED-4078-8F07-9141BA07C9F3',
         'A987FBC9-4BED-5078-AF07-9141BA07C9F3',
+        '018C544A-D384-7000-BB74-3B1738ABE43C',
       ],
       invalid: [
         '',
@@ -5038,6 +5092,7 @@ describe('Validators', () => {
       valid: [
         'A117FBC9-4BED-3078-CF07-9141BA07C9F3',
         'A117FBC9-4BED-5078-AF07-9141BA07C9F3',
+        '018C544A-D384-7000-BB74-3B1738ABE43C',
       ],
       invalid: [
         '',
@@ -5051,6 +5106,7 @@ describe('Validators', () => {
       args: [null],
       valid: [
         'A127FBC9-4BED-3078-CF07-9141BA07C9F3',
+        '018C544A-D384-7000-BB74-3B1738ABE43C',
       ],
       invalid: [
         '',
@@ -5072,6 +5128,7 @@ describe('Validators', () => {
         'AAAAAAAA-1111-2222-AAAG-111111111111',
         'A987FBC9-4BED-4078-8F07-9141BA07C9F3',
         'A987FBC9-4BED-5078-AF07-9141BA07C9F3',
+        '018C544A-D384-7000-BB74-3B1738ABE43C',
       ],
     });
     test({
@@ -5087,6 +5144,7 @@ describe('Validators', () => {
         'AAAAAAAA-1111-1111-AAAG-111111111111',
         'A987FBC9-4BED-4078-8F07-9141BA07C9F3',
         'A987FBC9-4BED-5078-AF07-9141BA07C9F3',
+        '018C544A-D384-7000-BB74-3B1738ABE43C',
       ],
     });
     test({
@@ -5102,6 +5160,7 @@ describe('Validators', () => {
         'AAAAAAAA-1111-1111-AAAG-111111111111',
         'A987FBC9-4BED-4078-8F07-9141BA07C9F3',
         'A987FBC9-4BED-5078-AF07-9141BA07C9F3',
+        '018C544A-D384-7000-BB74-3B1738ABE43C',
       ],
     });
     test({
@@ -5120,6 +5179,7 @@ describe('Validators', () => {
         'AAAAAAAA-1111-1111-AAAG-111111111111',
         'A987FBC9-4BED-5078-AF07-9141BA07C9F3',
         'A987FBC9-4BED-3078-CF07-9141BA07C9F3',
+        '018C544A-D384-7000-BB74-3B1738ABE43C',
       ],
     });
     test({
@@ -5138,6 +5198,7 @@ describe('Validators', () => {
         'AAAAAAAA-1111-1111-AAAG-111111111111',
         '9c858901-8a57-4791-81fe-4c455b099bc9',
         'A987FBC9-4BED-3078-CF07-9141BA07C9F3',
+        '018C544A-D384-7000-BB74-3B1738ABE43C',
       ],
     });
     test({
@@ -5150,6 +5211,26 @@ describe('Validators', () => {
         '987FBC97-4BED-3078-AF07-9141BA07C9F3',
         '987FBC97-4BED-4078-AF07-9141BA07C9F3',
         '987FBC97-4BED-5078-AF07-9141BA07C9F3',
+        '018C544A-D384-7000-BB74-3B1738ABE43C',
+      ],
+    });
+    test({
+      validator: 'isUUID',
+      args: [7],
+      valid: [
+        '018C544A-D384-7000-BB74-3B1738ABE43C',
+      ],
+      invalid: [
+        '',
+        'xxxA987FBC9-4BED-3078-CF07-9141BA07C9F3',
+        '934859',
+        'AAAAAAAA-1111-1111-AAAG-111111111111',
+        'A987FBC9-4BED-5078-AF07-9141BA07C9F3',
+        'A987FBC9-4BED-3078-CF07-9141BA07C9F3',
+        '713ae7e3-cb32-45f9-adcb-7c4fa86b90c1',
+        '625e63f3-58f5-40b7-83a1-a72ad31acffb',
+        '57b73598-8764-4ad0-a76a-679bb6640eb1',
+        '9c858901-8a57-4791-81fe-4c455b099bc9',
       ],
     });
   });
@@ -5232,6 +5313,26 @@ describe('Validators', () => {
     });
   });
 
+  it('should validate ABA routing number', () => {
+    test({
+      validator: 'isAbaRouting',
+      valid: [
+        '322070381',
+        '011103093',
+        '263170175',
+        '124303065',
+      ],
+      invalid: [
+        '426317017',
+        '789456124',
+        '603558459',
+        'qwerty',
+        '12430306',
+        '382070381',
+      ],
+    });
+  });
+
   it('should validate IBAN', () => {
     test({
       validator: 'isIBAN',
@@ -5259,6 +5360,7 @@ describe('Validators', () => {
         'IR200170000000339545727003',
         'MZ97123412341234123412341',
         'MA64011519000001205000534921',
+        'DZ580002100001113000000570',
       ],
       invalid: [
         'XX22YYY1234567890123',
@@ -6632,20 +6734,39 @@ describe('Validators', () => {
       {
         locale: 'am-AM',
         valid: [
-          '+37410324123',
-          '+37422298765',
-          '+37431276521',
-          '022698763',
-          '37491987654',
-          '+37494567890',
+          '+37433123456',
+          '+37441123456',
+          '+37443123456',
+          '+37444123456',
+          '+37455123456',
+          '+37477123456',
+          '+37488123456',
+          '+37491123456',
+          '+37493123456',
+          '+37494123456',
+          '+37495123456',
+          '+37496123456',
+          '+37498123456',
+          '+37499123456',
+          '055123456',
+          '37455123456',
         ],
         invalid: [
           '12345',
-          '+37411498855',
-          '+37411498123',
+          '+37403498855',
+          '+37416498123',
           '05614988556',
           '',
           '37456789000',
+          '37486789000',
+          '+37431312345',
+          '+37430312345',
+          '+37460123456',
+          '+37410324123',
+          '+37422298765',
+          '+37431276521',
+          '022698763',
+          '+37492123456',
         ],
       },
       {
@@ -11440,6 +11561,7 @@ describe('Validators', () => {
         'MYR',
         'SGD',
         'USD',
+        'SLE',
       ],
       invalid: [
         '',
@@ -11860,6 +11982,13 @@ describe('Validators', () => {
           '3950IO',
           '3997 GH',
         ],
+        invalid: [
+          '1234',
+          '0603 JV',
+          '5194SA',
+          '9164 SD',
+          '1841SS',
+        ],
       },
       {
         locale: 'NP',
@@ -12892,6 +13021,7 @@ describe('Validators', () => {
         '+&DxJ=X7-4L8jRCD',
         'etV*p%Nr6w&H%FeF',
         '£3.ndSau_7',
+        'VaLIDWith\\Symb0l',
       ],
       invalid: [
         '',
@@ -12957,6 +13087,8 @@ describe('Validators', () => {
         '2019-02-29', // non-leap year
         '2020-04-31', // invalid date
         '2020/03-15', // mixed delimiter
+        '-2020-04-19',
+        '-2023/05/24',
       ],
     });
     test({
@@ -13558,6 +13690,30 @@ describe('Validators', () => {
       ],
       invalid: ['mh04ad0045', 'invalidlicenseplate', '4578', '', 'GJ054GH4785'],
     });
+    test({
+      validator: 'isLicensePlate',
+      args: ['en-PK'],
+      valid: [
+        'P 1789',
+        'RL745',
+        'RIR 5421',
+        'KHI 201',
+        'LB6571',
+        'LHR-786-23',
+        'AJGB 816-10',
+        'LES 7891 06',
+        'IDS 7871',
+        'LEH 4607 15',
+      ],
+      invalid: [
+        'ajgb 816-10',
+        ' 278-37',
+        'ABZ-27',
+        '',
+        'ABC-123-',
+        'D 272',
+      ],
+    });
   });
   it('should validate VAT numbers', () => {
     test({
@@ -14022,11 +14178,11 @@ describe('Validators', () => {
       validator: 'isVAT',
       args: ['KZ'],
       valid: [
-        'KZ123456789',
-        '123456789',
+        'KZ123456789012',
+        '123456789012',
       ],
       invalid: [
-        'KZ 123456789',
+        'KZ 123456789012',
         '12345678',
       ],
     });