ajt-validator
Version:
Validation library for JavaScript and TypeScript
196 lines (195 loc) • 7.67 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.DOBValidator = void 0;
const base_1 = require("../base");
/**
* Validator for Date of Birth fields
* Validates dates and provides age calculations
*/
class DOBValidator extends base_1.BaseValidator {
/**
* Create a new Date of Birth validator
* @param options Configuration options for DOB validation
*/
constructor(options = {}) {
super();
this.DEFAULT_MIN_AGE = 0;
this.DEFAULT_MAX_AGE = 120;
this.options = Object.assign({ minAge: this.DEFAULT_MIN_AGE, maxAge: this.DEFAULT_MAX_AGE, allowFutureDates: false }, options);
}
/**
* Validate a date of birth value
* @param value DOB as string or Date object
* @returns Validation result with additional DOB and age metadata
*/
validate(value) {
// Handle null/undefined
if (value === null || value === undefined) {
return this.createError('DOB_REQUIRED', 'Date of birth is required');
}
// Parse the date if it's a string
let dateObj;
if (typeof value === 'string') {
dateObj = this.parseDate(value);
if (isNaN(dateObj.getTime())) {
return this.createError('INVALID_DATE_FORMAT', 'Invalid date format');
}
}
else if (value instanceof Date) {
dateObj = value;
// Validate that it's a valid date
if (isNaN(dateObj.getTime())) {
return this.createError('INVALID_DATE', 'Invalid date');
}
}
else {
return this.createError('INVALID_INPUT_TYPE', 'Date of birth must be a string or Date object');
}
// Get reference date (today by default)
const referenceDate = this.options.referenceDate || new Date();
// Check for future dates
if (!this.options.allowFutureDates && dateObj > referenceDate) {
return this.createError('FUTURE_DATE', 'Date of birth cannot be in the future');
}
// Calculate age
const age = this.calculateAge(dateObj, referenceDate);
// Validate minimum age
if (age.years < this.options.minAge) {
return this.createError('BELOW_MINIMUM_AGE', `Age must be at least ${this.options.minAge} years`);
}
// Validate maximum age
if (age.years > this.options.maxAge) {
return this.createError('ABOVE_MAXIMUM_AGE', `Age must not exceed ${this.options.maxAge} years`);
}
// Check if DOB falls within any specified valid ranges
let category;
if (this.options.validRanges && this.options.validRanges.length > 0) {
const matchingRange = this.options.validRanges.find(range => dateObj >= range.startDate && dateObj <= range.endDate);
category = matchingRange === null || matchingRange === void 0 ? void 0 : matchingRange.name;
// If valid ranges are specified and DOB doesn't match any, it's invalid
if (!matchingRange) {
return this.createError('OUTSIDE_VALID_RANGES', 'Date of birth does not fall within any valid range');
}
}
// Return successful validation result with metadata
return {
isValid: true,
value: dateObj,
age,
category,
isLegalAge: age.years >= this.options.minAge
};
}
/**
* Check if a person is of legal age based on their DOB
* @param dob Date of birth
* @param minAge Minimum age to be considered legal (defaults to options.minAge)
* @returns Whether the person is of legal age
*/
isLegalAge(dob, minAge) {
const minimumAge = minAge || this.options.minAge || 18; // Default to 18 if not specified
const age = this.calculateAge(dob).years;
return age >= minimumAge;
}
/**
* Get age at a specific reference date
* @param dob Date of birth
* @param referenceDate Date to calculate age at (defaults to current date)
* @returns Age details
*/
getAgeAt(dob, referenceDate) {
return this.calculateAge(dob, referenceDate);
}
/**
* Calculate exact age from DOB
* @param dob Date of birth
* @param referenceDate Reference date (defaults to current date)
* @returns Detailed age calculation
*/
calculateAge(dob, referenceDate = new Date()) {
let years = referenceDate.getFullYear() - dob.getFullYear();
let months = referenceDate.getMonth() - dob.getMonth();
let days = referenceDate.getDate() - dob.getDate();
// Adjust for negative days
if (days < 0) {
// Get the number of days in the previous month
const previousMonth = new Date(referenceDate.getFullYear(), referenceDate.getMonth(), 0).getDate();
days += previousMonth;
months--;
}
// Adjust for negative months
if (months < 0) {
years--;
months += 12;
}
// Calculate decimal years (approximate)
const daysInYear = 365.25; // Account for leap years
const decimalYears = years + (months / 12) + (days / daysInYear);
return {
years,
months,
days,
decimalYears: Math.round(decimalYears * 100) / 100 // Round to 2 decimal places
};
}
/**
* Parse string date into Date object with format detection
* @param dateStr Date string
* @returns Parsed Date object
*/
parseDate(dateStr) {
var _a;
// Try to parse with Date constructor first
const date = new Date(dateStr);
if (!isNaN(date.getTime())) {
return date;
}
// If strict format validation is enabled, check against allowed formats
if (((_a = this.options.format) === null || _a === void 0 ? void 0 : _a.strict) && this.options.format.allowedFormats) {
// Implementation would check each allowed format
// This is simplified - in a real implementation you would need
// to check each format pattern (perhaps using a library like date-fns)
}
// Common formats handling
// MM/DD/YYYY
const usFormat = /^(\d{1,2})\/(\d{1,2})\/(\d{4})$/;
const usMatch = dateStr.match(usFormat);
if (usMatch) {
return new Date(parseInt(usMatch[3]), // year
parseInt(usMatch[1]) - 1, // month (0-based)
parseInt(usMatch[2]) // day
);
}
// DD/MM/YYYY
const ukFormat = /^(\d{1,2})\/(\d{1,2})\/(\d{4})$/;
const ukMatch = dateStr.match(ukFormat);
if (ukMatch) {
return new Date(parseInt(ukMatch[3]), // year
parseInt(ukMatch[2]) - 1, // month (0-based)
parseInt(ukMatch[1]) // day
);
}
// YYYY-MM-DD (ISO format)
const isoFormat = /^(\d{4})-(\d{1,2})-(\d{1,2})$/;
const isoMatch = dateStr.match(isoFormat);
if (isoMatch) {
return new Date(parseInt(isoMatch[1]), // year
parseInt(isoMatch[2]) - 1, // month (0-based)
parseInt(isoMatch[3]) // day
);
}
// If no format matched, return invalid date
return new Date('Invalid Date');
}
/**
* Create an error result for DOB validation
*/
createError(code, message) {
return {
isValid: false,
errors: [{ code, message }],
age: undefined
};
}
}
exports.DOBValidator = DOBValidator;