chronia

isEqual

Overview

The isEqual function checks if two dates represent the same point in time. It provides flexible date comparison with optional unit-based granularity, allowing you to compare dates at different time scales (year, month, day, hour, minute, second, or millisecond).

Signature

function isEqual(
  a: DateInput,
  b: DateInput,
  options?: ComparisonOptions,
): boolean;

Parameters

Parameter Type Description
a DateInput The first date as a Date object, numeric timestamp, or ISO 8601 string
b DateInput The second date as a Date object, numeric timestamp, or ISO 8601 string
options ComparisonOptions Optional configuration object
options.unit TimeUnit The unit of comparison: "year", "month", "day", "hour", "minute", "second", or "millisecond". Defaults to "millisecond"

Return Value

Type Description
boolean Returns true if date a equals date b, false otherwise or if either date is invalid

Description

The isEqual function determines whether two dates represent the same point in time. It supports both precise millisecond-level comparison and coarser-grained comparisons by truncating dates to a specified unit before comparing.

Specification

Returns true when:

Returns false when:

Behavior Notes

Use Cases

Usage Examples

Duplicate Detection

import { isEqual } from "chronia";

// Check for duplicate events
interface Event {
  id: string;
  timestamp: Date;
  name: string;
}

function hasDuplicateTimestamp(events: Event[]): boolean {
  for (let i = 0; i < events.length; i++) {
    for (let j = i + 1; j < events.length; j++) {
      if (isEqual(events[i].timestamp, events[j].timestamp)) {
        return true;
      }
    }
  }
  return false;
}

// Remove duplicate timestamps
function removeDuplicateTimestamps(dates: Date[]): Date[] {
  return dates.filter(
    (date, index) => dates.findIndex((d) => isEqual(d, date)) === index,
  );
}

// Example usage
const event1 = new Date(2025, 0, 15, 10, 0, 0);
const event2 = new Date(2025, 0, 15, 10, 0, 0);

isEqual(event1, event2); // Returns: true (exact same timestamp)

const dates = [
  new Date(2025, 0, 1),
  new Date(2025, 0, 2),
  new Date(2025, 0, 1), // Duplicate
];
removeDuplicateTimestamps(dates); // Returns: [Jan 1, Jan 2]

Cache Validation

import { isEqual } from "chronia";

interface CacheEntry<T> {
  data: T;
  timestamp: number;
}

// Validate cache by comparing timestamps
function isCacheValid<T>(
  cache: CacheEntry<T>,
  sourceTimestamp: number,
): boolean {
  return isEqual(cache.timestamp, sourceTimestamp);
}

// Check if cache is stale
function isCacheStale<T>(cache: CacheEntry<T>, sourceTimestamp: Date): boolean {
  return !isEqual(cache.timestamp, sourceTimestamp);
}

// Example usage
const cache = {
  data: { user: "John" },
  timestamp: Date.now(),
};

const sourceTime = new Date();
isCacheValid(cache, sourceTime.getTime()); // Returns: true (if timestamps match)

// After some time passes
setTimeout(() => {
  isCacheStale(cache, new Date()); // Returns: true (different timestamps)
}, 1000);

Test Assertions

import { isEqual } from "chronia";

// Test date manipulation functions
function testDateAddition(): void {
  const startDate = new Date(2025, 0, 1, 12, 0, 0);
  const expectedDate = new Date(2025, 0, 1, 13, 0, 0);

  // Assume addHours is a function that adds hours to a date
  const result = addHours(startDate, 1);

  if (!isEqual(result, expectedDate)) {
    throw new Error("Date addition test failed");
  }
}

// Test timestamp equality
function testTimestampConversion(): void {
  const date = new Date(2025, 0, 15, 10, 30, 0);
  const timestamp = date.getTime();
  const reconstructed = new Date(timestamp);

  if (!isEqual(date, reconstructed)) {
    throw new Error("Timestamp conversion test failed");
  }
}

// Example usage with mixed types
const originalDate = new Date(2025, 0, 1);
const timestamp = originalDate.getTime();

isEqual(originalDate, timestamp); // Returns: true (Date vs number)
isEqual(timestamp, originalDate); // Returns: true (number vs Date)

Same-Day Checks

import { isEqual } from "chronia";

// Check if two events occur on the same day
function isSameDay(date1: Date, date2: Date): boolean {
  return isEqual(date1, date2, { unit: "day" });
}

// Group events by day
interface Event {
  name: string;
  timestamp: Date;
}

function groupEventsByDay(events: Event[]): Event[][] {
  const groups: Event[][] = [];

  for (const event of events) {
    const existingGroup = groups.find((group) =>
      isEqual(group[0].timestamp, event.timestamp, { unit: "day" }),
    );

    if (existingGroup) {
      existingGroup.push(event);
    } else {
      groups.push([event]);
    }
  }

  return groups;
}

// Example usage
const morning = new Date(2025, 0, 15, 9, 0, 0); // January 15, 2025, 09:00
const evening = new Date(2025, 0, 15, 18, 0, 0); // January 15, 2025, 18:00

isSameDay(morning, evening); // Returns: true (same day, different times)
isEqual(morning, evening); // Returns: false (different times)

// Check if today
function isToday(date: Date): boolean {
  return isEqual(date, new Date(), { unit: "day" });
}

isToday(new Date()); // Returns: true

Synchronization

import { isEqual } from "chronia";

interface SyncRecord {
  id: string;
  lastModified: number;
  data: unknown;
}

// Check if local and remote records are synchronized
function isSynchronized(local: SyncRecord, remote: SyncRecord): boolean {
  return (
    local.id === remote.id && isEqual(local.lastModified, remote.lastModified)
  );
}

// Find records that need synchronization
function findOutOfSync(
  local: SyncRecord[],
  remote: SyncRecord[],
): SyncRecord[] {
  return local.filter((localRecord) => {
    const remoteRecord = remote.find((r) => r.id === localRecord.id);
    return (
      !remoteRecord ||
      !isEqual(localRecord.lastModified, remoteRecord.lastModified)
    );
  });
}

// Example usage
const localRecord = {
  id: "123",
  lastModified: Date.now(),
  data: { value: "local" },
};

const remoteRecord = {
  id: "123",
  lastModified: Date.now(),
  data: { value: "remote" },
};

isSynchronized(localRecord, remoteRecord); // Returns: true (if timestamps match)

Unit-Based Equality

import { isEqual } from "chronia";

// Check if dates are in the same year
const date1 = new Date(2025, 0, 1); // January 1, 2025
const date2 = new Date(2025, 11, 31); // December 31, 2025

isEqual(date1, date2, { unit: "year" }); // Returns: true (both in 2025)
isEqual(date1, date2); // Returns: false (different days)

// Check if dates are in the same month
const jan15 = new Date(2025, 0, 15); // January 15, 2025
const jan30 = new Date(2025, 0, 30); // January 30, 2025

isEqual(jan15, jan30, { unit: "month" }); // Returns: true (both in January 2025)

// Check if dates are in the same hour
const time1 = new Date(2025, 0, 15, 9, 15, 0); // 09:15:00
const time2 = new Date(2025, 0, 15, 9, 45, 0); // 09:45:00

isEqual(time1, time2, { unit: "hour" }); // Returns: true (both at 9:00 hour)
isEqual(time1, time2); // Returns: false (different minutes)

// Compare at second granularity
const precise1 = new Date(2025, 0, 15, 9, 30, 15, 123); // .123 milliseconds
const precise2 = new Date(2025, 0, 15, 9, 30, 15, 456); // .456 milliseconds

isEqual(precise1, precise2, { unit: "second" }); // Returns: true (same second)
isEqual(precise1, precise2); // Returns: false (different milliseconds)

// Invalid dates return false
isEqual(new Date("invalid"), new Date(2025, 0, 1)); // Returns: false
isEqual(NaN, new Date()); // Returns: false