Skip to main content

Overview

The Custom Parse Format plugin adds the ability to parse date strings using custom format patterns. This is similar to Day.js’s customParseFormat plugin and supports a wide variety of format tokens.

Installation

Load the plugin using either extend or lazyLoad:
import atemporal from '@atemporal/core';
import customParseFormatPlugin from '@atemporal/core/plugins/customParseFormat';

// Option 1: Load immediately
atemporal.extend(customParseFormatPlugin);

// Option 2: Lazy load on first use
atemporal.lazyLoad(customParseFormatPlugin);

Methods

atemporal.fromFormat(dateString, formatString, timeZone?)

Creates an Atemporal instance from a string and a specific format. Signature:
atemporal.fromFormat(
  dateString: string,
  formatString: string,
  timeZone?: string
): TemporalWrapper
Parameters:
  • dateString: The date string to parse
  • formatString: The format that the date string follows (e.g., ‘YYYY-MM-DD’)
  • timeZone (optional): The timezone to apply. If not provided, uses the default
Returns: A new TemporalWrapper instance Examples:
atemporal.fromFormat('12-25-2023', 'MM-DD-YYYY');

atemporal.fromFormat('2023/01/15 14:30', 'YYYY/MM/DD HH:mm');

atemporal.fromFormat('15.03.2024', 'DD.MM.YYYY', 'Europe/Berlin');

Format Tokens

The plugin supports a comprehensive set of format tokens:

Year

TokenDescriptionExample
YYYY4-digit year2023
YY2-digit year23 (interpreted as 2023)
2-digit year rules:
  • Years 00-682000-2068
  • Years 69-991969-1999

Month

TokenDescriptionExample
MMMonth with leading zero01, 12
MMonth without leading zero1, 12
MMMMFull month nameJanuary, December
MMMAbbreviated month nameJan, Dec

Day

TokenDescriptionExample
DDDay with leading zero01, 31
DDay without leading zero1, 31
DDDDay of year (3 digits)001, 365
DDDDDay of year (1-3 digits)1, 365

Hour

TokenDescriptionExample
HHHour (24-hour, leading zero)00, 23
HHour (24-hour, no leading zero)0, 23
hhHour (12-hour, leading zero)01, 12
hHour (12-hour, no leading zero)1, 12

Minute

TokenDescriptionExample
mmMinute with leading zero00, 59
mMinute without leading zero0, 59

Second

TokenDescriptionExample
ssSecond with leading zero00, 59
sSecond without leading zero0, 59

Millisecond

TokenDescriptionExample
SSSMilliseconds (3 digits)000, 999
SSCentiseconds (2 digits)00, 99
SDeciseconds (1 digit)0, 9

AM/PM

TokenDescriptionExample
AAM/PM uppercaseAM, PM
aam/pm lowercaseam, pm
Note: When using 12-hour format (h or hh), an AM/PM token is required.

Validation

The plugin includes comprehensive validation:
  • Range validation: Ensures values are within valid ranges
  • Date validation: Uses Temporal API to validate dates (e.g., rejects February 30th)
  • Leap year support: Correctly handles leap years
  • Month-specific days: Validates day counts for each month
  • 12-hour format: Requires AM/PM marker when using h or hh

Strict Mode

The parsing is strict by default. The entire string must match the format pattern exactly:
// This works
atemporal.fromFormat('2023-12-25', 'YYYY-MM-DD');

// This returns Invalid Date (extra characters)
atemporal.fromFormat('2023-12-25 extra', 'YYYY-MM-DD');

Flexible Whitespace

The parser allows flexible whitespace matching. Single spaces in the format string will match any amount of whitespace:
atemporal.fromFormat('2023-12-25  14:30', 'YYYY-MM-DD HH:mm');
// Matches despite double space

Literal Text

You can include literal text in format strings using square brackets:
atemporal.fromFormat('Year: 2023', '[Year:] YYYY');

atemporal.fromFormat('Date 2023-12-25', '[Date] YYYY-MM-DD');

Examples

Common Date Formats

// ISO format
atemporal.fromFormat('2023-12-25', 'YYYY-MM-DD');

// US format
atemporal.fromFormat('12/25/2023', 'MM/DD/YYYY');

// European format
atemporal.fromFormat('25.12.2023', 'DD.MM.YYYY');

// With full month name
atemporal.fromFormat('December 25, 2023', 'MMMM DD, YYYY');

Time Formats

// 24-hour format
atemporal.fromFormat('14:30:00', 'HH:mm:ss');

// 12-hour format with AM/PM
atemporal.fromFormat('2:30 PM', 'h:mm A');
atemporal.fromFormat('02:30:45 pm', 'hh:mm:ss a');

// With milliseconds
atemporal.fromFormat('14:30:00.500', 'HH:mm:ss.SSS');

Combined Date and Time

atemporal.fromFormat('2023-12-25 14:30', 'YYYY-MM-DD HH:mm');

atemporal.fromFormat('12/25/2023 2:30 PM', 'MM/DD/YYYY h:mm A');

atemporal.fromFormat('25.12.2023 14:30:00', 'DD.MM.YYYY HH:mm:ss');

Day of Year

// Parse 100th day of 2023
atemporal.fromFormat('2023-100', 'YYYY-DDD');
// Results in April 10, 2023

atemporal.fromFormat('2024-366', 'YYYY-DDD');
// Results in December 31, 2024 (leap year)

With Timezone

atemporal.fromFormat(
  '2023-12-25 14:30',
  'YYYY-MM-DD HH:mm',
  'America/New_York'
);

atemporal.fromFormat(
  '25/12/2023 9:00 AM',
  'DD/MM/YYYY h:mm A',
  'Europe/London'
);

Error Handling

// Returns Invalid Date instance
const invalid = atemporal.fromFormat('invalid-date', 'YYYY-MM-DD');
invalid.isValid();  // false

// Invalid day for month
const feb30 = atemporal.fromFormat('2023-02-30', 'YYYY-MM-DD');
feb30.isValid();  // false

// Missing AM/PM with 12-hour format
const noAmPm = atemporal.fromFormat('2:30', 'h:mm');
noAmPm.isValid();  // false

Performance

The plugin includes an LRU cache (max 100 entries) that stores compiled regular expressions for format strings. This significantly improves performance when parsing multiple dates with the same format.

Cache Management

// Clear the format cache
atemporal.clearFormatCache();

// Get cache size
const size = atemporal.getFormatCacheSize();

// Get cache statistics
const stats = atemporal.getFormatCacheStats();

Best Practices

  1. Reuse format strings: The cache makes repeated parsing with the same format very fast
  2. Use strict formats: Always match the exact format of your input strings
  3. Validate results: Check .isValid() after parsing untrusted input
  4. Specify timezones: Always provide a timezone when parsing times from specific locations
  5. Prefer ISO format: When possible, use ISO 8601 format strings for best compatibility

Build docs developers (and LLMs) love