Parse time, naturally
Human-friendly time expressions → JavaScript Dates
A few examples of what you can parse. See the docs below for details.
Dates
today, tomorrow, yesterday
next monday at 3pm
march 15th, the 20th
day after tomorrow
third Thursday of November
last day of march
Durations
2 days, 3 weeks, 1 month
1d, 2w, 3mo, 1y, 2h, 30m
half a day, 1.5 weeks
1 week and 2 days
two and a half hours
a couple of days
Spans
jan 5 to jan 20
last 30 days, next 2 weeks
july 3rd for 10 days
this week, this month, ytd
jan through mar
monday through friday
Fuzzy Periods
Q1, Q1 2025, first quarter
early january, mid Q1
H1 2025, H2 2025
spring, summer, winter
beginning of month
end of year, start of week
Business
next business day
in 5 business days
EOD, COB, EOD Friday
close of business Monday
end of day tomorrow
2 business days ago
Natural Time
half past 4, quarter to 5
noon on Friday, midnight
in a fortnight
week 12, the week of March 15
10 to noon, 5 past 3pm
tomorrow half past 9
npm install @timelang/parseUsage
Use parse() for automatic type detection, or one of the specific functions if you know what to expect.
import { parse } from '@timelang/parse'// Datesparse('next friday')parse('tomorrow at 3pm')parse('march 15th 2025')// → { type: 'date', date: Date, title: null }// Durationsparse('2 weeks')parse('1h 30m')// → { type: 'duration', duration: 1209600000, title: null }// Time spansparse('jan 5 to jan 20')parse('last 30 days')parse('next monday for 2 weeks')// → { type: 'span', start: Date, end: Date, duration: number, title: null }// With titlesparse('Team offsite - March 10 to March 14')parse('Sprint 1: jan 5 to jan 19')// → { type: 'span', ..., title: 'Team offsite' }// Fuzzy periodsparse('Q1 2025')parse('early january')parse('mid Q1')// → { type: 'fuzzy', start: Date, end: Date, approximate: true }
Helpers
import { parseDate, parseDuration, parseSpan, scan } from '@timelang/parse'// Single datesparseDate('tomorrow') // DateparseDate('next friday at 3pm') // DateparseDate('in 2 hours') // Date// Durations in millisecondsparseDuration('2 weeks') // 1209600000parseDuration('2h 30m') // 9000000// Time spansparseSpan('jan 5 to jan 20') // { start, end, duration }parseSpan('last 30 days') // { start, end, duration }parseSpan('this week') // { start, end, duration }// Scan for dates in prose textscan('can we meet tomorrow at 5pm?')// → [{ result: { type: 'date', ... }, match: 'tomorrow at 5pm', start: 12, end: 27 }]
Titles
Automatically extracts titles from expressions using common separators.
parse('Team offsite - March 10 to March 14')// → { type: 'span', title: 'Team offsite', ... }parse('Sprint 1: jan 5 to jan 19')// → { type: 'span', title: 'Sprint 1', ... }parse('Meeting with Team (Jan 15 at 2pm)')// → { type: 'date', title: 'Meeting with Team', ... }parse('Project Kickoff [March 1st]')// → { type: 'date', title: 'Project Kickoff', ... }
Options
parse('Q1', {referenceDate: new Date('2025-06-01'),fiscalYearStart: 'april', // 'january' | 'april' | 'july' | 'october'weekStartsOn: 'monday', // 'sunday' | 'monday'dateFormat: 'intl' // 'us' | 'intl' | 'auto'})
Types
type ParseResult = DateResult | DurationResult | SpanResult | FuzzyResult | nullinterface DateResult {type: 'date'date: Datetitle: string | null}interface DurationResult {type: 'duration'duration: numbertitle: string | null}interface SpanResult {type: 'span'start: Dateend: Dateduration: numbertitle: string | null}interface FuzzyResult {type: 'fuzzy'start: Dateend: Dateapproximate: truetitle: string | null}interface ScanMatch {result: ParseResultmatch: stringstart: numberend: number}
Playground
Test each function individually.
parse()— returns discriminated unionparseDate()— returns Date | nullparseDuration()— returns ms | nullparseSpan()— returns { start, end, duration } | nullscan()— returns array of matches with positions