npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

24x7

v1.0.0

Published

A production-ready date/time library for JavaScript with first-class intervals, business-aware date math, timezone support, and deterministic time control

Readme

24x7

A production-ready date/time library for JavaScript with unique features for modern applications.

Why 24x7 Exists

24x7 was created to provide a modern, production-ready date/time library with unique features:

  1. First-class intervals - Work with time intervals as first-class objects with end-exclusive behavior
  2. First-class date ranges - Work with date ranges as first-class objects, not just calculations
  3. Business-aware date math - Handle business days, holidays, and custom weekends with ease
  4. Deterministic time control - Freeze time for predictable testing without global side effects
  5. Timezone support - Full timezone support with UTC default and IANA timezone support
  6. Global configuration - Set defaults for formats, weekends, holidays, and more
  7. Flexible parsing - Support for custom date formats with automatic ISO detection

Installation

npm install 24x7

Quick Start

import _tx from '24x7';

// Create a date
const date = _tx('2025-01-15');

// Chain operations (all immutable)
const nextWeek = date.addDays(7).startOfDay();

// Format
console.log(nextWeek.format('YYYY-MM-DD')); // '2025-01-22'

Configuration

24x7 supports comprehensive global configuration with a structured, hierarchical system. Configuration follows a priority system:

  1. Instance overrides (highest priority)
  2. Global config (set via _tx.config())
  3. DEFAULT_CONFIG (lowest priority)

Basic Usage

import _tx from '24x7';

// Get current configuration
const config = _tx.config();

// Set configuration options
_tx.config({
  immutable: true,
  parsing: {
    inputFormats: ['DD/MM/YYYY', 'YYYY-MM-DD'],
    strict: false,
    safe: true
  },
  formatting: {
    outputFormat: 'YYYY-MM-DD'
  },
  calendar: {
    weekends: [0, 6],
    holidays: ['2025-01-15']
  }
});

// Reset to defaults
_tx.resetConfig();

Configuration Structure

Immutability

_tx.config({
  immutable: true,          // Instances are immutable by default
  mutableFallback: false    // Allow mutation only when explicitly enabled
});

Usage:

// Default: immutable (returns new instance)
const date1 = _tx('2025-01-15');
const date2 = date1.addDays(5); // date1 unchanged

// Explicitly enable mutation
const date3 = _tx('2025-01-15').immutable(false);
date3.addDays(5); // date3 is mutated in place

Parsing Configuration

_tx.config({
  parsing: {
    inputFormats: ['DD/MM/YYYY', 'YYYY-MM-DD'], // Priority order
    strict: false,          // Reject invalid dates
    safe: true,             // Do not throw on invalid input
    overflow: 'reject',     // 'reject' | 'clamp'
    yearPivot: 50,          // Two-digit year pivot (50 = 1950-2049)
    monthNames: null,       // Custom month names
    allowNative: true,      // Fallback to native Date parsing
    trimInput: true         // Trim input strings
  }
});

Examples:

// Use inputFormats for automatic parsing
_tx.config({
  parsing: {
    inputFormats: ['DD/MM/YYYY', 'MM/DD/YYYY']
  }
});

x('23/01/2025'); // Parsed as DD/MM/YYYY
x('01/23/2025'); // Parsed as MM/DD/YYYY

// Strict mode rejects invalid dates
_tx.config({
  parsing: {
    strict: true
  }
});

// Safe mode returns current date instead of throwing
_tx.config({
  parsing: {
    safe: true
  }
});
x('invalid'); // Returns current date instead of throwing

Formatting Configuration

_tx.config({
  formatting: {
    outputFormat: 'YYYY-MM-DD',
    
    tokens: {},             // Custom format tokens
    
    ordinal: n => `${n}th`, // Custom ordinal function
    
    pad: {
      year: 4,
      month: 2,
      day: 2,
      hour: 2,
      minute: 2,
      second: 2
    },
    
    rules: {
      sameDay: 'HH:mm',     // Format for same day
      sameYear: 'MMM D',    // Format for same year
      default: 'YYYY-MM-DD' // Default format
    }
  }
});

Examples:

// Set default output format
_tx.config({
  formatting: {
    outputFormat: 'MMMM D, YYYY'
  }
});

x('2025-01-15').format(); // "January 15, 2025"

// Custom tokens
_tx.config({
  formatting: {
    tokens: {
      'Q': (date) => Math.floor(date.month() / 3) + 1
    }
  }
});

x('2025-01-15').format('YYYY-Q'); // "2025-1"

Calendar / Business Logic

_tx.config({
  calendar: {
    weekends: [0, 6],       // Sunday, Saturday
    weekStart: 1,           // Monday (0=Sunday, 1=Monday, etc.)
    holidays: ['2025-01-15', '2025-12-25'], // ISO date strings
    
    businessHours: {
      start: '09:00',
      end: '17:00'
    },
    
    includeHolidaysInRanges: false
  }
});

Examples:

// Set global weekends and holidays
_tx.config({
  calendar: {
    weekends: [0, 6],
    holidays: ['2025-01-15', '2025-12-25']
  }
});

// Business day methods use config defaults
x('2025-01-10').addBusinessDays(5); // Uses config weekends/holidays

// Override per call
x('2025-01-10').addBusinessDays(5, {
  weekends: [5, 6], // Override config
  holidays: ['2025-01-20'] // Override config
});

Date Math Configuration

_tx.config({
  math: {
    clampMode: 'end',       // 'end' | 'strict' | 'overflow'
    diffRounding: 'floor'   // 'floor' | 'ceil' | 'round'
  }
});

Time / Clock Control

_tx.config({
  time: {
    timezone: 'UTC',        // 'UTC' | 'local' | IANA timezone (e.g., 'America/New_York')
    precision: 'ms',        // 'ms' | 's'
    freeze: false,          // false | Date | ISO string
    dst: 'preserve',        // 'preserve' | 'shift' | 'error'
    startOfDay: '00:00'
  }
});

Examples:

// Set default timezone to UTC (default)
_tx.config({
  time: {
    timezone: 'UTC'
  }
});

// Set default timezone to local
_tx.config({
  time: {
    timezone: 'local'
  }
});

// Set default timezone to IANA timezone
_tx.config({
  time: {
    timezone: 'America/New_York'
  }
});

// Freeze time
_tx.config({
  time: {
    freeze: '2025-01-15' // Freeze to specific date
  }
});

// Or use the convenience method
_tx.freeze('2025-01-15');

Debugging Configuration

_tx.config({
  debug: {
    warnings: false,
    onWarn: (message, context) => {
      console.warn(message, context);
    }
  }
});

Performance Configuration

_tx.config({
  performance: {
    cacheFormats: true,     // Cache compiled format patterns
    fastParse: false        // Use faster but less strict parsing
  }
});

Policy / Guards

_tx.config({
  policy: {
    maxFuture: '2y',        // Maximum future date (e.g., '2y', '1M')
    maxPast: '10y',         // Maximum past date
    forbidWeekends: false,  // Reject weekend dates
    forbidHolidays: false   // Reject holiday dates
  }
});

Intervals / Ranges Configuration

_tx.config({
  intervals: {
    inclusive: false,       // End-exclusive by default
    allowZeroDuration: false,
    splitRemainder: true,   // Keep leftover when splitting
    defaultSplitUnit: 'minute',
    overlap: {
      inclusive: false      // End === start is NOT overlap
    },
    businessOnly: false     // Ignore non-business time by default
  }
});

Examples:

// End-exclusive intervals (default)
_tx.config({
  intervals: {
    inclusive: false
  }
});
const interval = _tx('2025-01-01').interval('2025-01-10');
interval.contains('2025-01-10'); // false (end-exclusive)

// End-inclusive intervals
_tx.config({
  intervals: {
    inclusive: true
  }
});
const inclusiveInterval =_tx('2025-01-01').interval('2025-01-10');
inclusiveInterval.contains('2025-01-10'); // true (end-inclusive)

// Allow zero-duration intervals
_tx.config({
  intervals: {
    allowZeroDuration: true
  }
});
const zeroInterval =_tx('2025-01-01').interval('2025-01-01'); // Allowed

Instance-Level Configuration

You can override configuration per instance:

// Create instance with custom config
const date =_tx('2025-01-15', {
  immutable: false,
  calendar: {
    weekends: [5, 6] // Friday-Saturday weekend
  }
});

// Or modify immutability after creation
const mutableDate =_tx('2025-01-15').immutable(false);
mutableDate.addDays(5); // Mutates in place

Configuration Resolution

Configuration is resolved with the following priority:

  1. Instance overrides - Passed when creating DateTime or via .immutable()
  2. Global config - Set via _tx.config()
  3. DEFAULT_CONFIG - Built-in defaults
// Global config
_tx.config({
  calendar: {
    weekends: [0, 6]
  }
});

// Instance override
const date =_tx('2025-01-15', {
  calendar: {
    weekends: [5, 6] // This instance uses Friday-Saturday
  }
});

// Method-level override (highest priority)
date.addBusinessDays(5, {
  weekends: [1, 2] // This call uses Monday-Tuesday
});

Core Features

Immutable API (Default)

By default, all operations return new instances. The original date is never modified:

const date1 =_tx('2025-01-15');
const date2 = date1.addDays(5);

console.log(date1.format('YYYY-MM-DD')); // '2025-01-15' (unchanged)
console.log(date2.format('YYYY-MM-DD')); // '2025-01-20'

Mutable Mode (Explicit)

You can enable mutation for specific instances:

// Enable mutation explicitly
const date =_tx('2025-01-15').immutable(false);
date.addDays(5); // Mutates date in place

// Check immutability
date.isImmutable(); // false

// Create mutable instance from config
const mutableDate =_tx('2025-01-15', { immutable: false });
mutableDate.addDays(5); // Mutates in place

Note: Immutability is the default and recommended mode. Use mutable mode only when explicitly needed for performance or specific use cases.

Chainable Methods

Methods can be chained for fluent APIs:

const result =_tx('2025-01-15')
  .addMonths(1)
  .startOfMonth()
  .addDays(2)
  .format('YYYY-MM-DD');

Unique Features

1️⃣ Intervals (First-class)

Work with time intervals as first-class objects with end-exclusive behavior by default:

Creating Intervals

import _tx from '24x7';

// Create an interval (end-exclusive by default)
const interval =_tx('2025-01-01').interval('2025-01-10');

// Static method (alternative)
const staticInterval = _tx.interval('2025-01-01', '2025-01-10');

// End-exclusive: [start, end) - end is NOT included
const exclusive =_tx('2025-01-01').interval('2025-01-10');
exclusive.contains('2025-01-01'); // true
exclusive.contains('2025-01-10'); // false (end is exclusive)

Interval Configuration

_tx.config({
  intervals: {
    inclusive: false,       // End-exclusive by default
    allowZeroDuration: false,
    splitRemainder: true,   // Keep leftover when splitting
    defaultSplitUnit: 'minute',
    overlap: {
      inclusive: false      // End === start is NOT overlap
    },
    businessOnly: false     // Ignore non-business time by default
  }
});

Checking if Date or Interval is Contained

import _tx from '24x7';

const interval =_tx('2025-01-01').interval('2025-01-10');

// Check if date is in interval
interval.contains('2025-01-05'); // true
interval.contains('2025-01-10'); // false (end-exclusive)

// Check if another interval is contained
const large =_tx('2025-01-01').interval('2025-01-31');
const small =_tx('2025-01-10').interval('2025-01-20');
large.contains(small); // true

Overlap Detection

import _tx from '24x7';

const interval1 =_tx('2025-01-01').interval('2025-01-10');
const interval2 =_tx('2025-01-05').interval('2025-01-15');
const interval3 =_tx('2025-01-20').interval('2025-01-25');

interval1.overlaps(interval2); // true
interval1.overlaps(interval3); // false

// With inclusive overlap option
interval1.overlaps(interval2, { inclusive: true });

Intersection

import _tx from '24x7';

const interval1 =_tx('2025-01-01').interval('2025-01-10');
const interval2 =_tx('2025-01-05').interval('2025-01-15');

const intersection = interval1.intersect(interval2);
if (intersection) {
  console.log(intersection.start().format('YYYY-MM-DD')); // '2025-01-05'
  console.log(intersection.end().format('YYYY-MM-DD'));   // '2025-01-10'
}

Clamping

import _tx from '24x7';

const container =_tx('2025-01-01').interval('2025-01-31');
const toClamp =_tx('2024-12-25').interval('2025-02-05');

const clamped = container.clamp(toClamp);
// Clamped to fit within container: 2025-01-01 to 2025-01-31

Splitting Intervals

import _tx from '24x7';

const interval =_tx('2025-01-01T00:00:00').interval('2025-01-01T02:30:00');

// Split by unit
const hourIntervals = interval.splitBy('hour');
// Returns array of 1-hour intervals

// Split by duration (milliseconds)
const customIntervals = interval.splitBy(30 * 60 * 1000); // 30 minutes

Iterating Over Intervals

import _tx from '24x7';

const interval =_tx('2025-01-01').interval('2025-01-05');

// Iterate by day
for (const day of interval.iterate('day')) {
  console.log(day.format('YYYY-MM-DD'));
}
// Output:
// 2025-01-01
// 2025-01-02
// 2025-01-03
// 2025-01-04
// (2025-01-05 is excluded - end-exclusive)

Duration Calculations

import _tx from '24x7';

const interval =_tx('2025-01-01T10:00:00').interval('2025-01-01T14:30:00');

interval.duration();        // Duration in milliseconds (default)
interval.duration('s');     // Duration in seconds
interval.duration('minute'); // Duration in minutes
interval.duration('hour');  // Duration in hours
interval.duration('day');   // Duration in days

Merging Intervals

import _tx from '24x7';

const interval1 =_tx('2025-01-01').interval('2025-01-10');
const interval2 =_tx('2025-01-05').interval('2025-01-15');
const interval3 =_tx('2025-01-20').interval('2025-01-25');

const merged = _tx.merge([interval1, interval2, interval3]);
// Returns interval from earliest start to latest end
// 2025-01-01 to 2025-01-25

Real-World Use Cases

import _tx from '24x7';

// Use case 1: Time slot booking
function isTimeSlotAvailable(slot, bookedSlots) {
  return !bookedSlots.some(booked => slot.overlaps(booked));
}

// Use case 2: Calculate working hours in interval
function getWorkingHours(interval, businessHours) {
  // Filter to business hours only
  return interval.splitBy('hour').filter(hour => {
    const hourStart = hour.start().hour();
    return hourStart >= 9 && hourStart < 17;
  });
}

// Use case 3: Find gaps between intervals
function findGaps(intervals) {
  // Sort by start time
  const sorted = [...intervals].sort((a, b) => 
    a.start().getTime() - b.start().getTime()
  );
  
  const gaps = [];
  for (let i = 0; i < sorted.length - 1; i++) {
    const gap = sorted[i].end().interval(sorted[i + 1].start());
    if (gap.duration() > 0) {
      gaps.push(gap);
    }
  }
  return gaps;
}

2️⃣ Date Ranges (First-class)

Work with date ranges as first-class objects:

Creating Ranges

import _tx from '24x7';

// Create a range from a date to another date
const range =_tx('2025-01-01').range('2025-01-10');

// Static method (alternative)
const staticRange = _tx.range('2025-01-01', '2025-01-10');

// Range with different input types
const range2 =_tx('2025-01-01').range(new Date('2025-01-10'));
const range3 =_tx(new Date('2025-01-01')).range('2025-01-10');

// Single day range
const singleDay =_tx('2025-01-15').range('2025-01-15');

Checking if Date is in Range

import _tx from '24x7';

const range =_tx('2025-01-01').range('2025-01-10');

// Check with DateTime instance
range.contains(_tx('2025-01-05')); // true
range.contains(_tx('2025-01-15')); // false

// Check with string
range.contains('2025-01-05'); // true
range.contains('2025-01-01'); // true (inclusive start)
range.contains('2025-01-10'); // true (inclusive end)

// Check with Date object
range.contains(new Date('2025-01-05')); // true

// Real-world example: Check if today is in a promotion period
const promotionStart =_tx('2025-01-01');
const promotionEnd =_tx('2025-01-31');
const promotionRange = promotionStart.range(promotionEnd);
const isPromotionActive = promotionRange.contains(_tx()); // true if today is in range

Getting Range Information

import _tx from '24x7';

const range =_tx('2025-01-01').range('2025-01-10');

// Get start and end dates
const start = range.start(); // DateTime instance
const end = range.end();     // DateTime instance

console.log(start.format('YYYY-MM-DD')); // '2025-01-01'
console.log(end.format('YYYY-MM-DD'));   // '2025-01-10'

// Get number of days (inclusive)
range.days(); // 10 (includes both start and end)

// Calculate duration in other units
const days = range.days();
const hours = days * 24;
const minutes = hours * 60;

Iterating Over Range

import _tx from '24x7';

const range =_tx('2025-01-01').range('2025-01-05');

// Iterate with for...of
for (const day of range) {
  console.log(day.format('YYYY-MM-DD'));
}
// Output:
// 2025-01-01
// 2025-01-02
// 2025-01-03
// 2025-01-04
// 2025-01-05

// Convert to array
const daysArray = range.toArray();
console.log(daysArray.length); // 5

// Use array methods
const weekdays = range.toArray().filter(day => {
  const dayOfWeek = day.day();
  return dayOfWeek !== 0 && dayOfWeek !== 6; // Exclude weekends
});

// Map to formatted strings
const formattedDays = range.toArray().map(day => day.format('MMM D'));
console.log(formattedDays); // ['Jan 1', 'Jan 2', 'Jan 3', 'Jan 4', 'Jan 5']

// Real-world example: Get all business days in a range
const businessDays = range.toArray().filter(day => {
  return day.isBusinessDay({ weekends: [0, 6] });
});

Range Overlap Detection

import _tx from '24x7';

const range1 =_tx('2025-01-01').range('2025-01-10');
const range2 =_tx('2025-01-05').range('2025-01-15');
const range3 =_tx('2025-01-20').range('2025-01-25');

// Check if ranges overlap
range1.overlaps(range2); // true (overlaps from Jan 5-10)
range1.overlaps(range3); // false (no overlap)

// Real-world example: Check if booking periods conflict
const booking1 =_tx('2025-01-10').range('2025-01-15');
const booking2 =_tx('2025-01-12').range('2025-01-18');
const hasConflict = booking1.overlaps(booking2); // true

// Find overlapping period
if (booking1.overlaps(booking2)) {
  const overlapStart = booking1.start().isAfter(booking2.start()) 
    ? booking1.start() 
    : booking2.start();
  const overlapEnd = booking1.end().isBefore(booking2.end()) 
    ? booking1.end() 
    : booking2.end();
  const overlapRange = overlapStart.range(overlapEnd);
  console.log(`Overlap: ${overlapRange.days()} days`);
}

Real-World Use Cases

import _tx from '24x7';

// Use case 1: Event date range validation
function isEventDateValid(eventStart, eventEnd, today = _tx()) {
  const eventRange = eventStart.range(eventEnd);
  return eventRange.contains(today) || eventRange.start().isAfter(today);
}

// Use case 2: Calculate working days in a project timeline
function getWorkingDays(startDate, endDate, holidays = []) {
  const range =_tx(startDate).range(endDate);
  return range.toArray().filter(day => {
    return day.isBusinessDay({ weekends: [0, 6], holidays });
  }).length;
}

// Use case 3: Check if two schedules conflict
function schedulesConflict(schedule1Start, schedule1End, schedule2Start, schedule2End) {
  const schedule1 =_tx(schedule1Start).range(schedule1End);
  const schedule2 =_tx(schedule2Start).range(schedule2End);
  return schedule1.overlaps(schedule2);
}

3️⃣ Business-Aware Date Math

Handle business days with custom weekends and holidays:

Adding Business Days

import _tx from '24x7';

// Basic usage: Add 5 business days (skips weekends)
const result =_tx('2025-01-10') // Friday
  .addBusinessDays(5, {
    weekends: [0, 6] // Sunday and Saturday
  });
// Result: 2025-01-17 (Friday) - skips weekend

// With holidays
const result2 =_tx('2025-01-10')
  .addBusinessDays(5, {
    weekends: [0, 6],
    holidays: ['2025-01-15', '2025-01-20'] // Wednesday and Monday
  });
// Skips weekends AND holidays

// Custom weekends (e.g., Friday-Saturday weekend)
const result3 =_tx('2025-01-08') // Wednesday
  .addBusinessDays(3, {
    weekends: [5, 6] // Friday and Saturday
  });

// Real-world example: Calculate delivery date (5 business days)
const orderDate =_tx('2025-01-10');
const holidays = ['2025-01-15', '2025-01-20']; // Company holidays
const deliveryDate = orderDate.addBusinessDays(5, {
  weekends: [0, 6],
  holidays
});
console.log(`Order placed: ${orderDate.format('MMM D')}`);
console.log(`Expected delivery: ${deliveryDate.format('MMM D')}`);

Subtracting Business Days

import _tx from '24x7';

// Subtract business days
const past =_tx('2025-01-20')
  .subtractBusinessDays(3, {
    weekends: [0, 6]
  });

// Real-world example: Calculate deadline (10 business days ago)
const deadline =_tx('2025-01-31')
  .subtractBusinessDays(10, {
    weekends: [0, 6],
    holidays: ['2025-01-15']
  });
console.log(`Deadline: ${deadline.format('MMM D')}`);

Checking Business Days

import _tx from '24x7';

// Check if a date is a business day
x('2025-01-15').isBusinessDay({
  weekends: [0, 6],
  holidays: ['2025-01-15']
}); // false (it's a holiday)

x('2025-01-16').isBusinessDay({
  weekends: [0, 6],
  holidays: ['2025-01-15']
}); // true (Thursday, not a holiday)

x('2025-01-18').isBusinessDay({
  weekends: [0, 6]
}); // false (Saturday)

// Real-world example: Validate business day
function canProcessOrder(date, holidays = []) {
  return _tx(date).isBusinessDay({
    weekends: [0, 6],
    holidays
  });
}

if (canProcessOrder('2025-01-15', ['2025-01-15'])) {
  console.log('Order can be processed today');
} else {
  console.log('Order will be processed next business day');
}

Counting Business Days Between Dates

import _tx from '24x7';

// Count business days between two dates
const count =_tx('2025-01-01')
  .businessDaysUntil('2025-01-31', {
    weekends: [0, 6],
    holidays: ['2025-01-15']
  });
// Returns 21 (excluding weekends and holidays)

// Forward count (positive)
x('2025-01-10').businessDaysUntil('2025-01-20', {
  weekends: [0, 6]
}); // 7 business days

// Backward count (negative)
x('2025-01-20').businessDaysUntil('2025-01-10', {
  weekends: [0, 6]
}); // -7 business days

// Real-world example: Calculate SLA remaining days
function getSLARemainingDays(startDate, endDate, holidays = []) {
  const today = _tx();
  const remaining = today.businessDaysUntil(endDate, {
    weekends: [0, 6],
    holidays
  });
  return Math.max(0, remaining); // Don't return negative
}

const startDate =_tx('2025-01-01');
const endDate =_tx('2025-01-31');
const remaining = getSLARemainingDays(startDate, endDate, ['2025-01-15']);
console.log(`${remaining} business days remaining`);

Custom Weekend Configurations

import _tx from '24x7';

// Middle Eastern weekend (Friday-Saturday)
const result =_tx('2025-01-08') // Wednesday
  .addBusinessDays(3, {
    weekends: [5, 6] // Friday and Saturday
  });

// No weekends (7-day work week)
const result2 =_tx('2025-01-10')
  .addBusinessDays(5, {
    weekends: [] // No weekends
  });

// Single day weekend
const result3 =_tx('2025-01-10')
  .addBusinessDays(5, {
    weekends: [0] // Only Sunday
  });

Holiday Management

import _tx from '24x7';

// Holidays as strings (ISO format preferred)
const holidays = [
  '2025-01-01', // New Year
  '2025-01-15', // Custom holiday
  '2025-12-25'  // Christmas
];

// Holidays as Date objects
const holidayDates = [
  new Date('2025-01-01'),
  new Date('2025-01-15'),
  new Date('2025-12-25')
];

// Holidays as DateTime instances
const holidayDateTimes = [
_tx('2025-01-01'),
_tx('2025-01-15'),
_tx('2025-12-25')
];

// All work the same
const result = _tx('2025-01-10')
  .addBusinessDays(5, {
    weekends: [0, 6],
    holidays: holidays // or holidayDates or holidayDateTimes
  });

// Real-world example: Company holiday calendar
class HolidayCalendar {
  constructor() {
    this.holidays = [];
  }

  addHoliday(date) {
    this.holidays.push(_tx(date).format('YYYY-MM-DD'));
    return this;
  }

  getBusinessDays(startDate, endDate) {
    return _tx(startDate).businessDaysUntil(endDate, {
      weekends: [0, 6],
      holidays: this.holidays
    });
  }
}

const calendar = new HolidayCalendar();
calendar
  .addHoliday('2025-01-01')
  .addHoliday('2025-07-04')
  .addHoliday('2025-12-25');

const businessDays = calendar.getBusinessDays('2025-01-01', '2025-01-31');
console.log(`${businessDays} business days in January`);

Real-World Use Cases

import _tx from '24x7';

// Use case 1: Calculate invoice due date (Net 30 business days)
function getInvoiceDueDate(invoiceDate, holidays = []) {
  return _tx(invoiceDate).addBusinessDays(30, {
    weekends: [0, 6],
    holidays
  });
}

// Use case 2: Project timeline with business days only
function calculateProjectEndDate(startDate, businessDaysNeeded, holidays = []) {
  return _tx(startDate).addBusinessDays(businessDaysNeeded, {
    weekends: [0, 6],
    holidays
  });
}

// Use case 3: SLA monitoring
function checkSLACompliance(createdDate, slaBusinessDays, holidays = []) {
  const today = _tx();
  const deadline =_tx(createdDate).addBusinessDays(slaBusinessDays, {
    weekends: [0, 6],
    holidays
  });
  
  return {
    deadline: deadline.format('YYYY-MM-DD'),
    isOverdue: today.isAfter(deadline),
    daysRemaining: today.businessDaysUntil(deadline, {
      weekends: [0, 6],
      holidays
    })
  };
}

// Use case 4: Work schedule validation
function isValidWorkSchedule(startDate, endDate, maxConsecutiveDays, holidays = []) {
  const range =_tx(startDate).range(endDate);
  const businessDays = range.toArray().filter(day => 
    day.isBusinessDay({ weekends: [0, 6], holidays })
  );
  
  // Check for consecutive days exceeding limit
  let consecutive = 1;
  let maxConsecutive = 1;
  
  for (let i = 1; i < businessDays.length; i++) {
    const daysDiff = businessDays[i].getTime() - businessDays[i-1].getTime();
    if (daysDiff === 24 * 60 * 60 * 1000) { // Exactly 1 day
      consecutive++;
      maxConsecutive = Math.max(maxConsecutive, consecutive);
    } else {
      consecutive = 1;
    }
  }
  
  return maxConsecutive <= maxConsecutiveDays;
}

4️⃣ Deterministic Time Control (Testing-Friendly)

Freeze time for predictable tests:

Basic Freeze/Unfreeze

import _tx from '24x7';

// Freeze time to a specific date
_tx.freeze('2025-01-15');

// Now _tx() always returns the frozen time
const now = _tx(); // Always '2025-01-15'
const later = _tx().addDays(1); // Always '2025-01-16'

// Unfreeze to return to normal behavior
_tx.unfreeze();

// Check if time is frozen
_tx.isFrozen(); // false

Freezing with Different Input Types

import _tx from '24x7';

// Freeze with string
_tx.freeze('2025-01-15');

// Freeze with Date object
_tx.freeze(new Date('2025-01-15'));

// Freeze with timestamp
_tx.freeze(1705334400000);

// Freeze to current time (useful for snapshot testing)
_tx.freeze(); // Freezes to current moment

Testing Examples

import _tx from '24x7';

// Example 1: Testing date-dependent logic
describe('Order Processing', () => {
  beforeEach(() => {
    // Freeze time to a known date
    _tx.freeze('2025-01-15T10:00:00');
  });

  afterEach(() => {
    // Always unfreeze in teardown
    _tx.unfreeze();
  });

  it('should calculate delivery date correctly', () => {
    const orderDate = _tx(); // Always '2025-01-15'
    const deliveryDate = orderDate.addBusinessDays(5, {
      weekends: [0, 6]
    });
    
    expect(deliveryDate.format('YYYY-MM-DD')).toBe('2025-01-22');
  });

  it('should handle same-day orders', () => {
    const orderDate = _tx();
    const isSameDay = orderDate.format('YYYY-MM-DD') === _tx().format('YYYY-MM-DD');
    expect(isSameDay).toBe(true);
  });
});

// Example 2: Testing time-sensitive features
describe('Promotion System', () => {
  beforeEach(() => {
    _tx.freeze('2025-01-10');
  });

  afterEach(() => {
    _tx.unfreeze();
  });

  it('should check if promotion is active', () => {
    const promotionStart =_tx('2025-01-01');
    const promotionEnd =_tx('2025-01-31');
    const range = promotionStart.range(promotionEnd);
    
    expect(range.contains(_tx())).toBe(true); // Today is in range
  });

  it('should detect expired promotions', () => {
    _tx.freeze('2025-02-01'); // After promotion ends
    
    const promotionStart =_tx('2025-01-01');
    const promotionEnd =_tx('2025-01-31');
    const range = promotionStart.range(promotionEnd);
    
    expect(range.contains(_tx())).toBe(false); // Today is not in range
  });
});

// Example 3: Testing with multiple freeze points
describe('Time Progression', () => {
  afterEach(() => {
    _tx.unfreeze();
  });

  it('should simulate time progression', () => {
    // Start at day 1
    _tx.freeze('2025-01-01');
    const day1 = _tx();
    expect(day1.format('YYYY-MM-DD')).toBe('2025-01-01');

    // Move to day 2
    _tx.freeze('2025-01-02');
    const day2 = _tx();
    expect(day2.format('YYYY-MM-DD')).toBe('2025-01-02');

    // Move to day 3
    _tx.freeze('2025-01-03');
    const day3 = _tx();
    expect(day3.format('YYYY-MM-DD')).toBe('2025-01-03');
  });
});

Integration with Test Frameworks

import _tx from '24x7';

// Jest example
describe('My Feature', () => {
  beforeAll(() => {
    _tx.freeze('2025-01-15');
  });

  afterAll(() => {
    _tx.unfreeze();
  });

  test('feature works with frozen time', () => {
    // Your test code
  });
});

// Mocha example
describe('My Feature', function() {
  beforeEach(function() {
    _tx.freeze('2025-01-15');
  });

  afterEach(function() {
    _tx.unfreeze();
  });

  it('should work with frozen time', function() {
    // Your test code
  });
});

// Vitest example
import { beforeEach, afterEach, test } from 'vitest';

beforeEach(() => {
  _tx.freeze('2025-01-15');
});

afterEach(() => {
  _tx.unfreeze();
});

test('feature works', () => {
  // Your test code
});

Real-World Testing Scenarios

import _tx from '24x7';

// Scenario 1: Testing subscription renewal
describe('Subscription Service', () => {
  beforeEach(() => {
    _tx.freeze('2025-01-15');
  });

  afterEach(() => {
    _tx.unfreeze();
  });

  it('should calculate next billing date', () => {
    const subscriptionDate =_tx('2025-01-01');
    const nextBilling = subscriptionDate.addMonths(1);
    
    expect(nextBilling.format('YYYY-MM-DD')).toBe('2025-02-01');
  });

  it('should detect expired subscriptions', () => {
    const expiryDate =_tx('2025-01-10');
    const isExpired = _tx().isAfter(expiryDate);
    
    expect(isExpired).toBe(true); // Today is after expiry
  });
});

// Scenario 2: Testing business day calculations
describe('Business Day Calculator', () => {
  beforeEach(() => {
    _tx.freeze('2025-01-15'); // Wednesday
  });

  afterEach(() => {
    _tx.unfreeze();
  });

  it('should skip weekends', () => {
    const start =_tx('2025-01-15'); // Wednesday
    const result = start.addBusinessDays(2, { weekends: [0, 6] });
    
    // Should be Friday (skips weekend if it falls on one)
    expect(result.format('YYYY-MM-DD')).toBe('2025-01-17');
  });

  it('should skip holidays', () => {
    const start =_tx('2025-01-14'); // Tuesday
    const result = start.addBusinessDays(1, {
      weekends: [0, 6],
      holidays: ['2025-01-15'] // Wednesday is a holiday
    });
    
    // Should skip to Thursday
    expect(result.format('YYYY-MM-DD')).toBe('2025-01-16');
  });
});

// Scenario 3: Testing date range operations
describe('Date Range Operations', () => {
  beforeEach(() => {
    _tx.freeze('2025-01-15');
  });

  afterEach(() => {
    _tx.unfreeze();
  });

  it('should check if current date is in range', () => {
    const range =_tx('2025-01-01').range('2025-01-31');
    expect(range.contains(_tx())).toBe(true);
  });

  it('should handle edge cases', () => {
    const range =_tx('2025-01-15').range('2025-01-15'); // Single day
    expect(range.contains(_tx())).toBe(true);
    expect(range.days()).toBe(1);
  });
});

Important Notes:

  • Freezing time is global - affects all _tx() calls
  • Always call _tx.unfreeze() in test teardown (afterEach/afterAll)
  • Freezing affects only _tx() without arguments - explicit dates are unaffected
  • Safe to use in parallel test runners (each test should manage its own freeze state)

Examples

Factory Function

Create dates from various inputs:

import _tx from '24x7';

// Current time
const now = _tx();
console.log(now.format('YYYY-MM-DD HH:mm:ss')); // Current date/time

// From ISO string
const date1 =_tx('2025-01-15');
const date2 =_tx('2025-01-15T14:30:00Z');
const date3 =_tx('2025-01-15T14:30:00.000Z');

// From Date object
const nativeDate = new Date('2025-01-15');
const date4 =_tx(nativeDate);

// From timestamp (milliseconds)
const date5 =_tx(1705334400000); // Unix timestamp

// From timestamp (seconds)
const date6 =_tx(1705334400 * 1000);

// Undefined uses current time
const date7 =_tx(undefined); // Same as _tx()

Date Manipulation

Add or subtract time units:

import _tx from '24x7';

const date =_tx('2025-01-15T10:30:00');

// Add milliseconds
date.add(1000); // Add 1 second

// Add time units
date.addYears(1);    // '2026-01-15T10:30:00'
date.addMonths(2);   // '2025-03-15T10:30:00'
date.addDays(7);     // '2025-01-22T10:30:00'
date.addHours(5);    // '2025-01-15T15:30:00'
date.addMinutes(30); // '2025-01-15T11:00:00'
date.addSeconds(45);  // '2025-01-15T10:30:45'

// Subtract time units
date.subtract(1000);      // Subtract 1 second
date.addDays(-7);         // Subtract 7 days (same as subtractDays)
date.subtract(7 * 24 * 60 * 60 * 1000); // Subtract 7 days in milliseconds

// Chain operations
const result =_tx('2025-01-15')
  .addMonths(1)
  .addDays(5)
  .addHours(2)
  .format('YYYY-MM-DD HH:mm'); // '2025-02-20 02:00'

Date Comparison

Compare dates in various ways:

import _tx from '24x7';

const date1 =_tx('2025-01-15');
const date2 =_tx('2025-01-20');
const date3 =_tx('2025-01-15');

// Before/After checks
date1.isBefore(date2);  // true
date1.isAfter(date2);   // false
date1.isSame(date3);    // true

// Compare with different input types
date1.isBefore('2025-01-20');        // true (string)
date1.isBefore(new Date('2025-01-20')); // true (Date object)
date1.isBefore(1705334400000);        // true (timestamp)

// Between check (inclusive)
const start =_tx('2025-01-10');
const end =_tx('2025-01-20');
const middle =_tx('2025-01-15');

middle.isBetween(start, end);  // true
start.isBetween(start, end);   // true (inclusive)
end.isBetween(start, end);     // true (inclusive)
x('2025-01-25').isBetween(start, end); // false

// Real-world example: Check if date is in current month
const today = _tx();
const monthStart = today.startOfMonth();
const monthEnd = today.endOfMonth();
const isInCurrentMonth = today.isBetween(monthStart, monthEnd); // Always true

Date Information

Extract date components:

import _tx from '24x7';

const date =_tx('2025-01-15T14:30:45.123');

// Get date parts
date.year();        // 2025
date.month();       // 0 (January, 0-indexed)
date.date();        // 15 (day of month, 1-indexed)
date.day();         // 3 (Wednesday, 0=Sunday)
date.hour();        // 14
date.minute();      // 30
date.second();      // 45
date.millisecond(); // 123

// Use in calculations
const isWeekend = date.day() === 0 || date.day() === 6;
const isJanuary = date.month() === 0;
const isLeapYear = date.year() % 4 === 0 && (date.year() % 100 !== 0 || date.year() % 400 === 0);

// Format with extracted values
const customFormat = `${date.year()}-${String(date.month() + 1).padStart(2, '0')}-${String(date.date()).padStart(2, '0')}`;

Start/End of Periods

Get boundaries of time periods:

import _tx from '24x7';

const date =_tx('2025-01-15T14:30:45');

// Day boundaries
date.startOfDay();  // '2025-01-15T00:00:00.000'
date.endOfDay();    // '2025-01-15T23:59:59.999'

// Month boundaries
date.startOfMonth(); // '2025-01-01T00:00:00.000'
date.endOfMonth();   // '2025-01-31T23:59:59.999'

// Year boundaries
date.startOfYear();  // '2025-01-01T00:00:00.000'
date.endOfYear();    // '2025-12-31T23:59:59.999'

// Common use case: Get all dates in current month
const monthStart = _tx().startOfMonth();
const monthEnd = _tx().endOfMonth();
const daysInMonth = monthEnd.date(); // 28-31 depending on month

// Get first Monday of month
const firstDay =_tx('2025-01-15').startOfMonth();
const firstMonday = firstDay.addDays((8 - firstDay.day()) % 7 || 7);

Conversion

Convert to different formats:

import _tx from '24x7';

const date =_tx('2025-01-15T14:30:45.123');

// To native Date object (cloned for safety)
const nativeDate = date.toDate();
console.log(nativeDate instanceof Date); // true

// To timestamp
const timestamp = date.valueOf(); // or date.getTime()
console.log(timestamp); // 1705334445123

// To ISO string
const isoString = date.toISOString();
console.log(isoString); // '2025-01-15T14:30:45.123Z'

// Implicit conversion (uses valueOf)
const num = +date; // Same as date.valueOf()
const str = String(date); // Uses toString()

// Use with native Date methods
const utcString = date.toDate().toUTCString();

Timezone

Work with different timezones (default is UTC):

import _tx from '24x7';

// Default timezone is UTC
const date =_tx('2025-01-15T10:00:00Z');
console.log(date.tz()); // 'UTC'
console.log(date.isUTC()); // true

// Convert to local timezone
const local = date.toLocal();
console.log(local.tz()); // 'local'
console.log(local.isLocal()); // true

// Convert to specific IANA timezone (chaining works!)
const kolkata = date.tz('Asia/Kolkata');
console.log(kolkata.tz()); // 'Asia/Kolkata'
console.log(kolkata.format('YYYY-MM-DD HH:mm:ss')); // Shows time in Kolkata timezone
console.log(kolkata.ianaTimezone()); // 'Asia/Kolkata'

// Convert to another timezone
const nyDate = date.toTimezone('America/New_York');
console.log(nyDate.tz()); // 'America/New_York'

// Convert back to UTC
const backToUTC = nyDate.toUTC();
console.log(backToUTC.isUTC()); // true

// Get timezone offset
console.log(date.timezoneOffset()); // 0 (minutes)
console.log(date.timezoneOffsetString()); // '+00:00'
console.log(kolkata.timezoneOffsetString()); // '+05:30'

// Set global default timezone
_tx.config({
  time: {
    timezone: 'America/Los_Angeles'
  }
});
const pacificDate =_tx('2025-01-15T10:00:00Z');
console.log(pacificDate.tz()); // 'America/Los_Angeles'

Supported Timezones

  • UTC - Coordinated Universal Time (default)
  • local - System's local timezone
  • IANA timezones - Any valid IANA timezone identifier (e.g., 'America/New_York', 'Asia/Kolkata', 'Europe/London')

Timezone Methods

date.tz()                    // Get current timezone
date.tz(timezone)            // Set timezone (returns DateTime for chaining)
date.toTimezone(timezone)    // Convert to timezone (alternative method)
date.toUTC()                 // Convert to UTC
date.toLocal()                // Convert to local timezone
date.timezoneOffset()         // Get offset in minutes
date.timezoneOffsetString()   // Get offset as string (e.g., '+05:30')
date.isUTC()                 // Check if UTC
date.isLocal()               // Check if local
date.ianaTimezone()          // Get IANA timezone name or null

Timezone-Aware Date Components

All date component methods (year(), month(), date(), hour(), minute(), second(), etc.) automatically respect the timezone:

const utc =_tx('2025-01-15T10:00:00Z');
console.log(utc.hour()); // 10 (UTC)

const kolkata = utc.tz('Asia/Kolkata');
console.log(kolkata.hour()); // 15 (IST, UTC+5:30)

// Formatting also respects timezone
console.log(utc.format('YYYY-MM-DD HH:mm:ss')); // '2025-01-15 10:00:00'
console.log(kolkata.format('YYYY-MM-DD HH:mm:ss')); // '2025-01-15 15:30:00'

Real-World Timezone Examples

import _tx from '24x7';

// Example 1: Convert meeting time across timezones
const meetingUTC =_tx('2025-01-15T14:00:00Z');
const meetingNY = meetingUTC.tz('America/New_York');
const meetingLA = meetingUTC.tz('America/Los_Angeles');
const meetingLondon = meetingUTC.tz('Europe/London');

console.log('Meeting times:');
console.log('UTC:', meetingUTC.format('YYYY-MM-DD HH:mm'));
console.log('New York:', meetingNY.format('YYYY-MM-DD HH:mm'));
console.log('Los Angeles:', meetingLA.format('YYYY-MM-DD HH:mm'));
console.log('London:', meetingLondon.format('YYYY-MM-DD HH:mm'));

// Example 2: Calculate time difference between timezones
const now = _tx();
const nyTime = now.tz('America/New_York');
const offset = nyTime.timezoneOffset() - now.timezoneOffset();
console.log(`Time difference: ${offset} minutes`);

// Example 3: Display user's local time
function displayUserTime(userTimezone) {
  const now = _tx().tz(userTimezone);
  return now.format('YYYY-MM-DD HH:mm:ss');
}

console.log(displayUserTime('Asia/Kolkata')); // User's local time in Kolkata
console.log(displayUserTime('America/New_York')); // User's local time in NY

Format Patterns

Format dates with custom patterns:

import _tx from '24x7';

const date =_tx('2025-01-15T14:30:45');

// Common formats
date.format('YYYY-MM-DD');           // '2025-01-15'
date.format('MM/DD/YYYY');           // '01/15/2025'
date.format('DD-MM-YYYY');           // '15-01-2025'
date.format('YYYY-MM-DD HH:mm:ss');  // '2025-01-15 14:30:45'

// Human-readable formats
date.format('MMMM D, YYYY');         // 'January 15, 2025'
date.format('MMM D, YYYY');          // 'Jan 15, 2025'
date.format('dddd, MMMM D, YYYY');   // 'Wednesday, January 15, 2025'
date.format('ddd, MMM D');          // 'Wed, Jan 15'

// Time formats
date.format('HH:mm');                // '14:30'
date.format('HH:mm:ss');             // '14:30:45'
date.format('hh:mm A');              // '02:30 PM'
date.format('hh:mm a');              // '02:30 pm'

// Combined formats
date.format('YYYY-MM-DD HH:mm');     // '2025-01-15 14:30'
date.format('MMM D, YYYY [at] hh:mm A'); // 'Jan 15, 2025 at 02:30 PM'

// File-safe format
date.format('YYYY-MM-DD_HH-mm-ss');  // '2025-01-15_14-30-45'

API Reference

Factory Function

_t_tx()                                    // Current time (or frozen time)
_tx('2025-01-15')                        // Parse string (ISO format)
_tx('23/01/2025', 'DD/MM/YYYY')          // Parse with format
_tx('2025-01-15', { immutable: false })  // With config object
_tx(new Date())                          // From Date object
_tx(1234567890)                          // From timestamp

Configuration Methods

_tx.config()              // Get current configuration
_tx.config(options)       // Set configuration (deep merge)
_tx.resetConfig()          // Reset to DEFAULT_CONFIG

Immutability Control

date.immutable(true)    // Enable immutability (default)
date.immutable(false)   // Disable immutability (mutable mode)
date.isImmutable()      // Check if instance is immutable

Date Manipulation

date.add(ms)           // Add milliseconds (returns new instance if immutable)
date.subtract(ms)      // Subtract milliseconds (returns new instance if immutable)
date.addYears(n)       // Add years
date.addMonths(n)      // Add months
date.addDays(n)        // Add days
date.addHours(n)       // Add hours
date.addMinutes(n)     // Add minutes
date.addSeconds(n)     // Add seconds

Interval Methods

date.interval(end)     // Create interval from date to end
_tx.interval(start, end)  // Static method to create interval

interval.contains(date | interval)  // Check if contains date or interval
interval.overlaps(other, options)   // Check if overlaps with another interval
interval.intersect(other)           // Get intersection interval or null
interval.clamp(other)               // Clamp interval to fit within this
interval.splitBy(unit | duration)   // Split into array of intervals
interval.iterate(unit)              // Iterator over interval
interval.duration(unit?)            // Get duration in specified unit
_tx.merge(intervals[])                // Merge multiple intervals

Date Comparison

date.isBefore(other)   // Check if before
date.isAfter(other)    // Check if after
date.isSame(other)     // Check if equal
date.isBetween(start, end) // Check if between

Date Information

date.year()            // Get year
date.month()           // Get month (0-11)
date.date()            // Get day of month (1-31)
date.day()             // Get day of week (0-6)
date.hour()            // Get hour (0-23)
date.minute()          // Get minute (0-59)
date.second()          // Get second (0-59)
date.millisecond()     // Get millisecond (0-999)

Start/End of Periods

date.startOfDay()      // Start of day (00:00:00.000)
date.endOfDay()        // End of day (23:59:59.999)
date.startOfMonth()    // Start of month
date.endOfMonth()      // End of month
date.startOfYear()     // Start of year
date.endOfYear()       // End of year

Conversion

date.toDate()          // Get native Date object
date.valueOf()         // Get timestamp
date.getTime()         // Get timestamp
date.toISOString()     // Get ISO string
date.format(pattern)   // Format with pattern (requires format plugin)

// Timezone methods
date.tz()              // Get current timezone (or set with date.tz('Asia/Kolkata'))
date.tz(timezone)      // Set timezone and return DateTime (for chaining)
date.timezone()        // Alias for tz() (backward compatibility)
date.toTimezone(tz)    // Convert to timezone
date.toUTC()           // Convert to UTC
date.toLocal()         // Convert to local
date.timezoneOffset()  // Get offset in minutes
date.timezoneOffsetString() // Get offset as string (e.g., '+05:30')
date.isUTC()           // Check if UTC
date.isLocal()         // Check if local
date.ianaTimezone()    // Get IANA timezone name or null

Format Patterns

date.format('YYYY-MM-DD')        // '2025-01-15'
date.format('MMM D, YYYY')       // 'Jan 15, 2025'
date.format('YYYY-MM-DD HH:mm')  // '2025-01-15 14:30'
date.format('dddd, MMMM D, YYYY') // 'Wednesday, January 15, 2025'

Pattern tokens:

  • YYYY - 4-digit year
  • YY - 2-digit year
  • MMMM - Full month name
  • MMM - Short month name
  • MM - 2-digit month
  • M - Month number
  • DD - 2-digit day
  • D - Day number
  • dddd - Full day name
  • ddd - Short day name
  • HH - 24-hour format (2-digit)
  • hh - 12-hour format (2-digit)
  • mm - Minutes (2-digit)
  • ss - Seconds (2-digit)
  • A - AM/PM
  • a - am/pm

Plugin System

Plugins extend the DateTime prototype. All plugins are included by default, but you can create custom plugins:

Basic Plugin Example

import _tx, { DateTime } from '24x7';

// Simplest form: Named function becomes the method
function myMethod() {
  // Your custom method
  return this;
}

_tx.extend(myMethod);

// Now you can use it
_tx().myMethod();

// Alternative 1: Explicit method name
_tx.extend('myMethod2', function() {
  return this;
});

// Alternative 2: Install function (for multiple methods)
_tx.extend((DateTime) => {
  DateTime.prototype.method1 = function() { return this; };
  DateTime.prototype.method2 = function() { return this; };
});

// Alternative 3: Object with install method (also supported)
const myPlugin = {
  install(DateTime) {
    DateTime.prototype.myMethod3 = function() {
      return this;
    };
  }
};

_tx.extend(myPlugin);

Advanced Plugin Examples

import _tx, { DateTime } from '24x7';

// Example 1: Add timezone offset methods (install function for multiple methods)
_tx.extend((DateTime) => {
  DateTime.prototype.getTimezoneOffset = function() {
    return this.toDate().getTimezoneOffset();
  };

  DateTime.prototype.toUTC = function() {
    const date = this.toDate();
    const utcDate = new Date(date.getTime() + (date.getTimezoneOffset() * 60000));
    return new DateTime(utcDate);
  };
});

_tx.extend(timezonePlugin);

// Usage
const date =_tx('2025-01-15T14:30:00');
console.log(date.getTimezoneOffset()); // -480 (PST)
const utc = date.toUTC();

// Example 2: Add age calculation (direct method)
function age(referenceDate = _tx()) {
  const ref = referenceDate instanceof DateTime 
    ? referenceDate 
    : new DateTime(new Date(referenceDate));
  
  let years = ref.year() - this.year();
  const monthDiff = ref.month() - this.month();
  const dayDiff = ref.date() - this.date();

  if (monthDiff < 0 || (monthDiff === 0 && dayDiff < 0)) {
    years--;
  }

  return years;
}

_tx.extend(age);

// Usage
const birthDate =_tx('1990-05-15');
const age = birthDate.age(); // Age as of today
const ageOnDate = birthDate.age(_tx('2025-05-15')); // Age on specific date

// Example 3: Add quarter methods (install function for multiple methods)
_tx.extend((DateTime) => {
  DateTime.prototype.quarter = function() {
    return Math.floor(this.month() / 3) + 1;
  };

  DateTime.prototype.startOfQuarter = function() {
    const quarter = this.quarter();
    const month = (quarter - 1) * 3;
    const newDate = new Date(this._date);
    newDate.setMonth(month, 1);
    newDate.setHours(0, 0, 0, 0);
    return new DateTime(newDate);
  };

  DateTime.prototype.endOfQuarter = function() {
    const quarter = this.quarter();
    const month = quarter * 3 - 1;
    const newDate = new Date(this._date);
    newDate.setMonth(month + 1, 0);
    newDate.setHours(23, 59, 59, 999);
    return new DateTime(newDate);
  };
});

// Usage
const date =_tx('2025-02-15');
console.log(date.quarter()); // 1 (Q1)
const quarterStart = date.startOfQuarter(); // 2025-01-01
const quarterEnd = date.endOfQuarter(); // 2025-03-31

// Example 4: Add relative time formatting (direct method)
function fromNow() {
  const now = _tx();
  const diffMs = now.getTime() - this.getTime();
  const diffSeconds = Math.floor(diffMs / 1000);
  const diffMinutes = Math.floor(diffSeconds / 60);
  const diffHours = Math.floor(diffMinutes / 60);
  const diffDays = Math.floor(diffHours / 24);

  if (Math.abs(diffSeconds) < 60) {
    return diffSeconds >= 0 ? 'just now' : 'in a few seconds';
  } else if (Math.abs(diffMinutes) < 60) {
    const mins = Math.abs(diffMinutes);
    return diffMinutes >= 0 ? `${mins} minute${mins > 1 ? 's' : ''} ago` : `in ${mins} minute${mins > 1 ? 's' : ''}`;
  } else if (Math.abs(diffHours) < 24) {
    const hrs = Math.abs(diffHours);
    return diffHours >= 0 ? `${hrs} hour${hrs > 1 ? 's' : ''} ago` : `in ${hrs} hour${hrs > 1 ? 's' : ''}`;
  } else if (Math.abs(diffDays) < 7) {
    const days = Math.abs(diffDays);
    return diffDays >= 0 ? `${days} day${days > 1 ? 's' : ''} ago` : `in ${days} day${days > 1 ? 's' : ''}`;
  } else {
    return this.format('MMM D, YYYY');
  }
}

_tx.extend(fromNow);

// Usage
const past = _tx().subtractHours(2);
console.log(past.fromNow()); // "2 hours ago"

const future = _tx().addDays(3);
console.log(future.fromNow()); // "in 3 days"

Creating Reusable Plugins

// plugins/custom.js - Single method
export default function myMethod() {
  return this;
}

// plugins/custom.js - Multiple methods (install function)
export default (DateTime) => {
  DateTime.prototype.method1 = function() { return this; };
  DateTime.prototype.method2 = function() { return this; };
};

// In your application
import _tx from '24x7';
import customPlugin from './plugins/custom.js';

_tx.extend(customPlugin);

Plugin Best Practices

import _tx, { DateTime } from '24x7';

// Single method - use named function
function chainableMethod() {
  // Do something
  return this;
}
_tx.extend(chainableMethod);

// Multiple methods - use install function
_tx.extend((DateTime) => {
  // 1. Always return this for chainability (when appropriate)
  DateTime.prototype.chainableMethod2 = function() {
    return this;
  };

  // 2. Create new DateTime instances for immutable operations
  DateTime.prototype.immutableOperation = function() {
    const newDate = new Date(this._date);
    // Modify newDate
    return new DateTime(newDate);
  };

  // 3. Validate inputs
  DateTime.prototype.safeMethod = function(input) {
    if (typeof input !== 'number') {
      throw new TypeError('Expected a number');
    }
    // Your logic
    return this;
  };

  // 4. Use descriptive method names
  DateTime.prototype.calculateBusinessHours = function() {
    // Better than calculateBH()
    return this;
  };
});

Error Handling

24x7 throws explicit errors for invalid operations:

Parsing Errors

import _tx from '24x7';

// Invalid date string
try {
_tx('invalid-date');
} catch (error) {
  console.error(error); // TypeError: Cannot parse date from: invalid-date
  console.error(error.message); // "Cannot parse date from: invalid-date"
}

// Invalid date object
try {
_tx(new Date('invalid'));
} catch (error) {
  console.error(error); // TypeError: Invalid Date object provided
}

// Invalid input type
try {
_tx({ not: 'a date' });
} catch (error) {
  console.error(error); // TypeError: Cannot parse date from type: object
}

Range Errors

import _tx from '24x7';

// Start date after end date
try {
_tx('2025-01-10').range('2025-01-05'); // start > end
} catch (error) {
  console.error(error); // RangeError: Start date must be before or equal to end date
}

// Valid range (no error)
const validRange =_tx('2025-01-05').range('2025-01-10'); // OK
const singleDayRange =_tx('2025-01-05').range('2025-01-05'); // OK (same day)

Business Day Errors

import _tx from '24x7';

// Invalid weekends array
try {
_tx('2025-01-10').addBusinessDays(5, {
    weekends: 'not an array' // Should be array
  });
} catch (error) {
  console.error(error); // TypeError: weekends must be an array
}

// Invalid weekend values
try {
_tx('2025-01-10').addBusinessDays(5, {
    weekends: [0, 7] // 7 is invalid (0-6 only)
  });
} catch (error) {
  console.error(error); // TypeError: weekends must be an array of numbers 0-6
}

// Invalid days parameter
try {
_tx('2025-01-10').addBusinessDays('five'); // Should be number
} catch (error) {
  console.error(error); // TypeError: addBusinessDays() expects a number
}

Comparison Errors

import _tx from '24x7';

// Invalid comparison value
try {
_tx('2025-01-10').isBefore('not-a-date');
} catch (error) {
  console.error(error); // TypeError: Cannot compare with invalid date: not-a-date
}

// Valid comparisons
x('2025-01-10').isBefore('2025-01-15'); // true (no error)
x('2025-01-10').isBefore(new Date('2025-01-15')); // true (no error)

Format Errors

import _tx from '24x7';

// Invalid pattern type
try {
_tx('2025-01-15').format(123); // Should be string
} catch (error) {
  console.error(error); // TypeError: format() expects a string pattern
}

// Valid format
x('2025-01-15').format('YYYY-MM-DD'); // '2025-01-15' (no error)

Manipulation Errors

import _tx from '24x7';

// Invalid add/subtract values
try {
_tx('2025-01-15').add('not a number');
} catch (error) {
  console.error(error); // TypeError: add() expects a number (milliseconds)
}

try {
_tx('2025-01-15').addDays('five');
} catch (error) {
  console.error(error); // TypeError: addDays() expects a number
}

// Valid operations
x('2025-01-15').add(1000); // OK
x('2025-01-15').addDays(5); // OK

Error Handling Best Practices

import _tx from '24x7';

// Pattern 1: Try-catch for user input
function parseUserDate(input) {
  try {
    return _tx(input);
  } catch (error) {
    console.error('Invalid date input:', input);
    return null; // or return a default date
  }
}

// Pattern 2: Validate before operations
function safeAddBusinessDays(date, days, options) {
  if (typeof days !== 'number') {
    throw new TypeError('Days must be a number');
  }
  
  try {
    return _tx(date).addBusinessDays(days, options);
  } catch (error) {
    console.error('Error adding business days:', error);
    throw error; // Re-throw or handle as needed
  }
}

// Pattern 3: Graceful degradation
function getDateOrNow(input) {
  try {
    return _tx(input);
  } catch {
    return _tx(); // Fallback to current time
  }
}

// Pattern 4: Type checking
function isValidDateInput(input) {
  try {
  _tx(input);
    return true;
  } catch {
    return false;
  }
}

// Usage
if (isValidDateInput(userInput)) {
  const date =_tx(userInput);
  // Process date
} else {
  // Show error to user
  console.error('Please enter a valid date');
}

Browser and Node.js Support

  • ES2020+ required
  • Node.js: 14+ (or any environment with ES modules)
  • Browsers: Modern browsers with ES module support

Design Principles

  1. Immutability: All operations return new instances
  2. Explicit APIs: No magic, no surprises
  3. Predictable behavior: DST changes don't silently corrupt data
  4. Small core: Extensible via plugins
  5. No dependencies: Self-contained
  6. Tree-shakable: Import only what you need

License

See LICENSE file for details.

Contributing

This is a production-ready library. Contributions should maintain:

  • Immutability guarantees
  • Explicit error handling
  • Comprehensive documentation
  • Test coverage

Non-Goals

24x7 explicitly does not provide:

  • Moment.js compatibility layer
  • Large locale databases
  • Full timezone engine
  • Implicit magic behavior

These are intentional design decisions to keep the library focused and maintainable.