ajv-dates
v1.1.0
Published
Intuitive JSON Schema Date Validation
Downloads
932
Readme
ajv-dates
What's this?
Good date validation has been conspicuously missing from the JSON schema spec. ajv-dates
adds a few (configurable) keywords to ajv to make date validation a snap.
Getting Started
npm install ajv-dates
# or
yarn add ajv-dates
Usage
import Ajv from 'ajv';
import dates from 'ajv-dates';
const instance = dates(new Ajv());
const validator = instance.compile({
type: 'string',
isAfter: Date.UTC(2021, 12, 31)
});
console.log(validator('2022-01-01').errors)
// null, no errors!
console.log(validator('2021-01-01').errors)
/*
[
{
message: 'Date must be after 1643587200000',
instancePath: '',
schemaPath: '#/isAfter'
}
]
*/
Keywords
isAfter
isAfter
accepts a single argument that is, or can be parsed into a Date or unix timestamp:
instance.validate({
type: 'string',
isAfter: Date.now()
})
isBefore
isBefore
accepts a single argument that is, or can be parsed into a Date or unix timestamp:
instance.validate({
type: 'string',
isAfter: Date.now()
})
isBetween
isBetween
accepts a tuple of two values that are, or can be parsed into, Dates or unix timestamps:
instance.validate({
type: 'string',
isBetween: [Date.now()-86400, Date.now()+86400]
})
isWeekday
isWeekday
accepts a boolean:
instance.validate({
type: 'string',
isWeekday: true
})
isWeekend
isWeekend
accepts a boolean:
instance.validate({
type: 'string',
isWeekend: false
})
Bonus Keywords: times
If you supply a parser
you can also take advantage of our time keywords:
import Ajv from 'ajv';
import { date, times } from 'ajv-dates';
import { parseDate } from 'chrono-node';
const options = { parser: parseDate }
const instance = times(dates(new Ajv(),options),options)
isTimeAfter
instance.validate({
type: 'string',
isTimeAfter: '7:00pm',
}, '6:30pm');
// Error: Time must be after 7:00pm
isTimeBefore
instance.validate({
type: 'string',
isTimeBefore: '6:00pm',
}, '6:30pm');
// Error: Time must be before 6:00pm
isTimeBetween
instance.validate({
type: 'string',
isTimeBetween: ['7:00pm','10:00pm'],
}, '6:30pm');
// Error: Time must be between 7:00pm and 10:00pm
API
This library exports a default function that accepts an AJV instance and options.
Options
type Options = {
parser?: (val: any) => Date | number;
keywordMap?: KeywordMap;
};
parser = (val) => new Date(val)
The parser accepts any value and tries to parse it to a Date (or a unix timestamp). It is used to parse both the subject being evaluated and the arguments given to the keyword. As shown below, the ability to specify a parser allows some really great features to be unlocked.
keywordMap = Partial<Record<Keyword,string>>
Don't like the keywords? Configure your own using the keyword map:
const instance = dates(
new Ajv(),
{
keywordMap: {
isAfter: 'isDateAfter'
}
}
)
instance.validate({
type: 'string',
isDateAfter: Date.now()
}, '2021-12-31')
Parser
Even with ajv-formats, there's still something missing with date validation - the difficulty to encode "now", for validation purposes, in a schema. Fortunately, by using a smart date parser that can parse natural language relative strings into timestamps, you can now encode relative date logic into your schemas.
Two great options (tested with ajv-dates
) are:
- The older, but still incredibly powerful and relevant Sugar Date
- The more modern chrono-node
import Ajv from 'ajv';
import dates from 'ajv-dates';
import { Date as SDate } from 'sugar-date'
import { parseDate } from 'chrono-node';
// sugardate
const instance = dates(new Ajv(), { parser: SDate.create })
// chrono
const instance = dates(new Ajv(), { parser: parseDate })
Here's what the super power looks like:
const schema = {
type: 'string',
isAfter: '30 days ago'
}
const instance = dates(new Ajv(), { parser: /* your chosen natural language date parser */ })
const today = (new Date()).toISOString();
instance.validate(
{
type: 'string',
isAfter: '2 weeks ago'
},
today
);
console.log(instance.errors); // null
instance.validate(
{
type: 'string',
isAfter: '2 weeks from now'
},
today
);
console.log(instance.errors);
/*
[
{
message: 'Date must be after 2 weeks from now',
instancePath: '',
schemaPath: '#/isAfter'
}
]
*/
License
Contributing
Grab an issue, PRs welcome!