Immutable date and time handling for JavaScript with locale-aware formatting, parsing, calendar math, and IANA or fixed-offset time zones. FrostDateTime works in Node and bundlers, and also ships a browser-friendly UMD bundle that exposes globalThis.DateTime.
- Default ESM
DateTimeexport for Node and bundlers - Browser UMD bundle in
dist/exposed asglobalThis.DateTime - No runtime dependencies
- Immutable operations across getters, setters, and date math
- Locale-aware formatting, parsing, relative time, and week rules through
Intl - IANA time zones such as
Australia/Brisbaneand fixed offsets such as+10:00 - JSDoc-powered IntelliSense
npm i @fr0st/datetimeFrostDateTime is ESM-only. Import the default DateTime export in Node and bundlers.
import DateTime from '@fr0st/datetime';Load the bundle from your own copy or a CDN:
<script src="/path/to/dist/frost-datetime.min.js"></script>
<!-- or -->
<script src="https://cdn.jsdelivr.net/npm/@fr0st/datetime@latest/dist/frost-datetime.min.js"></script>
<script>
const date = DateTime.now({ timeZone: 'UTC' });
console.log(date.toISOString());
</script>import DateTime from '@fr0st/datetime';
const meeting = DateTime.fromFormat(
'yyyy-MM-dd HH:mm:ss',
'2026-03-23 09:30:00',
{ timeZone: 'Australia/Brisbane' },
);
const nextWeek = meeting.addWeeks(1);
nextWeek.toString();
// Mon Mar 30 2026 09:30:00 +1000 (Australia/Brisbane)
nextWeek.toISOString();
// 2026-03-29T23:30:00.000+00:00
nextWeek.monthName();
// MarchTypeScript note: FrostDateTime is written in JavaScript and uses JSDoc types, which most editors surface as IntelliSense.
FrostDateTime revolves around an immutable DateTime class and a small set of predictable parsing and formatting rules.
- Every setter and manipulation method returns a new instance
- Constructor numbers are milliseconds since the UNIX epoch
fromTimestamp()andwithTimestamp()use seconds since the UNIX epoch- Strings without a zone designator are interpreted in the requested or default time zone
- Week calculations such as
getWeek(),getWeekYear(),withWeekYear(), andweeksInYear()use the active locale's week rules
const a = DateTime.fromArray([2026, 3, 23]);
const b = a.addDays(1);
a.toISOString(); // 2026-03-23T00:00:00.000+00:00
b.toISOString(); // 2026-03-24T00:00:00.000+00:00
new DateTime('January 1, 2019 00:00:00', { timeZone: 'Australia/Brisbane' })
.toISOString();
// 2018-12-31T14:00:00.000+00:00FrostDateTime exports a default DateTime class from @fr0st/datetime.
All creation methods accept an optional options object:
{
timeZone?: string;
locale?: string;
}new DateTime(date?, options?): create from now, milliseconds, or a string accepted byDate.parse()DateTime.fromArray(dateArray, options?): create from[year, month, date, hours, minutes, seconds, milliseconds]DateTime.fromDate(date, options?): wrap a nativeDateDateTime.fromFormat(formatString, dateString, options?): parse a string with a known token patternDateTime.fromISOString(dateString, options?): parseyyyy-MM-dd'T'HH:mm:ss.SSSxxxDateTime.fromTimestamp(timestamp, options?): create from seconds since the UNIX epochDateTime.now(options?): create the current time
const now = new DateTime();
const fromMillis = new DateTime(1711152000000);
const fromArray = DateTime.fromArray([2026, 3, 23, 9, 30], {
timeZone: 'Europe/London',
});
const fromFormat = DateTime.fromFormat(
'dd/MM/yyyy HH:mm:ss',
'23/03/2026 09:30:00',
{ timeZone: 'Australia/Brisbane' },
);fromFormat() and fromISOString() can return an invalid DateTime when the text parses structurally but the calendar values are impossible.
const invalid = DateTime.fromFormat('yyyy-MM-dd', '2019-02-31');
invalid.isValid; // falsefromFormat() does not support narrow month parsing tokens MMMMM and LLLLL; those tokens are output-only.
Format tokens are documented in Formats.md.
format(formatString): format with FrostDateTime's token settoString():eee MMM dd yyyy HH:mm:ss xx (VV)toDateString():eee MMM dd yyyytoTimeString():HH:mm:ss xx (VV)toISOString():yyyy-MM-dd'T'HH:mm:ss.SSSxxxin English and UTCtoJSON(): same UTC ISO string for valid dates,nullfor invalid datestoUTCString():toString()shape in English and UTC
const date = DateTime.fromArray([2026, 3, 23, 9, 30, 15], {
timeZone: 'Australia/Brisbane',
});
date.format('eee MMM dd yyyy HH:mm:ss xxx (VV)');
// Mon Mar 23 2026 09:30:15 +10:00 (Australia/Brisbane)Supported format tokens are listed in Formats.md.
Relevant instance methods:
getLocale()withLocale(locale)getTimeZone()getTimeZoneOffset()withTimeZone(timeZone)withTimeZoneOffset(offsetMinutes)dayName(type?)dayPeriod(type?)monthName(type?)era(type?)timeZoneName(type?)
Accepted time-zone formats:
- IANA names such as
UTC,Europe/London, andAmerica/New_York - Numeric offsets such as
+10:00,+1000, and-05:30 - GMT offsets such as
GMT+10:00
const brisbane = DateTime.fromArray([2026, 3, 23, 9, 30], {
timeZone: 'Australia/Brisbane',
});
brisbane.withTimeZone('UTC').toString();
// Sun Mar 22 2026 23:30:00 +0000 (UTC)
DateTime.fromArray([2026, 3, 23], { locale: 'ar-eg' }).toDateString();| Value | Getter | With |
|---|---|---|
| day of month | getDate() |
withDate(date) |
day of week (0-6, Sunday-based) |
getDay() |
withDay(day) |
| day of year | getDayOfYear() |
withDayOfYear(dayOfYear) |
month (1-12) |
getMonth() |
withMonth(month, date?) |
quarter (1-4) |
getQuarter() |
withQuarter(quarter) |
| year | getYear() |
withYear(year, month?, date?) |
| Value | Getter | With |
|---|---|---|
| locale-aware week of year | getWeek() |
withWeek(week, day?) |
locale-aware day of week (1-7) |
getWeekDay() |
withWeekDay(day) |
| week day in month | getWeekDayInMonth() |
withWeekDayInMonth(week) |
| week of month | getWeekOfMonth() |
withWeekOfMonth(week) |
| locale-aware week year | getWeekYear() |
withWeekYear(year, week?, day?) |
| Value | Getter | With |
|---|---|---|
| hour | getHours() |
withHours(hours, minutes?, seconds?, milliseconds?) |
| minute | getMinutes() |
withMinutes(minutes, seconds?, milliseconds?) |
| second | getSeconds() |
withSeconds(seconds, milliseconds?) |
| millisecond | getMilliseconds() |
withMilliseconds(milliseconds) |
| seconds since UNIX epoch | getTimestamp() |
withTimestamp(timestamp) |
| milliseconds since UNIX epoch | getTime() |
withTime(time) |
| Add | Subtract |
|---|---|
addDay() / addDays(amount) |
subDay() / subDays(amount) |
addWeek() / addWeeks(amount) |
subWeek() / subWeeks(amount) |
addMonth() / addMonths(amount) |
subMonth() / subMonths(amount) |
addYear() / addYears(amount) |
subYear() / subYears(amount) |
addHour() / addHours(amount) |
subHour() / subHours(amount) |
addMinute() / addMinutes(amount) |
subMinute() / subMinutes(amount) |
addSecond() / addSeconds(amount) |
subSecond() / subSeconds(amount) |
| Start | End |
|---|---|
startOfDay() |
endOfDay() |
startOfWeek() |
endOfWeek() |
startOfMonth() |
endOfMonth() |
startOfQuarter() |
endOfQuarter() |
startOfYear() |
endOfYear() |
startOfHour() |
endOfHour() |
startOfMinute() |
endOfMinute() |
startOfSecond() |
endOfSecond() |
diff(other): millisecondsdiffInDays(other, options?)diffInWeeks(other, options?)diffInMonths(other, options?)diffInYears(other, options?)diffInHours(other, options?)diffInMinutes(other, options?)diffInSeconds(other, options?)
options.relative defaults to true for unit-based differences.
const a = DateTime.fromArray([2026, 3, 23]);
const b = DateTime.fromArray([2026, 3, 30]);
a.diffInDays(b); // -7humanDiff(other)humanDiffInDays(other)humanDiffInWeeks(other)humanDiffInMonths(other)humanDiffInYears(other)humanDiffInHours(other)humanDiffInMinutes(other)humanDiffInSeconds(other)
DateTime.fromArray([2026, 3, 30]).humanDiff(DateTime.fromArray([2026, 3, 23]));
// "in 7 days"Base comparisons:
isAfter(other)isBefore(other)isBetween(start, end)isSame(other)isSameOrAfter(other)isSameOrBefore(other)
Scoped comparisons exist for these units:
DayWeekMonthYearHourMinuteSecond
Examples:
isAfterDay(other)isBetweenMonth(start, end)isSameWeek(other)isSameOrBeforeYear(other)
daysInMonth()daysInYear()weeksInYear()isLeapYear()isDst()
DateTime.dayOfYear(year, month, date)DateTime.daysInMonth(year, month)DateTime.daysInYear(year)DateTime.isLeapYear(year)
These affect new instances when you do not pass explicit options:
DateTime.getDefaultLocale()DateTime.setDefaultLocale(locale)DateTime.getDefaultTimeZone()DateTime.setDefaultTimeZone(timeZone)DateTime.setDateClamping(enabled)DateTime.clearDataCache()
DateTime.setDateClamping(true);
DateTime.clearDataCache();- Constructor-based parsing throws on invalid strings or unsupported time zones.
fromFormat()rejects trailing characters and marks impossible parsed dates asisValid === false.fromISOString()parses the RFC 3339 / ISO-style shape used bytoISOString().toISOString()always returns a UTC string regardless of the instance time zone.toJSON()returns the same value astoISOString()for valid dates andnullfor invalid dates.withTimeZone()keeps the same instant and changes representation.withTimeZoneOffset()returns a fixed-offset view of the same instant.- Date clamping controls whether month and year changes clamp invalid dates.
DateTime.clearDataCache()clears cached formatter and locale data, which is mainly useful in tests and long-lived processes.
npm test
npm run js-lint
npm run buildFrostDateTime is released under the MIT License.