Chronia provides a comprehensive suite of date validation and comparison functions that allow you to validate dates, compare dates with precision and flexibility, and check if dates represent the same point in time at various granularities. All functions support both Date objects and numeric timestamps, and handle invalid inputs gracefully.
| Function | Description |
|---|---|
isDate |
Checks if a value is a Date object instance |
isValid |
Checks if a Date object or timestamp is valid |
isExists |
Validates whether year, month, and day represent an existing date |
| Function | Description | Equality Behavior |
|---|---|---|
isFuture |
Checks if a date is strictly in the future relative to current time | Returns false for current moment |
isPast |
Checks if a date is strictly in the past relative to current time | Returns false for current moment |
| Function | Description | Equality Behavior |
|---|---|---|
isBefore |
Checks if the first date is strictly before the second date | Returns false for equal dates |
isAfter |
Checks if the first date is strictly after the second date | Returns false for equal dates |
isBeforeOrEqual |
Checks if the first date is before or equal to the second date | Returns true for equal dates |
isAfterOrEqual |
Checks if the first date is after or equal to the second date | Returns true for equal dates |
isEqual |
Checks if two dates represent the same point in time | Returns true only for equal dates |
| Function | Description | Granularity |
|---|---|---|
isSameYear |
Checks if two dates are in the same year | Year only (ignores month, day, time) |
isSameMonth |
Checks if two dates are in the same month and year | Month and year (ignores day, time) |
isSameDay |
Checks if two dates are on the same day | Day, month, and year (ignores time) |
isSameHour |
Checks if two dates are in the same hour | Hour, day, month, and year (ignores minutes, seconds) |
isSameMinute |
Checks if two dates are in the same minute | Minute, hour, day, month, and year (ignores seconds) |
isSameSecond |
Checks if two dates are in the same second | Second and all larger units (ignores milliseconds) |
All validation and comparison functions in this category share the following characteristics:
All functions accept both Date objects and numeric timestamps:
import { isBefore, isValid } from "chronia";
// Date objects
isBefore(new Date(2025, 0, 1), new Date(2025, 0, 2)); // true
isValid(new Date(2025, 0, 1)); // true
// Timestamps
isBefore(1704067200000, 1704153600000); // true
isValid(1704067200000); // true
// Mixed types
isBefore(new Date(2025, 0, 1), 1704153600000); // true
All functions validate inputs and return false for invalid dates without throwing exceptions:
import { isEqual, isValid, isSameDay } from "chronia";
isValid(new Date("invalid")); // false
isEqual(new Date("invalid"), new Date(2025, 0, 1)); // false
isSameDay(NaN, new Date(2025, 0, 1)); // false
Comparison functions (isBefore, isAfter, isBeforeOrEqual, isAfterOrEqual, isEqual) support optional unit-based comparison, allowing you to compare dates at different time scales:
Supported Units:
"year" - Compare only years"month" - Compare up to months"day" - Compare up to days"hour" - Compare up to hours"minute" - Compare up to minutes"second" - Compare up to seconds"millisecond" - Compare exact timestamps (default)Example:
import { isEqual } from "chronia";
// Same day, different times
const morning = new Date(2025, 0, 1, 9, 0, 0);
const evening = new Date(2025, 0, 1, 17, 0, 0);
isEqual(morning, evening); // false (different timestamps)
isEqual(morning, evening, { unit: "day" }); // true (same day)
isEqual(morning, evening, { unit: "hour" }); // false (different hours)
Validation Functions (isDate, isValid, isExists):
isDate: Use to check if a value is a Date object instance (type checking)isValid: Use to check if a date or timestamp represents a valid date valueisExists: Use to validate that year, month, and day components represent an existing dateisDate(value) && isValid(value) for complete validationisExists(year, month, day) for validating separate date partsCurrent Time Comparison Functions (isFuture, isPast):
false for the exact current momentComparison Functions (isBefore, isAfter, isBeforeOrEqual, isAfterOrEqual, isEqual):
Same-Time Functions (isSameYear, isSameMonth, isSameDay, etc.):
When to use Current Time Comparison (isFuture, isPast):
isPast(session.expiresAt)isFuture(event.date)When to use Two-Date Comparison (isBefore, isAfter, etc.):
isBefore(eventA, eventB)isBeforeOrEqual(submission, deadline)Strict Comparisons (isBefore, isAfter, isFuture, isPast):
falseInclusive Comparisons (isBeforeOrEqual, isAfterOrEqual):
true| Scenario | Recommended Function | Reason |
|---|---|---|
| Check if value is Date instance | isDate(value) |
Type checking for Date objects |
| Type guard before Date methods | isDate(value) |
Enables safe access to Date methods |
| Distinguish Date from timestamp | isDate(value) |
Differentiates Date objects from numbers |
| Validate user input (type and validity) | isDate(value) && isValid(value) |
Complete date validation |
| Validate API response date | isDate(data) |
Runtime type validation |
| Filter Date instances from array | array.filter(isDate) |
Extract only Date objects |
| Validate user input | isValid(date) |
Check if date is valid |
| Validate separate date components | isExists(year, month, day) |
Check that year/month/day represents a valid date |
| Validate form with separate fields | isExists(yearInput, monthInput, dayInput) |
Validate date parts before creating Date |
| Check if leap year has Feb 29 | isExists(year, 2, 29) |
Leap year validation |
| Validate parsed date string | isExists(parsedYear, parsedMonth, parsedDay) |
Check that parsed components are valid |
| Check if session expired | isPast(session.expiresAt) |
Current time-based expiry check |
| Check if event is upcoming | isFuture(event.date) |
Current time-based future check |
| Filter upcoming events | events.filter(e => isFuture(e.date)) |
Simple future filtering |
| Filter past events | events.filter(e => isPast(e.date)) |
Simple past filtering |
| Validate cache expiration | isFuture(cache.expiresAt) |
Check if cache is still valid |
| Check if deadline passed | isPast(deadline) |
Strict past check against now |
| Check if event has passed | isBefore(event, now()) |
Strict past check with explicit now |
| Check if deadline has been met | isBeforeOrEqual(submission, deadline) |
Includes deadline day |
| Validate age requirement | isAfterOrEqual(birthdate, minDate) |
Includes exact age |
| Check future events | isAfter(event, now()) |
Strict future check with explicit now |
| Detect duplicate timestamps | isEqual(date1, date2) |
Exact equality |
| Group events by day | isSameDay(date1, date2) |
Same-day check |
| Group events by month | isSameMonth(date1, date2) |
Same-month check |
| Sort chronologically | isBefore(a, b) or isAfter(a, b) |
Strict ordering |
| Range validation (inclusive) | isAfterOrEqual(date, start) && isBeforeOrEqual(date, end) |
Inclusive boundaries |
| Range validation (exclusive) | isAfter(date, start) && isBefore(date, end) |
Exclusive boundaries |
Given two dates a and b:
| Relationship | isBefore |
isAfter |
isBeforeOrEqual |
isAfterOrEqual |
isEqual |
|---|---|---|---|---|---|
a < b |
true |
false |
true |
false |
false |
a > b |
false |
true |
false |
true |
false |
a === b |
false |
false |
true |
true |
true |
import { isValid } from "chronia";
function processDate(date: Date | number): void {
if (!isValid(date)) {
throw new Error("Invalid date provided");
}
// Process valid date
}
import { isExists } from "chronia";
// Validate separate year/month/day inputs before creating Date
function createDateFromComponents(
year: number,
month: number,
day: number,
): Date | null {
// Note: isExists uses 1-based month (1=January, 12=December)
if (!isExists(year, month, day)) {
return null;
}
// Convert to 0-based month for JavaScript Date constructor
return new Date(year, month - 1, day);
}
// Valid dates
createDateFromComponents(2024, 2, 29); // Date object - leap year Feb 29
createDateFromComponents(2024, 4, 30); // Date object - April 30
// Invalid dates return null
createDateFromComponents(2023, 2, 29); // null - not a leap year
createDateFromComponents(2024, 4, 31); // null - April has only 30 days
createDateFromComponents(2024, 13, 1); // null - invalid month
// Form validation with detailed error messages
function validateDateForm(
year: number,
month: number,
day: number,
): {
valid: boolean;
error?: string;
} {
// Basic range checks before calling isExists
if (month < 1 || month > 12) {
return { valid: false, error: "Month must be between 1 and 12" };
}
if (day < 1) {
return { valid: false, error: "Day must be at least 1" };
}
// Use isExists for complex validation (leap years, month-specific day limits)
if (!isExists(year, month, day)) {
if (month === 2 && day === 29) {
return { valid: false, error: `${year} is not a leap year` };
}
return { valid: false, error: "This date does not exist" };
}
return { valid: true };
}
import { isExists } from "chronia";
// Check if a year is a leap year by testing February 29
function isLeapYear(year: number): boolean {
return isExists(year, 2, 29);
}
// Test various years
isLeapYear(2024); // true - divisible by 4, not a century year
isLeapYear(2000); // true - divisible by 400
isLeapYear(1900); // false - divisible by 100 but not 400
isLeapYear(2023); // false - not divisible by 4
// Validate date with leap year awareness
function getDaysInFebruary(year: number): number {
return isExists(year, 2, 29) ? 29 : 28;
}
import { isDate } from "chronia";
// Type guard for runtime type checking
function processUnknownValue(value: unknown): void {
if (isDate(value)) {
// TypeScript knows 'value' is Date here
console.log(value.getTime());
console.log(value.toISOString());
} else {
console.log("Not a Date instance");
}
}
// Distinguish Date objects from timestamps
function normalizeToDate(input: Date | number): Date {
if (isDate(input)) {
// Already a Date instance, return as-is
return input;
}
// It's a timestamp, convert to Date
return new Date(input);
}
import { isDate, isValid } from "chronia";
// Combine isDate and isValid for complete validation
function isValidDateInstance(value: unknown): value is Date {
return isDate(value) && isValid(value);
}
// Valid Date instance
isValidDateInstance(new Date(2025, 0, 1)); // true
// Invalid Date instance (type check passes, validity fails)
isValidDateInstance(new Date("invalid")); // false
// Timestamp (type check fails)
isValidDateInstance(Date.now()); // false
// Use in API response validation
interface ApiResponse {
date: unknown;
}
function processApiDate(response: ApiResponse): Date | null {
const { date } = response;
// Check if it's a Date instance
if (!isDate(date)) {
console.error("Expected Date instance");
return null;
}
// Check if it's valid
if (!isValid(date)) {
console.error("Invalid Date instance");
return null;
}
return date;
}
import { isPast } from "chronia";
interface Session {
token: string;
expiresAt: Date;
}
function isSessionValid(session: Session): boolean {
// Session is valid if expiration time is still in the future
return !isPast(session.expiresAt);
}
function validateSession(session: Session): void {
if (isPast(session.expiresAt)) {
throw new Error("Session has expired");
}
// Proceed with valid session
}
import { isFuture } from "chronia";
interface Event {
id: string;
name: string;
date: Date;
}
function getUpcomingEvents(events: Event[]): Event[] {
return events.filter((event) => isFuture(event.date));
}
function hasUpcomingEvents(events: Event[]): boolean {
return events.some((event) => isFuture(event.date));
}
import { isFuture } from "chronia";
interface CacheEntry<T> {
data: T;
expiresAt: number;
}
function getCachedData<T>(cache: CacheEntry<T>): T | null {
// Return data only if cache hasn't expired
if (isFuture(cache.expiresAt)) {
return cache.data;
}
return null;
}
function isCacheValid<T>(cache: CacheEntry<T>): boolean {
return isFuture(cache.expiresAt);
}
Inclusive range (both boundaries included):
import { isAfterOrEqual, isBeforeOrEqual } from "chronia";
function isWithinRange(date: Date, start: Date, end: Date): boolean {
return isAfterOrEqual(date, start) && isBeforeOrEqual(date, end);
}
Exclusive range (boundaries excluded):
import { isAfter, isBefore } from "chronia";
function isBetween(date: Date, start: Date, end: Date): boolean {
return isAfter(date, start) && isBefore(date, end);
}
import { isBefore } from "chronia";
const events = [
{ name: "Event C", date: new Date(2025, 2, 1) },
{ name: "Event A", date: new Date(2025, 0, 1) },
{ name: "Event B", date: new Date(2025, 1, 1) },
];
const sorted = events.sort((a, b) => (isBefore(a.date, b.date) ? -1 : 1));
import { isSameDay } from "chronia";
const events = [
new Date(2025, 0, 1, 9, 0),
new Date(2025, 0, 1, 14, 0),
new Date(2025, 0, 2, 10, 0),
];
const todayEvents = events.filter((event) =>
isSameDay(event, new Date(2025, 0, 1)),
);
// Returns first two events
import { isSameMonth } from "chronia";
function getMonthlyTransactions(
transactions: Transaction[],
targetMonth: Date,
) {
return transactions.filter((tx) => isSameMonth(tx.date, targetMonth));
}
import { isBeforeOrEqual } from "chronia";
function isSubmittedOnTime(submissionDate: Date, deadline: Date): boolean {
// Includes submissions on the deadline day
return isBeforeOrEqual(submissionDate, deadline);
}
type TimeUnit =
| "year"
| "month"
| "day"
| "hour"
| "minute"
| "second"
| "millisecond";
interface ComparisonOptions {
unit?: TimeUnit;
}
All validation and comparison functions follow a consistent error handling pattern:
false instead of throwing errorsfalsefalsefalse