Editor’s note: This article was last reviewed and updated by Jude Miracle in January 2025 to include the Temporal API as a popular alternative to Moment.js, as well as a newcomer to the internationalization libraries space: little-date.
Formatting dates is a crucial step in preparing applications for use in multiple languages and regions. Moment.js has been among the most popular options of JavaScript libraries for date formatting and manipulation. However, in some cases, its size and the way the library is structured have prompted developers to look for alternatives to Moment.js.
In this article, I’m going to review five alternatives to Moment.js regarding date internationalization:
I’ll focus on converting dates to strings in different formats for different locales, including relative time.
The Replay is a weekly newsletter for dev and engineering leaders.
Delivered once a week, it's your curated guide to the most important conversations around frontend dev, emerging AI tools, and the state of modern software.
Intl is a global object that acts as the namespace of the ECMAScript Internationalization API. Regarding dates, this object provides the following constructors:
Intl.DateTimeFormat: Provides date and time formattingIntl.RelativeTimeFormat: Provides language-sensitive easy-to-read phrases for dates and timestampsThese constructors take two optional arguments: the locale and an object with options to customize the output. Here’s an example:
let rtf = new Intl.RelativeTimeFormat('en-GB', { style: 'long' });
let dtf = new Intl.DateTimeFormat('de');
The locale argument is a string that represents a BCP 47 language tag, which is composed of the following parts:
el (modern Greek)Grek (Greek)GR (Greece)polyton (polytonic Greek)u-nu-native (native digits)Here’s an example with all the parts combined:
let rtf = new Intl.RelativeTimeFormat('el-Grek-GR-polyton-u-nu-native');
Only the first part (the language code) is required, and you can pass an array of strings to define fallback languages:
// Requests Dutch as the primary language and if it is not available, it requests french let dtf = new Intl.DateTimeFormat(['nl', 'fr'])
If a locale is not provided, the locale of the runtime environment is used. As for the second argument, the options object, this varies between constructors.
Intl.DateTimeFormat allows you to customize date formatting with options such as the date style (full, long, medium, or short), 12-hour or 24-hour time, and the representation of specific components like the year, month, and weekday. In the documentation page of Intl.DateTimeFormat, you can learn more about all the options available for customizing this object.
When it comes to Intl.RelativeTimeFormat, the options object only has the following properties:
localeMatcher: The locale matching algorithm to use. The possible values are lookup (from the more specific to the less specific, if en-us is not available, en is chosen) and best fit (the default value, if en-us is not available, something like en-uk can be chosen)numeric: To format the output message. The possible values are always (for example, 2 hours ago) or auto, which doesn’t always allow numeric values in the output (for example, yesterday)style: To format the length of the output message. The possible values are long, short, and narrowWith an Intl.DateTimeFormat or Intl.RelativeTimeFormat object, you can use the format() method to format a date or the formatToParts() method to return an array of its formatted components.
In the case of Intl.DateTimeFormat, the methods take the Date object to format:
const date = new Date(Date.UTC(2014, 8, 19, 14, 5, 0));
const options = {
dateStyle: 'short',
timeStyle: 'full',
hour12: true,
day: 'numeric',
month: 'long',
year: '2-digit',
minute: '2-digit',
second: '2-digit',
};
// Sample output: 19 septembre 14 à 05:00
console.log(new Intl.DateTimeFormat("fr", options).format(date));
// Sample output: 19. September 14, 05:00
console.log(new Intl.DateTimeFormat("de-AT", options).format(date));
/* Sample output: [{"type":"day","value":"19"},{"type":"literal","value":" "},{"type":"month","value":"settembre"},{"type":"literal","value":" "},{"type":"year","value":"14"},{"type":"literal","value":", "},{"type":"minute","value":"05"},{"type":"literal","value":":"},{"type":"second","value":"00"}] */
console.log(new Intl.DateTimeFormat("it", options).formatToParts(date));
Notice that if you only specify a few date-time components in the options object, these will be the ones present in the output:
const date = new Date(Date.UTC(2014, 08, 19, 14, 5, 0));
const options = {
year: '2-digit',
};
// Output: 14
console.log(new Intl.DateTimeFormat("en", options).format(date));
In the case of Intl.RelativeTimeFormat, format() takes the numeric value to use in the message and a second argument to indicate the unit of this value (like year or second, in either singular or plural forms):
const options = {
localeMatcher: 'best fit',
numeric: 'auto',
style: 'short',
};
// Output: last mo.
console.log(new Intl.RelativeTimeFormat("en-CA", options).format(-1, 'month'));
// Output: la semana pasada
console.log(new Intl.RelativeTimeFormat("es-ES", options).format(-1, 'week'));
/* Output: [{"type":"integer","value":"60","unit":"minute"},{"type":"literal","value":" 分鐘前"}] */
console.log(new Intl.RelativeTimeFormat("zh-TW", options).formatToParts(-60, 'minutes'));
Also, notice the difference between using the always and auto values for the numeric property:
// Output: in 0 days
console.log(new Intl.RelativeTimeFormat("en", {numeric: 'always'}).format(0, 'day'));
// Output: today
console.log(new Intl.RelativeTimeFormat("en", {numeric: 'auto'}).format(0, 'day'));
You can try and modify all of the above examples here and here, but depending on the browser you’re using, they may result in some errors.
Most of the functionality of Intl.DateTimeFormat is well-supported in modern browsers (more information here). However, Intl.RelativeTimeFormat, which was previously a concern, is now well-supported in all major browsers including Safari and Edge.
You can use a polyfill, but you’ll have to create the object differently:
const myLocale = /* Import JSON file for the choosen locale */;
const localeTag = /* Tag for the above locale */;
const options = { /* Options object */ };
RelativeTimeFormat.addLocale(myLocale);
new RelativeTimeFormat(localeTag, options).format(3, 'day');
You can try this example here.
As you can see, Intl.RelativeTimeFormat is similar to moment.duration().humanize():
moment.duration(-1, 'weeks').humanize(true); // a week ago
If you’re used to calculating relative times from now or calendar times relative to a given reference time the way Moment.js does:
moment('20140919', 'YYYYMMDD').fromNow(); // 5 years ago
moment().add(5, 'days').calendar(); // Tuesday at 1:15 PM
You’ll need to manually calculate the difference between the two dates.
Nothing beats using native features, but if this can become a problem, there are other options.
The Temporal API is a major upgrade to JavaScript’s date and time features. At the time of writing, the Temporal API is currently at Stage 3 in the TC39 process. This API solves many problems with the old Date object and offers a more user-friendly and powerful way to handle dates and times.
The Temporal API introduces several specialized types, each serving a specific purpose in date and time handling:
Plain types: For working with dates and times without timezone information:
Temporal.PlainDate: Represents a calendar dateTemporal.PlainTime: Represents a wall clock timeTemporal.PlainDateTime: Combines date and timeTemporal.PlainYearMonth: Represents a specific month in a specific yearTemporal.PlainMonthDay: Represents a specific day in a specific monthZoned types: For working with dates and times in specific time zones:
Temporal.ZonedDateTime: A complete date, time, and time zoneTemporal.TimeZone: Represents a specific time zoneTemporal.Instant: Represents a specific moment in timeWhile the API is still in the proposal stage, you can start experimenting with it using the polyfill or the @js-temporal/polyfill. This allows you to prepare your codebase for the future while maintaining compatibility with current browsers.
Here’s how these types work together. Because Temporal is not natively available yet, let’s use the Temporal polyfill:
import { Temporal } from '@js-temporal/polyfill';
// Creating dates and times
const date = Temporal.PlainDate.from({ year: 2024, month: 3, day: 15 });
console.log(date);
// Output: 2024-03-15
// Creating a specific time
const time = Temporal.PlainTime.from({ hour: 14, minute: 30 });
console.log(time);
// Output: 14:30:00
// Combining date and time into a single object
const dateTime = date.toPlainDateTime(time);
console.log(dateTime);
// Output: 2024-03-15T14:30:00
// Working with time zones
const timeZone = Temporal.TimeZone.from('Europe/Paris');
console.log(timeZone.toString());
// Output: Europe/Paris
// Converting our datetime to a specific timezone
const zonedDateTime = dateTime.toZonedDateTime(timeZone);
console.log(zonedDateTime);
// Output: 2024-03-15T14:30:00+01:00[Europe/Paris]
// Getting the current date and time in the system's timezone
const now = Temporal.Now.zonedDateTimeISO();
console.log(now);
// Output: 2024-12-22T15:30:00-05:00[America/New_York] (example output - will vary based on current time)
One of the Temporal API’s best features is its built-in support for different calendar systems:
// Working with different calendar systems
const hebrewDate = Temporal.PlainDate.from({
year: 5784,
month: 7,
day: 15,
calendar: 'hebrew'
});
const islamicDate = hebrewDate.withCalendar('islamic');
Temporal also provides great support for date arithmetic through the Temporal.Duration type:
// Creating and using durations
const duration = Temporal.Duration.from({
years: 1,
months: 2,
days: 15
});
const futureDate = dateTime.add(duration);
const pastDate = dateTime.subtract(duration);
// Calculating differences
const diff = dateTime.since(pastDate, {
largestUnit: 'year',
smallestUnit: 'day'
});
Temporal integrates seamlessly with the Internationalization API:
const dt = Temporal.Now.plainDateTime();
// Using Intl.DateTimeFormat
const formatter = new Intl.DateTimeFormat('fr', {
dateStyle: 'full',
timeStyle: 'long'
});
console.log(formatter.format(dt));
// Custom formatting
const relFormatter = new Intl.RelativeTimeFormat('de', {
numeric: 'auto'
});
const diff = dt.until(futureDate);
console.log(relFormatter.format(diff.days, 'day'));
Finally, Temporal provides methods to convert from legacy Date objects:
const legacyDate = new Date();
const temporal = Temporal.Instant.fromEpochMilliseconds(
legacyDate.getTime()
).toZonedDateTimeISO(Temporal.Now.timeZone());
Luxon, developed by one of Moment.js’s maintainers, builds on many of its concepts while introducing improvements in key areas. For internationalization purposes, you can think of Luxon as a wrapper for Intl.DateTimeFormat and Intl.RelativeTimeFormat.
For example, one way to format dates according to a locale is by first setting the locale and then using the toFormat(fmt:string, opts: Object) method along with date-time tokens from this table:
// Sample output: 2019 сентябрь
console.log(DateTime.local().setLocale('ru').toFormat('yyyy MMMM'));
You can also pass the locale in the options object that the method can take as an argument:
// Output: 2019 сентябрь
console.log(DateTime.local(2018, 9, 1).toFormat('yyyy MMMM', { locale: "ru" }));
If you’re using methods like fromObject, fromISO, fromHTTP, fromFormat, or fromRFC2822, you can set the locale at creation time:
const italianDate = DateTime.fromISO("2014-09-19", { locale: "it" });
// Output: 2014 settembre 19
console.log(italianDate.toFormat("yyyy MMMM dd"));
However, the recommended way is to use the toLocaleString() and toLocaleParts() methods, which return a localized string representing the date and an array with the individual parts of the string, respectively.
These methods are equivalent to the format() and formatToParts() methods of Intl.DateTimeFormat, and in fact, they take the same options object (along with some presets, such as DateTime.DATE_SHORT):
const date = DateTime.utc(2014, 9, 1, 14, 5, 0);
const options = {
dateStyle: "short",
timeStyle: "full",
hour12: true,
day: "numeric",
month: "long",
year: "2-digit",
minute: "2-digit",
second: "2-digit"
};
// Output: 1 septembre 14 à 05:00
console.log(date.setLocale("fr").toLocaleString(options));
// Output: 1. September 14, 05:00
console.log(date.setLocale("de-AT").toLocaleString(options));
/* Output: [{"type":"day","value":"1"},{"type":"literal","value":" "},{"type":"month","value":"settembre"},{"type":"literal","value":" "},{"type":"year","value":"14"},{"type":"literal","value":", "},{"type":"minute","value":"05"},{"type":"literal","value":":"},{"type":"second","value":"00"}] */
console.log(
JSON.stringify(date.setLocale("it").toLocaleParts(options), null, 3)
);
// Output: 2:05 PM
console.log(date.toLocaleString(DateTime.TIME_SIMPLE));
// Output: 01/09/2014
console.log(date.toLocaleString({ locale: 'pt' }));
This means that:
Intl objectIntl object is not available in your target browser, this part of the library won’t work properlyDateTime Luxon objectThe toRelative method (which returns a string representation of a time relative to now by default) and the toRelativeCalendar method (which provides a string representation of a date relative to today by default) offer functionality similar to Intl.RelativeTimeFormat:
// Sample output: in 23 hours
console.log(DateTime.local().plus({ days: 1 }).toRelative());
// Sample output: tomorrow
console.log(DateTime.local().plus({ days: 1 }).toRelativeCalendar());
// Sample output: in 1 Tag
console.log(DateTime.local().plus({ days: 1 }).toRelative({ locale: "de" }));
// Sample output: morgen
console.log(DateTime.local().plus({ days: 1 }).toRelativeCalendar({ locale: "de" }));
// Sample output: il y a 1 semaine
console.log(DateTime.local().setLocale("fr").minus({ days: 9 }).toRelative({ unit: "weeks" }));
// Sample output: la semaine dernière
console.log(DateTime.local().setLocale("fr").minus({ days: 9 }).toRelativeCalendar({ unit: "weeks" }));
Unlike Intl.RelativeTimeFormat, if your browser doesn’t support this API, the above methods won’t throw an error. The only problem is that they will not be translated into the appropriate language.
You can try all of the above examples here.
date-fns is another popular JavaScript library for date processing and formatting. Version 4, the latest at the time of this writing, only comes in the form of an npm package, so if you want to use it directly in a browser, you’ll have to use a bundler like Browserify.
This library contains around sixty different locales. To use one or more locales, you need to import them like this:
import { es, enCA, it, ptBR } from 'date-fns/locale'
The functions that accept a locale as an argument are the following:
format, which returns the formatted date, taking as parameters the date, a string representing the pattern to format the date (based on the date fields symbols of the Unicode technical standard #35), and an object with options like the locale and the index of the first day of the weekformatDistance, which returns the distance between the given dates in words, taking as parameters the dates to compare and an object with options like the locale or whether to include secondsformatDistanceToNow is the same as formatDistance but only takes one date (that will be compared to now)formatDistanceStrict is the same as formatDistance but without using helpers like almost, over, or less than. The options object has properties to force a time unit and to specify the way to round partial unitsformatRelative, which represents the date in words relative to a given base date. It can also take an options object as an argument, to set the locale and the index of the first day of the weekHere are some examples:
import {
format,
formatDistance,
formatDistanceToNow,
formatDistanceStrict,
formatRelative,
addDays
} from "date-fns";
import { es, enCA, ro, it, ptBR } from "date-fns/locale";
// Output: septiembre / 19
console.log(format(new Date(), "MMMM '/' yy", { locale: es }));
// Output: in less than 10 seconds
console.log(
formatDistance(
new Date(2019, 8, 1, 0, 0, 15),
new Date(2019, 8, 1, 0, 0, 10),
{ locale: enCA, includeSeconds: true, addSuffix: true }
)
);
// Output: less than 10 seconds ago
console.log(
formatDistance(
new Date(2019, 8, 1, 0, 0, 10),
new Date(2019, 8, 1, 0, 0, 15),
{ locale: enCA, includeSeconds: true, addSuffix: true }
)
);
// Output: circa 15 ore (assuming now is 9/20/2019 15:00)
console.log(formatDistanceToNow(new Date(2019, 8, 20), { locale: ro }));
// Output: 0 minuti
console.log(
formatDistanceStrict(
new Date(2019, 8, 1, 0, 0, 15),
new Date(2019, 8, 1, 0, 0, 10),
{ locale: it, unit: "minute" }
)
);
// Output: un minuto
console.log(
formatDistanceStrict(
new Date(2019, 8, 1, 0, 0, 10),
new Date(2019, 8, 1, 0, 0, 15),
{ locale: it, unit: "minute", roundingMethod: "ceil" }
)
);
// Output: amanhã às 14:48
console.log(formatRelative(addDays(new Date(), 1), new Date(), { locale: ptBR }));
formatRelative is usually used with helpers to add or subtract different units of time like addWeeks, subMonths, addQuarters, among others.
Also, consider that if the distance between the dates is more than six days, formatRelative will return the date given as the first argument:
// If today is September 20, 2019 the output will be 27/09/2019
console.log(formatRelative(addDays(new Date(), 7), new Date(), { locale: ptBR }));
You can try all of the above examples here.
date-fns uses functional programming, which means it has pure functions. These functions always give the same result for the same input. This leads to several benefits including predictable behavior, easier testing, excellent tree-shaking capabilities, and it integrates well with TypeScript. Unlike Moment.js or Day.js, which use chainable APIs, date-fns works directly with JavaScript native Date objects.
date-fns does not handle time zones on its own, but the related library date-fns-tz offers strong support for time zones:
import { zonedTimeToUtc, utcToZonedTime, format } from 'date-fns-tz';
import { addDays } from 'date-fns';
// Converting between time zones
const nyDate = zonedTimeToUtc('2024-12-23 14:00', 'America/New_York');
const tokyoDate = utcToZonedTime(nyDate, 'Asia/Tokyo');
// Formatting with time zone information
console.log(format(tokyoDate, 'yyyy-MM-dd HH:mm zzz', {
timeZone: 'Asia/Tokyo'
}));
// Output: "2024-12-24 04:00 JST"
date-fns also shines when handling complex date calculations and comparisons:
import {
eachDayOfInterval,
endOfMonth,
startOfMonth,
isWithinInterval,
getWeeksInMonth,
setDay,
format
} from 'date-fns';
// Let's assume today is December 23, 2024
const today = new Date('2024-12-23');
// Get the start of the month (December 1, 2024)
const monthStart = startOfMonth(today);
console.log('Month start:', format(monthStart, 'yyyy-MM-dd'));
// Output: Month start: 2024-12-01
// Get the end of the month (December 31, 2024)
const monthEnd = endOfMonth(monthStart);
console.log('Month end:', format(monthEnd, 'yyyy-MM-dd'));
// Output: Month end: 2024-12-31
// Get array of all days in the month
const daysInMonth = eachDayOfInterval({
start: monthStart,
end: monthEnd
});
console.log('Number of days in month:', daysInMonth.length);
// Output: Number of days in month: 31
console.log('First few days:', daysInMonth.slice(0, 3).map(d => format(d, 'yyyy-MM-dd')));
// Output: First few days: ['2024-12-01', '2024-12-02', '2024-12-03']
// Finding all Mondays in the month
const mondays = daysInMonth.filter(date =>
format(date, 'EEEE') === 'Monday'
);
console.log('Mondays in December 2024:', mondays.map(d => format(d, 'dd')));
// Output: Mondays in December 2024: ['02', '09', '16', '23', '30']
// Checking if December 15, 2024 falls within the month range
const targetDate = new Date('2024-12-15');
const isInRange = isWithinInterval(targetDate, {
start: monthStart,
end: monthEnd
});
console.log('Is December 15 in current month?', isInRange);
// Output: Is December 15 in current month?: true
// Getting number of weeks in December 2024
const weeksInMonth = getWeeksInMonth(today);
console.log('Number of weeks in month:', weeksInMonth);
// Output: Number of weeks in month: 5
// (Because December 2024 spans across 5 different weeks)
// Setting to nearest Monday
// If today is Monday Dec 23, it returns the same date
// If today is another day, it returns the following Monday
const nearestMonday = setDay(today, 1, { weekStartsOn: 1 });
console.log('Nearest Monday:', format(nearestMonday, 'yyyy-MM-dd'));
// Output: Nearest Monday: 2024-12-23
Day.js is a lightweight library alternative to Moment.js.
By default, Day.js comes with the United States English locale. To use other locales, you need to import them like this:
import 'dayjs/locale/pt';
import localeDe from 'dayjs/locale/de'; // With a custom alias for the locale object
dayjs.locale('pt') // use Portuguese locale globally
// To use the locale just in certain places
console.log(
dayjs()
.locale(localeDe)
.format()
);
console.log( dayjs('2018-4-28', { locale: 'pt' }) );
In the above example, the format() method returns a string with the formatted date. It can take a string with the tokens to format the date in a specific way:
// Sample output: September 2019, Samstag
console.log(
dayjs()
.locale(localeDe)
.format('MMMM YYYY, dddd')
);
Here is the list of all available formats.
However, much of the advanced functionality of Day.js comes from plugins that you can load based on your needs. For example, the UTC plugin adds methods to get a date in UTC and local time:
import dayjs from "dayjs"; import utc from "dayjs/plugin/utc"; dayjs.extend(utc); console.log(dayjs.utc().format()); // Sample output: 2019-09-21T11:31:55Z
Regarding internationalization, we can use the AdvancedFormat, LocalizedFormat, RelativeTime, and Calendar plugins.
The AdvancedFormat and LocalizedFormat plugins add more formatting options to the format() method:
// ...
// Plugins
import advancedFormat from "dayjs/plugin/advancedFormat";
import localizedFormat from "dayjs/plugin/localizedFormat";
// Load plugins
dayjs.extend(advancedFormat);
dayjs.extend(localizedFormat);
// Advanced format options
// If today is 2019/09/21 at 12:00 PM, the output will be 3 21º 12 12 1569087454 1569087454869
console.log(
dayjs()
.locale("pt")
.format("Q Do k kk X x")
);
// Localized format options
// If today is 2019/09/21 at 12:00 PM, the output will be Sábado, 21 de Setembro de 2019 às 12:00
console.log(
dayjs()
.locale("pt")
.format("LLLL")
);
The RelativeTime plugin adds methods to format dates to relative time strings:
.fromNow(withoutSuffix?: boolean) returns a string representing the relative time from now.from(compared: Dayjs, withoutSuffix?: boolean) returns a string representing the relative time from X.toNow(withoutSuffix?: boolean) returns a string representing the relative time to now.to(compared: Dayjs, withoutSuffix?: boolean) returns a string representing the relative time to XHere are some examples:
// ...
import relativeTime from "dayjs/plugin/relativeTime";
// Load plugin
dayjs.extend(relativeTime);
// Assuming now is 2019-09-21 at 12:00 PM
// Output: in einem Jahr
console.log(
dayjs()
.locale(localeDe)
.from(dayjs("2018-09-21"))
);
// Output: einem Jahr
console.log(
dayjs()
.locale(localeDe)
.from(dayjs("2018-09-21"), true)
);
// Output: vor einem Jahr
console.log(
dayjs("2018-09-21")
.locale(localeDe)
.fromNow()
);
// Output: vor 2 Jahren
console.log(
dayjs("2018-09-21")
.locale(localeDe)
.to(dayjs("2016-09-21"))
);
// Output: vor 11 Jahren
console.log(
dayjs("2030-09-21")
.locale(localeDe)
.toNow()
);
The Calendar plugin adds the .calendar method to display calendar time (within a distance of seven days). It doesn’t seem to localize the output:
// ...
import calendar from "dayjs/plugin/calendar";
// Load plugin
dayjs.extend(calendar);
// Assuming now is 2019-09-21 at 12:00 PM
// Output: Yesterday at 12:00 PM
console.log(
dayjs()
.locale('pt')
.calendar(dayjs("2019-09-22"))
);
However, it allows you to manually customize output labels for specific cases like the same day, next day, last weekend, and next week. You can define these using string literals (enclosed in square brackets) and date-time format tokens:
// Assuming now is 2019-09-21 at 12:00 PM
// The output is Hoje às 12:00
console.log(
dayjs().calendar(dayjs("2019-09-21"), {
sameDay: "[Hoje às] h:m",
nextDay: "[Amanhã]",
nextWeek: "dddd",
lastDay: "[Ontem]",
lastWeek: "[Último] dddd",
sameElse: "DD/MM/YYYY"
})
);
You can try all of the above examples here.
Unlike general-purpose date libraries such as Moment.js, Day.js, or date-fns, little-date takes a specialized approach. Developed by Vercel, it focuses on formatting date ranges in a concise and user-friendly way, prioritizing readability and simplicity.
Built on top of date-fns for parsing and manipulation, little-date also supports localization through configuration and works seamlessly in both browser and Node.js environments:
import { formatDateRange } from "little-date";
// Basic date range in the same month
const from = new Date("2024-01-01");
const to = new Date("2024-01-12");
console.log(formatDateRange(from, to));
// Output: "Jan 1 - 12"
// Date range spanning multiple months
const multiMonth = formatDateRange(
new Date("2024-01-03"),
new Date("2024-04-20")
);
console.log(multiMonth);
// Output: "Jan 3 - Apr 20"
// Date range with times
const withTime = formatDateRange(
new Date("2024-01-01T00:11:00"),
new Date("2024-01-01T14:30:00")
);
console.log(withTime);
// Output: "Jan 1, 12:11am - 2:30pm"
// Range spanning different years
const multiYear = formatDateRange(
new Date("2022-01-01"),
new Date("2023-01-20")
);
console.log(multiYear);
// Output: "Jan 1 '22 - Jan 20 '23"
One of little-date’s strengths is its ability to automatically choose appropriate formatting based on the context:
import { formatDateRange } from "little-date";
// Today with time range
const today = formatDateRange(
new Date("2024-12-23T00:00:00"),
new Date("2024-12-23T14:30:00"),
{ today: new Date("2024-12-23") }
);
console.log(today);
// Output: "12am - 2:30pm"
While little-date is intentionally opinionated, it provides essential customization options through its configuration object:
const options = {
locale: "de-AT", // Override default locale
includeTime: false, // Exclude time components
today: new Date(), // Set reference point for "today"
separator: "to" // Change the range separator
};
const formattedRange = formatDateRange(from, to, options);
Because little-date is built on top of date-fns, it integrates seamlessly with existing date-fns implementations:
import { formatDateRange } from "little-date";
import { addDays, subDays, getQuarter, getYear, format } from "date-fns";
// For quarter representation
const date = new Date("2023-01-01");
const quarterDisplay = `Q${getQuarter(date)} ${getYear(date)}`;
console.log(quarterDisplay);
// Output: "Q1 2023"
// For full month representation
const monthDisplay = format(date, "MMMM yyyy");
console.log(monthDisplay);
// Output: "January 2023"
const today = new Date();
const weekRange = formatDateRange(
subDays(today, 3),
addDays(today, 3)
);
console.log(weekRange);
// Output example: "Dec 20 - 26" (assuming today is Dec 23)
| Feature | Native Intl | Temporal API | Luxon | date-fns | Day.js | little-date |
|---|---|---|---|---|---|---|
| Bundle size | 0 KB (built-in) | Polyfill dependent | ~69 KB | ~14 KB (core) | ~2 KB (core) | ~3 KB |
| Immutability | N/A | Yes | Yes | Yes | No | N/A |
| Tree shaking | N/A | N/A | Partial | Yes | Yes | Yes |
| Timezone support | Basic | Advanced | Advanced | Via date-fns-tz | Via plugin | No |
| Parsing | Limited | Comprehensive | Comprehensive | Comprehensive | Good | Via date-fns |
| Formatting | Comprehensive | Comprehensive | Comprehensive | Comprehensive | Good | Range-focused |
| Internationalization | Excellent | Excellent | Excellent | Good | Good | Basic |
| Date arithmetic | No | Yes | Yes | Yes | Yes | No |
| Duration support | Basic | Advanced | Advanced | Yes | Via Plugin | No |
| Relative time | Yes | Yes | Yes | Yes | Via Plugin | Limited |
| Browser support | Excellent | Polyfill required | Good | Excellent | Excellent | Good |
| TypeScript support | Native | Excellent | Excellent | Excellent | Good | Good |
| Learning curve | Moderate | Moderate | Moderate | Moderate | Low | Low |
| Modern JS features | Yes | Yes | Yes | Yes | Yes | Yes |
| Dependencies | None | None | None | None | None | date-fns |
| Active development | Yes | In progress | Yes | Yes | Yes | Yes |
Moment.js is a well-established library for date processing, but it can be excessive for smaller or simpler projects. In this article, I’ve compared how five popular libraries approach date formatting in the context of internationalization.
The features provided by the JavaScript Internationalization API may suffice for simple use cases, but if you need a higher-level API (e.g., relative times) and other features such as timezones or helper methods for adding or subtracting units of time, you may benefit from one of the other libraries reviewed in this article.
Happy coding!
Debugging code is always a tedious task. But the more you understand your errors, the easier it is to fix them.
LogRocket allows you to understand these errors in new and unique ways. Our frontend monitoring solution tracks user engagement with your JavaScript frontends to give you the ability to see exactly what the user did that led to an error.
LogRocket records console logs, page load times, stack traces, slow network requests/responses with headers + bodies, browser metadata, and custom logs. Understanding the impact of your JavaScript code will never be easier!

Discover what’s new in The Replay, LogRocket’s newsletter for dev and engineering leaders, in the November 5th issue.

A senior developer discusses how developer elitism breeds contempt and over-reliance on AI, and how you can avoid it in your own workplace.

Examine AgentKit, Open AI’s new tool for building agents. Conduct a side-by-side comparison with n8n by building AI agents with each tool.

AI agents powered by MCP are redefining interfaces, shifting from clicks to intelligent, context-aware conversations.
Would you be interested in joining LogRocket's developer community?
Join LogRocket’s Content Advisory Board. You’ll help inform the type of content we create and get access to exclusive meetups, social accreditation, and swag.
Sign up now