CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-react-dates

A responsive and accessible date range picker component built with React

Pending

Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

Overview
Eval results
Files

utilities.mddocs/

Utility Functions

Date comparison and conversion utilities for working with Moment.js objects in React Dates. These functions provide consistent date handling and validation throughout the library.

Capabilities

Date Comparison Functions

Utility functions for comparing Moment.js date objects with specific semantics for date picker use cases.

/**
 * Check if moment `a` is the same day as moment `b`
 * @param a - First moment object
 * @param b - Second moment object  
 * @returns true if both moments represent the same calendar day
 */
function isSameDay(a: moment.Moment, b: moment.Moment): boolean;

/**
 * Check if moment `a` is after or same day as moment `b`
 * @param a - First moment object
 * @param b - Second moment object
 * @returns true if `a` is after or same day as `b`
 */
function isInclusivelyAfterDay(a: moment.Moment, b: moment.Moment): boolean;

/**
 * Check if moment `a` is before or same day as moment `b`
 * @param a - First moment object
 * @param b - Second moment object
 * @returns true if `a` is before or same day as `b`
 */
function isInclusivelyBeforeDay(a: moment.Moment, b: moment.Moment): boolean;

/**
 * Check if moment `b` is the day immediately after moment `a`
 * @param a - First moment object
 * @param b - Second moment object
 * @returns true if `b` is exactly one day after `a`
 */
function isNextDay(a: moment.Moment, b: moment.Moment): boolean;

Usage Examples:

import {
  isSameDay,
  isInclusivelyAfterDay,
  isInclusivelyBeforeDay,
  isNextDay
} from "react-dates";
import moment from "moment";

// Date comparison examples
const today = moment();
const tomorrow = moment().add(1, 'day');
const yesterday = moment().subtract(1, 'day');

// Same day comparison
console.log(isSameDay(today, moment())); // true
console.log(isSameDay(today, tomorrow)); // false

// Inclusive comparisons (includes same day)
console.log(isInclusivelyAfterDay(tomorrow, today)); // true
console.log(isInclusivelyAfterDay(today, today)); // true
console.log(isInclusivelyBeforeDay(yesterday, today)); // true
console.log(isInclusivelyBeforeDay(today, today)); // true

// Next day check
console.log(isNextDay(today, tomorrow)); // true
console.log(isNextDay(today, moment().add(2, 'days'))); // false

// Practical usage in validation
function validateDateRange(startDate, endDate) {
  if (!startDate || !endDate) return false;
  
  // End date must be after or same as start date
  return isInclusivelyAfterDay(endDate, startDate);
}

function findNextAvailableDate(currentDate, blockedDates) {
  let nextDate = currentDate.clone().add(1, 'day');
  
  while (blockedDates.some(blocked => isSameDay(nextDate, blocked))) {
    nextDate.add(1, 'day');
  }
  
  return nextDate;
}

// Range validation helper
function isDateInRange(date, startDate, endDate) {
  return isInclusivelyAfterDay(date, startDate) && 
         isInclusivelyBeforeDay(date, endDate);
}

// Consecutive date checker
function hasConsecutiveDates(dates) {
  const sortedDates = dates.sort((a, b) => a.valueOf() - b.valueOf());
  
  for (let i = 0; i < sortedDates.length - 1; i++) {
    if (!isNextDay(sortedDates[i], sortedDates[i + 1])) {
      return false;
    }
  }
  
  return true;
}

Date Conversion Functions

Utilities for converting between different date formats and representations.

/**
 * Convert date to ISO string format (YYYY-MM-DD)
 * @param date - Moment object or date string
 * @param currentFormat - Current format of date string (if date is string)
 * @returns ISO formatted date string or null if invalid
 */
function toISODateString(date: moment.Moment | string, currentFormat?: string): string;

/**
 * Convert date to localized string format
 * @param date - Moment object or date string
 * @param currentFormat - Current format of date string (if date is string)
 * @returns Localized date string or null if invalid
 */
function toLocalizedDateString(date: moment.Moment | string, currentFormat?: string): string;

/**
 * Convert date string to moment object with validation
 * @param dateString - Date string to parse
 * @param customFormat - Custom format for parsing (optional)
 * @returns Moment object or null if invalid
 */
function toMomentObject(dateString: string, customFormat?: string): moment.Moment | null;

Usage Examples:

import {
  toISODateString,
  toLocalizedDateString,
  toMomentObject
} from "react-dates";
import moment from "moment";

// ISO date string conversion
const today = moment();
console.log(toISODateString(today)); // "2023-12-25"

// From custom format string
console.log(toISODateString("12/25/2023", "MM/DD/YYYY")); // "2023-12-25"
console.log(toISODateString("Dec 25, 2023", "MMM DD, YYYY")); // "2023-12-25"

// Localized string conversion
console.log(toLocalizedDateString(today)); // Depends on locale, e.g., "12/25/2023"
console.log(toLocalizedDateString("2023-12-25")); // "12/25/2023"

// String to moment conversion
const momentObj = toMomentObject("2023-12-25");
console.log(momentObj?.format("MMMM DD, YYYY")); // "December 25, 2023"

// Custom format parsing
const customDate = toMomentObject("25/12/2023", "DD/MM/YYYY");
console.log(customDate?.format("YYYY-MM-DD")); // "2023-12-25"

// Invalid date handling
const invalid = toMomentObject("invalid-date");
console.log(invalid); // null

// Practical usage examples
function formatDateForAPI(date) {
  if (moment.isMoment(date)) {
    return toISODateString(date);
  }
  return null;
}

function formatDateForDisplay(date) {
  if (moment.isMoment(date)) {
    return toLocalizedDateString(date);
  }
  return "";
}

function parseUserInput(dateString, expectedFormat = "MM/DD/YYYY") {
  const momentObj = toMomentObject(dateString, expectedFormat);
  
  if (momentObj) {
    return {
      isValid: true,
      date: momentObj,
      iso: toISODateString(momentObj),
      display: toLocalizedDateString(momentObj)
    };
  }
  
  return {
    isValid: false,
    date: null,
    iso: null,
    display: ""
  };
}

// Date range serialization
function serializeDateRange(startDate, endDate) {
  return {
    startDate: startDate ? toISODateString(startDate) : null,
    endDate: endDate ? toISODateString(endDate) : null
  };
}

function deserializeDateRange(serialized) {
  return {
    startDate: serialized.startDate ? toMomentObject(serialized.startDate) : null,
    endDate: serialized.endDate ? toMomentObject(serialized.endDate) : null
  };
}

Common Date Utility Patterns

Date Range Validation

import { 
  isSameDay, 
  isInclusivelyAfterDay, 
  isInclusivelyBeforeDay 
} from "react-dates";

// Complete date range validator
function validateDateRange(startDate, endDate, options = {}) {
  const { minDate, maxDate, minimumNights = 0 } = options;
  
  if (!startDate || !endDate) {
    return { isValid: false, error: "Both dates are required" };
  }
  
  // End must be after start
  if (!isInclusivelyAfterDay(endDate, startDate)) {
    return { isValid: false, error: "End date must be after start date" };
  }
  
  // Check minimum nights
  if (endDate.diff(startDate, 'days') < minimumNights) {
    return { 
      isValid: false, 
      error: `Minimum ${minimumNights} night(s) required` 
    };
  }
  
  // Check against min/max constraints
  if (minDate && isInclusivelyBeforeDay(startDate, minDate)) {
    return { isValid: false, error: "Start date is too early" };
  }
  
  if (maxDate && isInclusivelyAfterDay(endDate, maxDate)) {
    return { isValid: false, error: "End date is too late" };
  }
  
  return { isValid: true, error: null };
}

// Usage
const validation = validateDateRange(
  moment("2023-12-25"),
  moment("2023-12-27"),
  { minimumNights: 2, minDate: moment() }
);

if (!validation.isValid) {
  console.error(validation.error);
}

Date List Processing

import { isSameDay, isInclusivelyAfterDay } from "react-dates";

// Filter and sort date lists
function processDates(dates, options = {}) {
  const { filterPast = false, sort = true, unique = true } = options;
  
  let processedDates = [...dates];
  
  // Remove past dates if requested
  if (filterPast) {
    const today = moment().startOf('day');
    processedDates = processedDates.filter(date => 
      isInclusivelyAfterDay(date, today)
    );
  }
  
  // Remove duplicates if requested
  if (unique) {
    processedDates = processedDates.filter((date, index, array) => 
      array.findIndex(d => isSameDay(d, date)) === index
    );
  }
  
  // Sort if requested
  if (sort) {
    processedDates.sort((a, b) => a.valueOf() - b.valueOf());
  }
  
  return processedDates;
}

// Find date ranges in a list
function findDateRanges(dates) {
  const sortedDates = processDates(dates, { sort: true, unique: true });
  const ranges = [];
  let currentRange = null;
  
  sortedDates.forEach(date => {
    if (!currentRange) {
      currentRange = { start: date, end: date };
    } else if (isNextDay(currentRange.end, date)) {
      currentRange.end = date;
    } else {
      ranges.push(currentRange);
      currentRange = { start: date, end: date };
    }
  });
  
  if (currentRange) {
    ranges.push(currentRange);
  }
  
  return ranges;
}

Format Conversion Pipeline

import { toISODateString, toLocalizedDateString, toMomentObject } from "react-dates";

// Comprehensive date format converter
class DateConverter {
  static formats = {
    ISO: 'YYYY-MM-DD',
    US: 'MM/DD/YYYY',
    EU: 'DD/MM/YYYY',
    DISPLAY: 'MMM DD, YYYY',
    API: 'YYYY-MM-DD'
  };
  
  static convert(date, fromFormat, toFormat) {
    // Parse input
    let momentObj;
    if (moment.isMoment(date)) {
      momentObj = date;
    } else if (typeof date === 'string') {
      momentObj = toMomentObject(date, fromFormat);
    } else {
      return null;
    }
    
    if (!momentObj) return null;
    
    // Convert to target format
    switch (toFormat) {
      case 'ISO':
        return toISODateString(momentObj);
      case 'LOCALIZED':
        return toLocalizedDateString(momentObj);
      default:
        return momentObj.format(toFormat);
    }
  }
  
  static batch(dates, fromFormat, toFormat) {
    return dates.map(date => this.convert(date, fromFormat, toFormat))
                .filter(Boolean);
  }
}

// Usage examples
const dates = ['12/25/2023', '01/01/2024', '02/14/2024'];
const isoDates = DateConverter.batch(dates, 'MM/DD/YYYY', 'ISO');
console.log(isoDates); // ['2023-12-25', '2024-01-01', '2024-02-14']

const displayDates = DateConverter.batch(dates, 'MM/DD/YYYY', 'MMM DD, YYYY');
console.log(displayDates); // ['Dec 25, 2023', 'Jan 01, 2024', 'Feb 14, 2024']

Error Handling

All utility functions handle invalid dates gracefully:

// Safe date operations
function safeOperation(dateA, dateB, operation) {
  try {
    if (!moment.isMoment(dateA) || !moment.isMoment(dateB)) {
      return false;
    }
    
    if (!dateA.isValid() || !dateB.isValid()) {
      return false;
    }
    
    return operation(dateA, dateB);
  } catch (error) {
    console.warn('Date operation failed:', error);
    return false;
  }
}

// Usage
const result = safeOperation(dateA, dateB, isSameDay);

Performance Considerations

The utility functions are optimized for performance:

  • Efficient Comparisons: Direct moment comparison without format conversion
  • Null Handling: Early returns for null/undefined values
  • Validation Caching: Results can be cached for repeated operations
  • Memory Efficient: No intermediate object creation in comparisons
// Performance-optimized date range checking
function optimizedDateInRange(date, startDate, endDate) {
  // Early returns for null values
  if (!date || !startDate || !endDate) return false;
  
  // Single comparison operation
  return date.valueOf() >= startDate.valueOf() && 
         date.valueOf() <= endDate.valueOf();
}

Install with Tessl CLI

npx tessl i tessl/npm-react-dates

docs

calendar-components.md

calendar-controllers.md

index.md

input-components.md

main-pickers.md

utilities.md

tile.json