mrkeyoor.com_
Sat 08 Aug 21:02 UTC
npmUtilsupdated 08 Aug 2026

@js-joda/core

@js-joda/core is an immutable date and time model patterned after Java's java.time and the ThreeTen backport. Instead of forcing every value into JavaScript Date, it separates calendar dates, wall-clock times, local date-times, offsets, instants, date-based periods, and time-based durations. ISO parsing and formatting are built in, and calculations return new values. Core handles ISO chronology plus fixed offsets and the system zone; named IANA zones and localized text live in separate js-joda packages.

Verdict

@js-joda/core is an excellent fit when date-only values, instants, and calendar arithmetic must remain distinct and a java.time-style API is welcome. Skip it for light Date formatting or if separate zone and locale data packages feel heavier than your problem.

API stability4/5The public domain model comes from the mature ThreeTen and java.time design, and classes such as LocalDate, Instant, Period, Duration, and DateTimeFormatter have stayed recognizable across releases. Major versions still matter: 6.0 simplified nativeJs, removed IE11 support, and coordinated a new Locale import path with @js-joda/locale 5, while older majors also cleaned up internal methods and typings.
Docs4/5The README clearly explains why LocalDate differs from native Date, inventories the core types, documents the companion timezone and locale packages, and links to a quick-start manual plus generated API pages. The Java-shaped API is very broad, some README claims and browser references are dated, and important version 6 migration details are easier to find in the monorepo changelog than in the main guide.
Maintenance5/5Core 6.1.0 was published in July 2026, the repository was pushed later that month, and the release fixed parsing, negative-duration formatting, pre-epoch truncation, zone IDs, and declarations. GitHub reports 11 open issues and pull requests in its combined counter across the monorepo. Time-zone data also received a 2026a update through the companion package.
Ecosystem4/5The package recorded 4,527,716 downloads for the measured week and the repository has 1,662 stars. The maintained family covers IANA time zones, locale data, ThreeTen-Extra types, examples, CommonJS, ESM, browsers, and TypeScript. It is less universal than native Date or date-fns, and its strongest conceptual interoperability is with teams already familiar with java.time.

Use it if

  • Your domain distinguishes a birthday or billing date from a timestamp and you want types that enforce that distinction
  • A Java or Kotlin team wants JavaScript date code with familiar java.time concepts and method names
  • You need immutable calendar arithmetic, ISO parsing, periods, durations, adjusters, and nanosecond-capable instants
  • The project needs deterministic clock injection for date-sensitive tests without wrapping every call to now
Skip it if

Setup reality

npm install @js-joda/core is straightforward: version 6.1.0 has no runtime or peer dependencies, ships CommonJS and ESM builds, and includes TypeScript declarations through its typings entry. The complexity comes from choosing the right domain type. LocalDate has no time or zone, LocalDateTime is only a wall-clock description, Instant is a point on the timeline, Period performs calendar math, and Duration measures elapsed time. Converting a local value to an instant always requires a zone or offset; do not silently use the machine zone for stored business data. Core does not bundle IANA rules. Install @js-joda/timezone and import it for side effects before ZoneId.of('Europe/Berlin') or other named zones; that package carries the changing tz database and offers reduced-range builds with explicit date limits. Localized formatting similarly needs @js-joda/locale and a locale data package. Since core 6, import Locale from @js-joda/locale, then import packages such as @js-joda/locale_en only to register data. The 6.0 release also simplified nativeJs and dropped IE11. Objects are immutable, so plusDays and withMonth return replacements rather than changing the original. Parsing errors throw DateTimeParseException, invalid calendar values throw DateTimeException, and Java-style formatter symbols have semantics worth testing, especially year-of-era versus proleptic year. native Date uses milliseconds while Instant can represent finer precision, so round trips through Date lose sub-millisecond data.

Patterns

Parse and serialize a date-only valueparse-local-date

import { LocalDate } from '@js-joda/core';

const birthday = LocalDate.parse('1990-05-23');
console.log(birthday.year(), birthday.monthValue(), birthday.dayOfMonth());
console.log(birthday.toString()); // 1990-05-23

LocalDate intentionally has no time or zone, which makes it safer for birthdays, holidays, and billing dates.

Construct a validated calendar datecreate-local-date

import { LocalDate } from '@js-joda/core';

const release = LocalDate.of(2026, 8, 8);

Invalid dates such as February 30 throw DateTimeException instead of rolling into March like native Date can.

Add months and days immutablyadd-calendar-time

const start = LocalDate.parse('2026-01-31');
const renewed = start.plusMonths(1).plusDays(3);

console.log(start.toString());   // 2026-01-31
console.log(renewed.toString()); // 2026-03-03

plusMonths resolves an invalid target day to the month's last valid day. Every operation returns a new value.

Order date-only valuescompare-local-dates

const today = LocalDate.parse('2026-08-08');
const deadline = LocalDate.parse('2026-08-31');

if (today.isBefore(deadline)) {
  console.log('still open');
}

Use isBefore, isAfter, isEqual, or compareTo. JavaScript < on these objects is not the intended API.

Calculate calendar and total-day differencesmeasure-date-difference

import { ChronoUnit, LocalDate, Period } from '@js-joda/core';

const start = LocalDate.parse('2025-12-31');
const end = LocalDate.parse('2026-03-02');
const calendar = Period.between(start, end);
const days = start.until(end, ChronoUnit.DAYS);

Period is expressed in calendar years, months, and days; ChronoUnit.DAYS gives a total date-line day count.

Model a wall-clock appointmentparse-local-date-time

import { LocalDateTime } from '@js-joda/core';

const appointment = LocalDateTime.parse('2026-10-25T09:30:00');
const reminder = appointment.minusMinutes(30);

LocalDateTime has no offset or zone and is not an instant. Attach an explicit zone before comparing it with timestamps.

Parse an absolute timestampparse-instant

import { Instant } from '@js-joda/core';

const receivedAt = Instant.parse('2026-08-08T12:30:45.123Z');
console.log(receivedAt.toEpochMilli());

Version 6.1 accepts ISO timestamps with offsets and resolves them to UTC. Instant can retain nanoseconds, while epoch milliseconds cannot.

Convert to and from JavaScript Dateconvert-native-date

import { convert, nativeJs } from '@js-joda/core';

const zoned = nativeJs(new Date('2026-08-08T12:30:00Z'));
const nativeDate = convert(zoned).toDate();

nativeJs returns a ZonedDateTime. Native Date stores milliseconds, so any finer Instant precision is lost on conversion.

Parse and format with an explicit patternformat-custom-pattern

import { DateTimeFormatter, LocalDate } from '@js-joda/core';

const formatter = DateTimeFormatter.ofPattern('dd/MM/uuuu');
const date = LocalDate.parse('08/08/2026', formatter);
console.log(date.format(formatter));

Core patterns handle numeric ISO fields. Localized names require @js-joda/locale plus registered locale data.

Find the last day of a monthadjust-calendar-date

import { LocalDate, TemporalAdjusters } from '@js-joda/core';

const date = LocalDate.parse('2026-02-10');
const monthEnd = date.with(TemporalAdjusters.lastDayOfMonth());
console.log(monthEnd.toString()); // 2026-02-28

Adjusters return new temporal values and encode calendar intent more clearly than manual day arithmetic.

Attach a named IANA time zoneuse-iana-timezone

import { LocalDateTime, ZoneId } from '@js-joda/core';
import '@js-joda/timezone';

const berlin = LocalDateTime.parse('2026-07-10T09:00')
  .atZone(ZoneId.of('Europe/Berlin'));

Install and import @js-joda/timezone for side effects before using named zones. Core alone supplies fixed offsets and the system zone.

Inject a fixed clock in testsfreeze-current-time

import { Clock, Instant, LocalDate, ZoneOffset } from '@js-joda/core';

const clock = Clock.fixed(
  Instant.parse('2026-08-08T23:30:00Z'),
  ZoneOffset.UTC
);
const today = LocalDate.now(clock);

Passing Clock avoids global timer mocks and makes the zone governing today explicit.

Alternatives

PackageRegistryPick it when
date-fnsnpmYou prefer tree-shakable functions around native Date and need a broad menu of formatting and calendar helpers
luxonnpmYou want immutable date-times with Intl-backed zones and locales in a more JavaScript-shaped API
dayjsnpmYou want a compact Moment-like chainable API and can add capabilities through plugins
temporal-polyfillnpmYou want the JavaScript Temporal model and a migration path toward the standard API