CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-date-fns

Modern JavaScript date utility library providing 200+ modular functions for date manipulation, formatting, parsing, and calculations

Pending

Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

SecuritybySnyk

Pending

The risk profile of this skill

Overview
Eval results
Files

fp.mddocs/

Functional Programming

The date-fns functional programming (FP) module provides curried versions of all date functions, enabling functional composition and pipeline-style operations. All FP functions automatically reorder parameters for optimal currying and include full TypeScript support.

FP Module Overview

Import Structure

// Import individual curried functions
import { add, format, isAfter } from "date-fns/fp";

// Import with namespace
import * as fp from "date-fns/fp";

Currying Pattern

All FP functions follow a consistent currying pattern where the data (typically the date) comes last, enabling partial application and composition:

// Regular date-fns: data first
format(date, 'yyyy-MM-dd')

// FP version: data last  
format('yyyy-MM-dd')(date)

Core FP Functions

Arithmetic Functions

const add: CurriedFn2<Duration, DateArg<Date>, Date>;
const addDays: CurriedFn2<number, DateArg<Date>, Date>;
const addHours: CurriedFn2<number, DateArg<Date>, Date>;
const addMinutes: CurriedFn2<number, DateArg<Date>, Date>;
const addMonths: CurriedFn2<number, DateArg<Date>, Date>;
const addYears: CurriedFn2<number, DateArg<Date>, Date>;
const sub: CurriedFn2<Duration, DateArg<Date>, Date>;
const subDays: CurriedFn2<number, DateArg<Date>, Date>;
const subHours: CurriedFn2<number, DateArg<Date>, Date>;
const subMinutes: CurriedFn2<number, DateArg<Date>, Date>;
const subMonths: CurriedFn2<number, DateArg<Date>, Date>;
const subYears: CurriedFn2<number, DateArg<Date>, Date>;

Examples:

import { add, addDays, subMonths } from "date-fns/fp";

// Create reusable functions
const addOneWeek = add({ weeks: 1 });
const addFiveDays = addDays(5);
const subtractTwoMonths = subMonths(2);

// Apply to dates
const dates = [
  new Date(2014, 0, 1),
  new Date(2014, 0, 15),
  new Date(2014, 0, 30)
];

const datesPlus5 = dates.map(addFiveDays);
const weekFromNow = addOneWeek(new Date());

Formatting Functions

const format: CurriedFn2<string, DateArg<Date>, string>;
const formatDistance: CurriedFn2<DateArg<Date>, DateArg<Date>, string>;
const formatDistanceToNow: CurriedFn1<DateArg<Date>, string>;
const formatISO: CurriedFn1<DateArg<Date>, string>;
const formatRelative: CurriedFn2<DateArg<Date>, DateArg<Date>, string>;
const lightFormat: CurriedFn2<string, DateArg<Date>, string>;

Examples:

import { format, formatDistance, formatISO } from "date-fns/fp";

// Create format functions
const formatYMD = format('yyyy-MM-dd');
const formatLong = format('EEEE, MMMM do, yyyy');
const toISO = formatISO;

// Use in pipelines
const dates = [new Date(2014, 0, 1), new Date(2014, 5, 15)];
const formatted = dates.map(formatYMD);
//=> ['2014-01-01', '2014-06-15']

// Distance from specific date
const distanceFromNewYear = formatDistance(new Date(2014, 0, 1));
distanceFromNewYear(new Date(2014, 5, 15));
//=> '5 months'

Comparison Functions

const compareAsc: CurriedFn2<DateArg<Date>, DateArg<Date>, number>;
const compareDesc: CurriedFn2<DateArg<Date>, DateArg<Date>, number>;
const isAfter: CurriedFn2<DateArg<Date>, DateArg<Date>, boolean>;
const isBefore: CurriedFn2<DateArg<Date>, DateArg<Date>, boolean>;
const isEqual: CurriedFn2<DateArg<Date>, DateArg<Date>, boolean>;
const max: CurriedFn1<DateArg<Date>[], Date>;
const min: CurriedFn1<DateArg<Date>[], Date>;

Examples:

import { isAfter, isBefore, compareAsc } from "date-fns/fp";

// Create comparison functions
const isAfter2020 = isAfter(new Date(2020, 0, 1));
const isBefore2025 = isBefore(new Date(2025, 0, 1));

// Filter dates
const dates = [
  new Date(2019, 0, 1),
  new Date(2021, 0, 1), 
  new Date(2026, 0, 1)
];

const recentDates = dates.filter(isAfter2020).filter(isBefore2025);
//=> [new Date(2021, 0, 1)]

// Sort dates
const sortedDates = dates.sort(compareAsc);

Validation Functions

const isValid: CurriedFn1<any, boolean>;
const isDate: CurriedFn1<any, boolean>;
const isFuture: CurriedFn1<DateArg<Date>, boolean>;
const isPast: CurriedFn1<DateArg<Date>, boolean>;
const isToday: CurriedFn1<DateArg<Date>, boolean>;
const isWeekend: CurriedFn1<DateArg<Date>, boolean>;
const isMonday: CurriedFn1<DateArg<Date>, boolean>;
const isTuesday: CurriedFn1<DateArg<Date>, boolean>;
const isWednesday: CurriedFn1<DateArg<Date>, boolean>;
const isThursday: CurriedFn1<DateArg<Date>, boolean>;
const isFriday: CurriedFn1<DateArg<Date>, boolean>;
const isSaturday: CurriedFn1<DateArg<Date>, boolean>;
const isSunday: CurriedFn1<DateArg<Date>, boolean>;

Examples:

import { isValid, isWeekend, isFuture } from "date-fns/fp";

// Filter valid dates
const mixedInput = [
  new Date(2014, 0, 1),
  'invalid-date',
  new Date(2014, 0, 2),
  null
];

const validDates = mixedInput.filter(isValid);
//=> [new Date(2014, 0, 1), new Date(2014, 0, 2)]

// Chain validations
const dates = [/* array of dates */];
const futureWeekends = dates.filter(isFuture).filter(isWeekend);

Same Period Functions

const isSameDay: CurriedFn2<DateArg<Date>, DateArg<Date>, boolean>;
const isSameWeek: CurriedFn2<DateArg<Date>, DateArg<Date>, boolean>;
const isSameMonth: CurriedFn2<DateArg<Date>, DateArg<Date>, boolean>;
const isSameYear: CurriedFn2<DateArg<Date>, DateArg<Date>, boolean>;
const isSameHour: CurriedFn2<DateArg<Date>, DateArg<Date>, boolean>;
const isSameMinute: CurriedFn2<DateArg<Date>, DateArg<Date>, boolean>;
const isSameSecond: CurriedFn2<DateArg<Date>, DateArg<Date>, boolean>;

Examples:

import { isSameDay, isSameMonth } from "date-fns/fp";

// Find dates matching a reference
const referenceDate = new Date(2014, 0, 15);
const dates = [/* array of dates */];

const sameDayDates = dates.filter(isSameDay(referenceDate));
const sameMonthDates = dates.filter(isSameMonth(referenceDate));

Functional Composition

Function Composition with Ramda

import { pipe, compose } from "ramda";
import { format, addDays, startOfDay } from "date-fns/fp";

// Create a pipeline
const formatTomorrowStart = pipe(
  addDays(1),
  startOfDay,
  format('yyyy-MM-dd HH:mm:ss')
);

formatTomorrowStart(new Date(2014, 0, 1, 14, 30));
//=> '2014-01-02 00:00:00'

// Compose operations (right to left)
const formatNextWeekEnd = compose(
  format('PP'),
  endOfDay,
  addDays(7)
);

Custom Composition Helpers

import { addDays, format, startOfWeek, endOfWeek } from "date-fns/fp";

// Create utility functions
const pipe = <T>(...fns: Array<(arg: T) => T>) => (value: T) => 
  fns.reduce((acc, fn) => fn(acc), value);

const formatWeekRange = (date: Date) => {
  const start = startOfWeek(date);
  const end = endOfWeek(date);
  const formatDate = format('MMM dd');
  return `${formatDate(start)} - ${formatDate(end)}`;
};

// Pipeline for date processing
const processDate = pipe(
  startOfDay,
  addDays(1),
  format('yyyy-MM-dd')
);

Advanced FP Patterns

Partial Application

import { formatDistance, isWithinInterval } from "date-fns/fp";

// Create partially applied functions
const distanceFromToday = formatDistance(new Date());
const isInCurrentYear = isWithinInterval({
  start: startOfYear(new Date()),
  end: endOfYear(new Date())
});

// Use with arrays
const dates = [/* dates */];
const distances = dates.map(distanceFromToday);
const currentYearDates = dates.filter(isInCurrentYear);

Function Factories

import { add, format, isBefore } from "date-fns/fp";

// Factory for creating date ranges
const createDateRange = (duration: Duration) => (start: Date): Date[] => {
  const end = add(duration)(start);
  return eachDayOfInterval({ start, end });
};

// Factory for deadline checkers
const createDeadlineChecker = (deadline: Date) => (date: Date): {
  passed: boolean;
  remaining: string;
} => ({
  passed: isAfter(deadline)(date),
  remaining: formatDistance(deadline)(date)
});

// Usage
const getWeekRange = createDateRange({ weeks: 1 });
const checkProjectDeadline = createDeadlineChecker(new Date(2024, 11, 31));

const weekDates = getWeekRange(new Date());
const status = checkProjectDeadline(new Date());

Data Transformation Pipelines

import { flow } from "lodash/fp";
import { parse, format, addBusinessDays, isValid } from "date-fns/fp";

// Process CSV date data
const processDateString = flow(
  parse('yyyy-MM-dd', new Date()), // Parse with reference date
  date => isValid(date) ? date : null, // Validate
  date => date ? addBusinessDays(5)(date) : null, // Add business days if valid
  date => date ? format('MM/dd/yyyy')(date) : 'Invalid Date' // Format output
);

const csvDates = ['2014-01-01', '2014-02-15', 'invalid-date'];
const processed = csvDates.map(processDateString);
//=> ['01/08/2014', '02/24/2014', 'Invalid Date']

Option Handling in FP

Functions with Options

const formatWithOptions: CurriedFn3<FormatOptions, string, DateArg<Date>, string>;
const parseWithOptions: CurriedFn4<ParseOptions, DateArg<Date>, string, string, Date>;
const startOfWeekWithOptions: CurriedFn2<WeekStartOptions, DateArg<Date>, Date>;

Examples:

import { formatWithOptions, startOfWeekWithOptions } from "date-fns/fp";
import { de } from "date-fns/locale";

// Create localized formatters
const formatGerman = formatWithOptions({ locale: de });
const formatGermanDate = formatGerman('EEEE, do MMMM yyyy');

formatGermanDate(new Date(2014, 0, 1));
//=> 'Mittwoch, 1. Januar 2014'

// Week starting on Monday
const startOfWeekMonday = startOfWeekWithOptions({ weekStartsOn: 1 });
const dates = [/* dates */];
const weekStarts = dates.map(startOfWeekMonday);

Performance Considerations

Memoization

import memoize from "lodash/memoize";
import { format, formatDistance } from "date-fns/fp";

// Memoize expensive operations
const memoizedFormat = memoize(format);
const formatISO = memoizedFormat('yyyy-MM-dd');

// Memoize with custom key
const memoizedDistance = memoize(
  formatDistance,
  (referenceDate) => referenceDate.getTime()
);

const distanceFromEpoch = memoizedDistance(new Date(0));

Lazy Evaluation

import { addDays, format, isFuture } from "date-fns/fp";

// Create lazy sequences
function* dateSequence(start: Date, step: number = 1) {
  let current = start;
  while (true) {
    yield current;
    current = addDays(step)(current);
  }
}

// Use with functional operations
const futureDates = Array.from(dateSequence(new Date()))
  .slice(0, 100) // Take first 100
  .filter(isFuture)
  .map(format('yyyy-MM-dd'))
  .slice(0, 10); // Take first 10 future dates

Type Definitions

Curried Function Types

interface CurriedFn1<A, R> {
  (a: A): R;
}

interface CurriedFn2<A, B, R> {
  (a: A): CurriedFn1<B, R>;
  (a: A, b: B): R;
}

interface CurriedFn3<A, B, C, R> {
  (a: A): CurriedFn2<B, C, R>;
  (a: A, b: B): CurriedFn1<C, R>;
  (a: A, b: B, c: C): R;
}

interface CurriedFn4<A, B, C, D, R> {
  (a: A): CurriedFn3<B, C, D, R>;
  (a: A, b: B): CurriedFn2<C, D, R>;
  (a: A, b: B, c: C): CurriedFn1<D, R>;
  (a: A, b: B, c: C, d: D): R;
}

Integration Examples

React Hooks

import { useMemo } from "react";
import { format, addDays, isToday } from "date-fns/fp";

function useDateFormatter(pattern: string) {
  return useMemo(() => format(pattern), [pattern]);
}

function useRelativeDates(dates: Date[]) {
  return useMemo(() => ({
    today: dates.filter(isToday),
    tomorrow: dates.filter(date => isSameDay(addDays(1)(new Date()))(date)),
    formatted: dates.map(format('MMM dd, yyyy'))
  }), [dates]);
}

Redux Selectors

import { createSelector } from "reselect";
import { isAfter, format, isSameMonth } from "date-fns/fp";

const getEvents = (state: State) => state.events;
const getCurrentMonth = () => new Date();

const getUpcomingEvents = createSelector(
  getEvents,
  events => events
    .filter(event => isAfter(new Date())(event.date))
    .sort(compareAsc)
);

const getCurrentMonthEvents = createSelector(
  getEvents,
  getCurrentMonth,
  (events, currentMonth) => events.filter(
    event => isSameMonth(currentMonth)(event.date)
  )
);

Validation Schemas

import { z } from "zod";
import { isValid, isAfter, isBefore } from "date-fns/fp";

const dateSchema = z.string()
  .transform(str => new Date(str))
  .refine(isValid, "Invalid date")
  .refine(isAfter(new Date(1900, 0, 1)), "Date must be after 1900")
  .refine(isBefore(new Date(2100, 0, 1)), "Date must be before 2100");

const eventSchema = z.object({
  title: z.string(),
  startDate: dateSchema,
  endDate: dateSchema
}).refine(
  data => isBefore(data.endDate)(data.startDate),
  "End date must be after start date"
);

docs

arithmetic.md

components.md

constants.md

formatting.md

fp.md

i18n.md

index.md

parsing.md

periods.md

validation.md

tile.json