Home Reference Source

Immutable date and time library for JavaScript

npm version Travis Build Status Sauce Test Status Coverage Status Downloads/Month

Sauce Browser Matrix

Introduction

js-joda is an immutable date and time library for JavaScript. It provides a simple, domain-driven and clean API based on the ISO calendar system, which is the de facto world calendar following the proleptic Gregorian rules.

  • js-joda has a lightweight footprint, only 43 kB minified and compressed, no third party dependencies.

  • js-joda is fast. It is about 2 to 10 times faster than other JavaScript date libraries.

  • js-joda comes with built-in parsers/ formatters for ISO 8601 as specified in RFC 3339, that can be easily customized.

  • js-joda supports ECMAScript 5 browsers down to IE11.

  • js-joda is a port of the threeten backport, which is the base for JSR-310 implementation of the Java SE 8 java.time package. Threeten is inspired by Joda-Time, having similar concepts and the same author.

  • js-joda is robust and stable. We ported more then 1700 test-cases with a lots of test-permutations from the threetenbp project. We run the automated karma test-suite against Firefox, Chrome, Node and phantomjs.

Why yet another JavaScript date and time library?

  • Popular JavaScript date libraries like moment or date-utils are wrappers around the native JavaScript Date object, providing syntactic sugar. The native Date object always consist of a date, time and a timezone part. In contrast, js-joda is a standalone date and time implementation.

  • The API has a domain-driven design with classes for each of the different use cases, like LocalDate, ZonedDateTime or Period. For example, LocalDate allows you to handle dates without times (like birthdays or holidays) in a clean and error-safe way, especially if these dates are persisted to an external server.

  • js-joda is immutable. Immutability aligns well with pure functions and with the architecture of frameworks like React and Flux.

The ThreeTen domain models

Dates and Times

  • LocalDate represents a date without a time and timezone in the ISO-8601 calendar system, such as 2007-12-24.

  • LocalTime represents a time without timezone in the ISO-8601 calendar system such as '11:55:00'.

  • LocalDateTime is a description of the date (LocalDate), as used for birthdays, combined with the local time (LocalTime) as seen on a wall clock.

  • ZonedDateTime is a date-time with a timezone in the ISO-8601 calendar system, such as 2007-12-24T16:15:30+01:00 UTC+01:00.

  • Instant is an instantaneous point on the time-line measured from the epoch of 1970-01-01T00:00:00Z in epoch-seconds and nanosecond-of-second.

Duration and Period

  • Duration is a time-based amount of time, such as '34.5 seconds'.

  • Period is a date-based amount of time in the ISO-8601 calendar system, such as '2 years, 3 months and 4 days'.

Additional value types

  • Year represents a year in the ISO-8601 calendar system, such as '2016'.

  • YearMonth represents a year and a month in the ISO-8601 calendar system, such as '2016-01'.

  • Month represents a month-of-year in the ISO-8601 calendar system, such as 'July'.

  • MonthDay represents a month-day in the ISO-8601 calendar system, such as '--12-03'. Could be used to represent e.g. Birthdays.

  • DayOfWeek represents a day-of-week in the ISO-8601 calendar system, such as 'Tuesday'.

Getting started

Node

Install joda using npm

npm install @js-joda/core

Then require it to any module

var LocalDate = require('@js-joda/core').LocalDate;

var d = LocalDate.parse('2012-12-24').atStartOfDay().plusMonths(2); // 2013-02-24T00:00:00

Browser

To use js-joda from a browser, download js-joda from a cdn (eg https://cdn.jsdelivr.net/npm/@js-joda/core@<version>) either dist/js-joda.min.js or dist/js-joda.js (with sourcemaps for development). Then add it as a script tag to your page

<script src="js-joda.min.js"></script>
<script>
    var LocalDate = JSJoda.LocalDate;
    var d = LocalDate.parse('2012-12-24').atStartOfDay().plusMonths(2); // 2013-02-24T00:00:00
</script>

js-joda packages

js-joda consist of four packages:

package name description path
@js-joda/core Implementation of the ThreeTen Classes and API /packages/core
@js-joda/timezone Implementation of timezone calculation based on the iana Time Zone Database /packages/timezone
@js-joda/locale Implementation of locale specific functionality for js-joda, especially for formatting and parsing locale specific dates /packages/locale
@js-joda/extra Implementation of the ThreeTen-Extra Classes and API /packages/extra

The @js-joda/examples package is for testing the different build artifacts in different context, like webpack, browser node, etc.

Documentation

Contributing

Contributions are always welcome. Before contributing please read the code of conduct & search the issue tracker. We use GitHub issues. Your issue may have already been discussed or fixed. To contribute, fork js-joda, commit your changes, & send a pull request.

By contributing to js-joda, you agree that your contributions will be licensed under its BSD license.

Note that only pull requests and issues that match the threeten backport API will be considered. Additional requested features will be rejected.

License

  • js-joda is released under the BSD 3-clause license.

  • js-joda uses the ThreeTen-Backport implementation (http://www.threeten.org/threetenbp/) as a reference base for implementation. This allows us to release js-joda under the BSD License while the OpenJDK java.time implementation is under GNU GPL+linking exception. The API of the ThreeTen-Backport is mostly identical to the official Java SE 8 API from the view of our JavaScript port.

  • Our implementation reference base ThreeTen-Backport (http://www.threeten.org/threetenbp/) is also released under the BSD 3-clause license

  • OpenJDK is under GNU GPL+linking exception.

  • The author of Joda-Time and the lead architect of the JSR-310 is Stephen Colebourne.

The API of this project (as far as possible with JavaScript), a lot of implementation details and documentation are just copied but never equalled.

Roadmap

Milestone 1: Core domains (reached with version v1.0.0)

  • Support for the domain models LocalDate, LocalDateTime, ZonedDateTime, Instant, Duration and Period converting to and from ISO8601.
  • ZonedDateTime (without support for loading iana timezone databases) currently supports only fixed offsets like UTC or UTC+02:00 and the system default time zone.

Milestone 2: IANA timezone support (reached with version v1.2.0)

  • Add IANA timezone database support to js-joda. Implement handling of daylight saving transitions, mainly in ZonedDateTime.
  • For access to the IANA timezone database, the plugin @js-joda/timezone is required. It provides an implementation of the ZoneRulesProvider and contains the iana timezone database.

Milestone 3: Locale support (reached with v2.0.0 of @js-joda/locale)

  • Add locale support.
  • Extend pattern parser/ formatter for text with locale support.

see the plugin @js-joda/locale

Future Milestones

  • Reduce library size by removing redundant code, especially by refactoring code for formatting/ parsing dates.
  • Increase test coverage (ongoing task)
  • Cleanup documentation (ongoing task)

Getting started

Node

Install joda using npm

npm install @js-joda/core

Then require it to any module

var LocalDate = require("@js-joda/core").LocalDate;

var d = LocalDate.parse("2012-12-24")
  .atStartOfDay()
  .plusMonths(2); // 2013-02-24T00:00:00

Browser

To use js-joda from a browser, download either dist/js-joda.min.js or dist/js-joda.js (with sourcemaps for development)

Then add it as a script tag to your page

<script src="js-joda.min.js"></script>
<script>
    var LocalDate = JSJoda.LocalDate;
    var d = LocalDate.parse('2012-12-24').atStartOfDay().plusMonths(2); // 2013-02-24T00:00:00
</script>

Fiddle around

Fiddle around with js-joda, here on this page in the browser developer console, the latest js-joda code of all packages is always injected into this documentation.

Or find a simple example setup at JSFiddle https://jsfiddle.net/shto0ze6/, but don't forget to update the cdnjs resources to the desired versions.

Usage

The API is immutable. An existing instance is never changed. All manipulating methods (plus, at, etc.) return new instances.

An existing instance is always valid. If you try to create an invalid value, you'll get an exception instead of a null or undefined value.

Method naming conventions

The API uses consistently named methods.

method name or prefix usage examples
.of static factory method for building by parts LocalDate.of(2016, 2, 23)
LocalDate.ofInstant(i)
.parse static factory method for parsing strings LocalDate.parse('2016-02-23')
LocalTime.parse('12:34')
.is checks for certain conditions t1.isAfter(t2)
d1.isLeapYear()
.equals checks for equivalence between two instances t1.equals(t2)
.with the immutable equivalent of a setter d.withDayOfMonth(1)
t.withHour(9)
.plus adds an amount to an object t.plusMinutes(5)
d.plus(3, ChronoUnit.YEARS)
.minus subtracts an amount from an object t.minusHours(1)
d.minus(1, ChronoUnit.DAYS)
.to converts this object to another type dt.toLocalDate()
d1.until(d2).toTotalMonths()
.at combines one object with another date.atTime(time)
localDate.atZone(tz)

Note that getter methods for instance properties omit the get keyword: d.year(), not d.getYear().

LocalDate

A LocalDate represents a date with no time and no time zone in the ISO-8601 calendar system, such as 2007-12-24.

Create a LocalDate

// obtain the current date in the system default time zone, e.g. 2016-02-23
LocalDate.now();

// obtain the current date in the UTC time zone, e.g. 2016-02-23
LocalDate.now(ZoneOffset.UTC);

// obtain an instance of LocalDate from an ISO 8601 formatted text string
LocalDate.parse("2016-02-23");

// obtain an instance of LocalDate from a year, month, and dayOfMonth value
LocalDate.of(2016, 2, 23); // 2016-02-23

// obtain an instance of LocalDate from a year, month, and dayOfMonth value
LocalDate.of(2016, Month.FEBRUARY, 23); // 2016-02-23

// obtain an instance of LocalDate from an epochDay where day 0 is 1970-01-01
LocalDate.ofEpochDay(-1); // 1969-12-31

// obtain an instance of LocalDate from an epochDay where day 0 is 1970-01-01
LocalDate.ofYearDay(2016, 42); // 2016-02-11

Get values from LocalDate

var d = LocalDate.parse("2016-12-24");

d.toString(); // '2016-12-24' ISO 8601 format

d.dayOfMonth(); // 24
d.month(); // Month.DECEMBER
d.monthValue(); // 12
d.year(); // 2016

d.dayOfWeek(); // DayOfWeek.SATURDAY
d.dayOfWeek().value(); // 6
d.dayOfYear(); // 359

d.isLeapYear(); // true - 2016 is a leap year
d.plusYears(1).isLeapYear(); // false

// get the epoch day where 0 is 1970-01-01
d.toEpochDay(); // 17159

// get range of month
d.lengthOfMonth(); // 31
d.range(ChronoField.DAY_OF_MONTH); // ValueRange(1 - 31)

// get range of year
d.lengthOfYear(); // 366
d.range(ChronoField.DAY_OF_YEAR); // ValueRange(1 - 366)

// get other date-based field like the aligned week of year
d.get(ChronoField.ALIGNED_WEEK_OF_YEAR); // 52

// or the day of week aligned to the first day of month
d.get(ChronoField.ALIGNED_DAY_OF_WEEK_IN_MONTH); // 3

Get week of week-based year, quarter of year, day of quarter

// get week of week-based year as defined by ISO 8601, with a Monday-based week
d.get(IsoFields.WEEK_OF_WEEK_BASED_YEAR); // 51

d.isoWeekOfWeekyear(); // 51, equivalent to the above
d.isoWeekyear(); // 2016

LocalDate.of(2017, 1, 1).isoWeekOfWeekyear(); // 52
LocalDate.of(2017, 1, 1).isoWeekyear(); // 2016

// set the date to week 52 of week-based year with the same day of week
d.with(IsoFields.WEEK_OF_WEEK_BASED_YEAR, 52); // 2016-12-31

// get the quarter of the year
d.get(IsoFields.QUARTER_OF_YEAR); // 4
d.get(IsoFields.DAY_OF_QUARTER); // 85

// set the date to the 15th day of the third quarter
d.with(IsoFields.QUARTER_OF_YEAR, 3).with(IsoFields.DAY_OF_QUARTER, 15); // 2016-07-15

Adding to and subtracting from a LocalDate

Note that each of these methods returns a new LocalDate instance.

var d = LocalDate.parse("2016-02-23");

// add/subtract 366 days
d.plusDays(366); // '2017-02-23'
d.minusDays(366); // '2015-02-22'

// add/subtract 12 months
d.plusMonths(12); // '2017-02-23'
d.minusMonths(12); // '2015-02-23'

// add/subtract 4 weeks
d.plusWeeks(4); // '2016-03-22'
d.minusWeeks(4); // '2016-01-26'

// add/subtract 1 year
d.plusYears(1); // '2017-02-23'
d.minusYears(1); // '2015-02-23'

// add/subtract 30 years
d.plus(3, ChronoUnit.DECADES); // '2046-02-23'
d.minus(3, ChronoUnit.DECADES); // '1986-02-23'

// add/subtract a Period of 3 Months and 3 Days
d.plus(Period.ofMonths(3).plusDays(3)); // '2016-05-26'
d.minus(Period.ofMonths(3).plusDays(3)); // '2015-11-20'

Alter specific fields of a LocalDate

var d = LocalDate.parse("2016-12-24");

// set the day of month to 1
d.withDayOfMonth(1); // '2016-12-01'

// set month and the day of month to 1
d.withMonth(1).withDayOfMonth(1); // '2016-01-01'

// set month to November and the day of month to 1
d.withMonth(Month.NOVEMBER).withDayOfMonth(1); // '2016-11-01'

// set the year to beginning of era
d.withYear(1); // '0001-12-24'

// get the last day of the current month
LocalDate.now()
  .plusMonths(1)
  .withDayOfMonth(1)
  .minusDays(1);

// set the day of year
d.withDayOfYear(42); // 2016-02-11

// set the week of week-based year to 52
d.with(IsoFields.WEEK_OF_WEEK_BASED_YEAR, 52); // 2016-12-31

Compare one LocalDate with another

var d1 = LocalDate.parse("2016-12-24");
var d2 = d1.plusDays(2);

d1.isAfter(d2); // false
d1.isBefore(d2); // true

d1.equals(d2); // false
d1.equals(d1.plusDays(0)); // true
d1.equals(d1.plusDays(1)); // false

d1.compareTo(d1) === 0; // true
d1.compareTo(d2) < 0; // true
d2.compareTo(d1) > 0; // true

d1.hashCode(); // 4129560
d2.hashCode(); // 4129562
d1.hashCode() !== d2.hashCode(); // true

Distance on the timeline

var d1 = LocalDate.parse("2016-12-24");
var d2 = d1.plusMonths(13).plusDays(42);

// obtain the Period between the two dates
d1.until(d2).toString(); // 'P1Y2M11D' (1 year, 2 months, 11 days in ISO-8601 period format)
d1.until(d2).toTotalMonths(); // 14

// obtain the distance between the two dates with a specific precision
d1.until(d2, ChronoUnit.MONTHS); // 14, returns the distance in total months
d1.until(d2, ChronoUnit.DAYS); // 438, returns the distance in total days

Converting from and to other temporals

// obtain a LocalDate from a LocalDateTime instance
var dt = LocalDateTime.now();
LocalDate.from(dt); // LocalDate from LocalDateTime
dt.toLocalDate(); // LocalDateTime to LocalDate (equivalent to the above)

var d1 = LocalDate.parse("2016-02-25");

// obtain a LocalDateTime at a certain LocalTime
d1.atStartOfDay(); // '2016-02-25T00:00'
d1.atTime(LocalTime.of(11, 55)); // '2016-02-25T11:55'
d1.atTime(LocalTime.NOON); // '2016-02-25T12:00'

// obtain a LocalDate from a JavaScript Date

// the manual way
var d = LocalDate.ofInstant(Instant.ofEpochMilli(new Date().getTime()));
// the recommended way with the JavaScript temporal
d = LocalDate.from(nativeJs(new Date()));
// converting from a moment works the same way
d = LocalDate.from(nativeJs(moment()));

Adjust a date to another date

TemporalAdjusters provide compact business logic for date-based temporals such as LocalDate, LocalDateTime or ZonedDateTime.

var d = LocalDate.parse("2016-12-24");

// get first/ last day of month
d.with(TemporalAdjusters.firstDayOfMonth()); // 2016-12-01
d.with(TemporalAdjusters.lastDayOfMonth()); // 2016-12-31

// get the next specified weekday
d.with(TemporalAdjusters.nextOrSame(DayOfWeek.SUNDAY)); // 2016-12-25
d.with(TemporalAdjusters.nextOrSame(DayOfWeek.SATURDAY)); // 2016-12-24
d.with(TemporalAdjusters.next(DayOfWeek.SATURDAY)); // 2016-12-31

// get the first/last weekday of month
d.with(TemporalAdjusters.lastInMonth(DayOfWeek.SATURDAY)); // 2016-12-31
d.with(TemporalAdjusters.firstInMonth(DayOfWeek.SATURDAY)); // 2016-12-03

Find more adjusters in the TemporalAdjusters API documentation.

LocalTime

A LocalTime represents a time with no date and no time zone in the ISO-8601 calendar system, such as '10:15:30'

Create a LocalTime instance

// obtain the current time in the system default time zone, e.g. '10:29:05.743'
LocalTime.now();

// obtain the current time in the UTC time zone, e.g. '09:29:05.743'
LocalTime.now(ZoneOffset.UTC);

// obtain an instance of LocalTime from an ISO 8601 formatted text string
LocalTime.parse("09:42"); // '09:42'
LocalTime.parse("09:42:42"); // '09:42:42'
LocalTime.parse("09:42:42.123"); // '09:42:42.123'
LocalTime.parse("09:42:42.123456789"); // '09:42:42.123456789'

// obtain an instance of LocalTime from hour, minute, second, and nanosecond values
LocalTime.of(23, 55); // '23:55'
LocalTime.of(23, 55, 42); // '23:55:42'
LocalTime.of(23, 55, 42, 123000000); // '23:55:42.123'

// obtain an instance of LocalTime from second of day
LocalTime.ofSecondOfDay(3666); // '01:01:06'

Get values from LocalTime

var t = LocalTime.parse("23:55:42.123");

t.toString(); // '23:55:42.123' ISO 8601 format

t.hour(); // 23
t.minute(); // 55
t.second(); // 42
t.nano(); // 123000000

// get other time-based fields
t.get(ChronoField.SECOND_OF_DAY); // 86142
t.get(ChronoField.MILLI_OF_SECOND); // 123
t.get(ChronoField.HOUR_OF_AMPM); // 11
// any other time-based ChronoField is allowed as param for get

Adding to/ subtracting from a LocalTime instance

var t = LocalTime.parse("11:55:42");

// add/subtract 12 hours
t.plusHours(12); // '23:55:42'
t.minusHours(12); // '23:55:42'

// add/subtract 30 minutes
t.plusMinutes(30); // '12:25:42'
t.minusMinutes(30); // '11:25:42'

// add/subtract 30 seconds
t.plusSeconds(30); // '11:56:12'
t.minusSeconds(30); // '11:55:12'

// add/subtract 1 million nanoseconds (1 millisecond)
t.plusNanos(1000000); // '11:56:42.001'
t.minusNanos(1000000); // '11:55:41.999'

// add/subtract a time-based unit
t.plus(1, ChronoUnit.MILLIS); // '11:55:42.001'
t.plus(1, ChronoUnit.HALF_DAYS); // '23:55:42'

// add/subtract a duration of 15 minutes
t.plus(Duration.ofMinutes(15)); // '12:10:42'
t.minus(Duration.ofMinutes(15)); // '11:40:42'

Alter specific fields of a LocalTime instance

var t = LocalTime.parse("11:55:42");

// set the hour of day to 1
t.withHour(1); // '01:55:42'

// set the minute of hour to 1
t.withMinute(1); // '11:01:42'

// set the second of minute to 1
t.withSecond(1); // '11:55:01'

// set the MILLI_OF_SECOND to 51
t.with(ChronoField.MILLI_OF_SECOND, 51); // '11:55:42.051'

// set by a custom  TemporalAdjusters
// sample of a custom adjuster that adjust to the next even second
nextEvenSecond = {
  adjustInto: function(t) {
    return t.second() % 2 === 0 ? t.plusSeconds(2) : t.plusSeconds(1);
  }
};
t.with(nextEvenSecond); // '11:55:44'
t.plusSeconds(1).with(nextEvenSecond); // '11:55:44'

Truncate a LocalTime instance

var t = LocalTime.parse("23:55:42.123");

t.truncatedTo(ChronoUnit.SECONDS); // '23:55:42'
t.truncatedTo(ChronoUnit.MINUTES); // '23:55:00'
t.truncatedTo(ChronoUnit.HOURS); // '23:00'
t.truncatedTo(ChronoUnit.HALF_DAYS); // '12:00'
t.truncatedTo(ChronoUnit.DAYS); // '00:00'

Compare LocalTime instances

var t1 = LocalTime.parse("11:55:42");
var t2 = t1.plusHours(2);

t1.isAfter(t2); // false
t1.isBefore(t2); // true

t1.equals(t1.plusHours(0)); // true
t1.equals(t1.plusHours(1)); // false

t1.compareTo(t1) === 0; // true
t1.compareTo(t2) < 0; // true
t2.compareTo(t1) > 0; // true

t1.hashCode(); // 916974646
t2.hashCode(); // -1743180648
t1.hashCode() !== t2.hashCode(); // true

Distance between times

var t1 = LocalTime.parse("11:00");
var t2 = t1
  .plusHours(2)
  .plusMinutes(42)
  .plusSeconds(12);

// obtain the duration between the two dates
t1.until(t2, ChronoUnit.HOURS); // 2
t1.until(t2, ChronoUnit.MINUTES); // 162
t1.until(t2, ChronoUnit.SECONDS); // 9732

Convert a LocalTime from a moment or JavaScript Date

// obtain a LocalTime instance from a JavaScript Date

// the manual way
var t = LocalTime.ofInstant(Instant.ofEpochMilli(new Date().getTime()));
// the recommended way with the JavaScript temporal
t = LocalTime.from(nativeJs(new Date()));
// converting from a `moment` instance works the same way
d = LocalTime.from(nativeJs(moment()));

LocalDateTime

A LocalDateTime represents a date-time with no time zone in the ISO-8601 calendar system, such as '2007-12-03T10:15:30'.

Create a LocalDateTime instance

// obtain the current date and time in the system default time zone, e.g. '2016-02-26T10:29:05.743'
LocalDateTime.now();

// obtain the current date and time in the UTC time zone
LocalDateTime.now(ZoneOffset.UTC);

// obtain an instance of LocalDateTime from an ISO 8601 formatted text string
LocalDateTime.parse("2016-02-26T09:42"); // '2016-02-26T09:42'
LocalDateTime.parse("2016-02-26T09:42:42.123"); // '2016-02-26T09:42:42.123'

// obtain an instance of LocalDateTime from year, month, dayOfMonth, hour, minute, second and nanosecond values
LocalDateTime.of(2016, 2, 29); // '2016-02-29T00:00'
LocalDateTime.of(2016, 2, 29, 12, 55, 42); // '2016-02-29T12:55:42'
LocalDateTime.of(2016, 2, 29, 12, 55, 42, 9); // '2016-02-29T12:55:42.000000009'

// obtain an instance of LocalDateTime from epoch seconds and a ZoneOffset
LocalDateTime.ofEpochSecond(0, ZoneOffset.UTC); // '1970-01-01T00:00'
LocalDateTime.ofInstant(Instant.now()); // current local date-time
LocalDateTime.ofInstant(Instant.now(), ZoneOffset.UTC); // current local UTC date-time

Get values from LocalDateTime

var dt = LocalDateTime.parse("2016-02-26T23:55:42.123");

dt.toString(); // '2016-02-26T23:55:42.123' ISO 8601 format

dt.year(); // 2016
dt.month(); // Month.FEBRUARY
dt.monthValue(); // 2
dt.dayOfMonth(); // 26
dt.hour(); // 23
dt.minute(); // 55
dt.second(); // 42
dt.nano(); // 123000000

dt.dayOfWeek(); // DayOfWeek.FRIDAY
dt.dayOfWeek().value(); // 5
dt.dayOfYear(); // 57

dt.toLocalDate().isLeapYear(); // true 2016 is a leap year

// obtain the date and time components of the LocalDateTime
dt.toLocalDate();
dt.toLocalTime();

// get range of month
dt.toLocalDate().lengthOfMonth(); // 29
dt.range(ChronoField.DAY_OF_MONTH); // ValueRange(1 - 29)

// get range of year
dt.toLocalDate().lengthOfYear(); // 366
dt.range(ChronoField.DAY_OF_YEAR); // ValueRange(1 - 366)

// get other date-based fields like the aligned week of year
dt.get(ChronoField.ALIGNED_WEEK_OF_YEAR); // 9

// get week of week-based year
dt.get(IsoFields.WEEK_OF_WEEK_BASED_YEAR); // 8
dt.toLocalDate().isoWeekOfWeekyear();

// get other time-based fields
dt.get(ChronoField.SECOND_OF_DAY); // 86142
dt.get(ChronoField.MILLI_OF_SECOND); // 123
dt.get(ChronoField.HOUR_OF_AMPM); // 11
// any other date or time-based ChronoField can be passed to `get`

Adding to and subtracting from a LocalDateTime instance

var dt = LocalDateTime.parse("2016-02-26T23:55:42.123");

// add/subtract 366 days
dt.plusDays(366); // '2017-02-26T23:55:42.123'
dt.minusDays(366); // '2015-02-25T23:55:42.123'

// add/subtract 12 months
dt.plusMonths(12); // '2017-02-26'
dt.minusMonths(12); // '2015-02-26'

// add/subtract 4 weeks
dt.plusWeeks(4); // '2016-03-25T23:55:42.123'
dt.minusWeeks(4); // '2016-01-29T23:55:42.123'

// add/subtract 1 year to the parsed LocalDate and returns a new instance
dt.plusYears(1); // '2017-02-26T23:55:42.123'
dt.minusYears(1); // '2015-02-26T23:55:42.123'

// add/subtract 30 years
dt.plus(3, ChronoUnit.DECADES); // '2046-02-26T23:55:42.123'
dt.minus(3, ChronoUnit.DECADES); // '1986-02-26T23:55:42.123'

// add subtract a Period of 3 Months and 3 Days
dt.plus(Period.ofMonths(3).plusDays(3)); // '2016-05-29T23:55:42.123'
dt.minus(Period.ofMonths(3).plusDays(3)); // '2015-11-23T23:55:42.123'

// add/subtract 12 hours
dt.plusHours(12); // '2016-02-27T11:55:42.123'
dt.minusHours(12); // '2016-02-26T11:55:42.123'

// add/subtract 30 minutes
dt.plusMinutes(30); // '2016-02-27T00:25:42.123'
dt.minusMinutes(30); // '2016-02-26T23:25:42.123'

// add/subtract 30 seconds
dt.plusSeconds(30); // '2016-02-26T23:56:12.123'
dt.minusSeconds(30); // '2016-02-26T23:55:12.123'

// add/subtract 1 million nanoseconds (1 millisecond)
dt.plusNanos(1000000); // '2016-02-26T23:55:42.124'
dt.minusNanos(1000000); // '2016-02-26T23:55:42.122'

// add/subtract a time-based unit
dt.plus(1, ChronoUnit.MILLIS); // '2016-02-26T23:55:42.124'
dt.plus(1, ChronoUnit.HALF_DAYS); // '2016-02-26T11:55:42.123'

// add/subtract a duration of 30 hours and 45 minutes
dt.plus(Duration.ofHours(30).plusMinutes(45)); // '2016-02-28T06:40:42.123'
dt.minus(Duration.ofHours(30).plusMinutes(45)); // '2016-02-25T17:10:42.123'

Alter specific fields of a LocalDateTime instance

var dt = LocalDateTime.parse("2016-02-26T23:55:42.123");

// set the hour of day to 1
dt.withHour(1); // '2016-02-26T01:55:42.123'

// set the minute of hour to 1
dt.withMinute(1); // '2016-02-26T23:01:42.123'

// set the second of minute to 1
dt.withSecond(1); // '2016-02-26T23:55:01.123'

// set the nanosecond of second to 1
dt.withNano(0); // '2016-02-26T23:55:42'

// set the millisecond of second to 51
dt.with(ChronoField.MILLI_OF_SECOND, 51); // '2016-02-26T23:55:42.051'

// set by a custom TemporalAdjuster that adjusts to the next even second
var nextEvenSecond = {
  adjustInto: function(t) {
    return t.second() % 2 === 0 ? t.plusSeconds(2) : t.plusSeconds(1);
  }
};
dt.with(nextEvenSecond); // '2016-02-26T23:55:44.123'
dt.plusSeconds(1).with(nextEvenSecond); // '2016-02-26T23:55:44.123'

Truncate a LocalDateTime instance

var dt = LocalDateTime.parse("2016-02-26T23:55:42.123");

dt.truncatedTo(ChronoUnit.SECONDS); // '2016-02-26T23:55:42'
dt.truncatedTo(ChronoUnit.MINUTES); // '2016-02-26T23:55:00'
dt.truncatedTo(ChronoUnit.HOURS); // '2016-02-26T23:00'
dt.truncatedTo(ChronoUnit.HALF_DAYS); // '2016-02-26T12:00'
dt.truncatedTo(ChronoUnit.DAYS); // '2016-02-26T00:00'

Compare LocalDateTime instances

var dt1 = LocalDateTime.parse("2016-02-26T23:55:42.123");
var dt2 = dt1.plusHours(2);

dt1.isAfter(dt2); // false
dt1.isBefore(dt2); // true

dt1.equals(dt1.plusHours(0)); // true
dt1.equals(dt1.plusHours(1)); // false

dt1.compareTo(dt1) === 0; // true
dt1.compareTo(dt2) < 0; // true
dt2.compareTo(dt1) > 0; // true

// Warn! hashCode is equal if in instances are equal, but might be equal for unequal instances as well
dt1.hashCode(); // -2036645668
dt2.hashCode(); // 1459191821
dt1.hashCode() !== dt2.hashCode(); // true

Distance between two LocalDateTime instances

var dt1 = LocalDateTime.parse("2016-02-26T23:55:42.123");
var dt2 = dt1
  .plusYears(6)
  .plusMonths(12)
  .plusHours(2)
  .plusMinutes(42)
  .plusSeconds(12);

// obtain the duration between the two dates
dt1.until(dt2, ChronoUnit.YEARS); // 7
dt1.until(dt2, ChronoUnit.MONTHS); // 84
dt1.until(dt2, ChronoUnit.WEEKS); // 356
dt1.until(dt2, ChronoUnit.DAYS); // 2557
dt1.until(dt2, ChronoUnit.HOURS); // 61370
dt1.until(dt2, ChronoUnit.MINUTES); // 3682242
dt1.until(dt2, ChronoUnit.SECONDS); // 220934532

Convert from a moment or JavaScript Date

// obtain a LocalDateTime instance from a JavaScript Date

// the manual way
var t = LocalDateTime.ofInstant(Instant.ofEpochMilli(new Date().getTime()));
// the recommended way with the JavaScript temporal
t = LocalDateTime.from(nativeJs(new Date()));
// converting from a moment works the same way
d = LocalDateTime.from(nativeJs(moment()));

ZonedDateTime

A ZonedDateTime represents a date-time with a time offset and/or a time zone in the ISO-8601 calendar system.

On its own, ZonedDateTime only supports specifying time offsets such as UTC or UTC+02:00, plus the SYSTEM time zone ID.

The SYSTEM zone ID

The SYSTEM zone ID is a non-standard ID that is specific to js-joda. It represents the default time zone of the current JavaScript runtime. The JavaScript spec does not provide an API this; it only provides the system default time offset for a point in the timeline (Date.prototype.getTimezoneOffset()).

You should not exchange ZonedDateTime instances using the SYSTEM zone ID between JavaScript environments (e.g. between server and client, or between two servers). The time offset on another machine won't necessarily be the same as yours. Before sending a ZonedDateTime to someone else, convert it to a fixed offset:

// current time with default `SYSTEM`
ZonedDateTime.now().toString(); // e.g. 2016-03-18T12:38:23.561+01:00[SYSTEM]

// converted to a fixed time offset
ZonedDateTime.now()
  .withFixedOffsetZone()
  .toString(); // e.g. 2016-03-18T12:38:23.561+01:00

Working with time zones

A time zone and a time offset are not the same thing. Some timezones change from standard time to daylight savings time and back every year:

  • In the Europe/Berlin time zone, the time offset is UTC+2 during the summer, and UTC+1 during the rest of the year.
  • In the Africa/Lagos time zone, on the other hand, the time offset is UTC+1 all year round.

Calculations that might span time zones or daylight savings transitions need to reference the time zone, not just the offset.

The @js-joda/timezone package provides bindings to the the IANA tz database, making joda-js's calculations time zone aware. The tz database uses zone names like Africa/Bujumbura, America/New_York, and Europe/Lisbon (see the full list).

To specify time zones using these names, you just need to require @js-joda/timezone.

var jsJoda = require("@js-joda/core");
require("@js-joda/timezone");

var zdt = ZonedDateTime.now(ZoneId.of("Europe/Paris"));

Create a ZonedDateTime

// get now with the default system time zone
ZonedDateTime.now().toString(); // e.g. 2016-03-18T12:38:23.561+01:00[SYSTEM]

// get now with the UTC time zone
ZonedDateTime.now(ZoneOffset.UTC).toString(); // e.g. 2016-03-18T11:38:23.561Z

// get now with a fixed offset time zone
ZonedDateTime.now(ZoneId.of("UTC-05:00")).toString(); // e.g. 2016-03-18T06:38:23.561-05:00[UTC-05:00]

// get now with a ZoneRegion (requires `@js-joda/timezone`)
ZonedDateTime.now(ZoneId.of("Europe/Paris")).toString(); // e.g. 2017-02-04T17:01:15.846+01:00[Europe/Paris]

// parse a date time with a time zone ISO String
ZonedDateTime.parse("2016-03-18T12:38:23.561+01:00[SYSTEM]");
ZonedDateTime.parse("2016-03-18T12:38:23.561+01:00");
ZonedDateTime.parse("2016-03-18T11:38:23.561Z");
ZonedDateTime.parse("2016-03-18T06:38:23.561-05:00[UTC-05:00]");
ZonedDateTime.parse("2017-02-04T17:01:15.846+01:00[Europe/Paris]");

// create from a LocalDate(Time) (requires `@js-joda/timezone`)
LocalDate.parse("2012-06-06")
  .atStartOfDay()
  .atZone(ZoneId.of("Europe/Paris")); // 2012-06-06T00:00+02:00[Europe/Paris]
ZonedDateTime.of(
  LocalDateTime.parse("2012-06-06T00:00"),
  ZoneId.of("Europe/Paris")
); // 2012-06-06T00:00+02:00[Europe/Paris]
ZonedDateTime.of(
  LocalDate.parse("2012-06-06"),
  LocalTime.MIDNIGHT,
  ZoneId.of("Europe/Paris")
); // 2012-06-06T00:00+02:00[Europe/Paris]

// create from an Instant
ZonedDateTime.ofInstant(Instant.now(), ZoneId.SYSTEM); // current system time

Switch time zones

These examples require @js-joda/timezone.

var d = LocalDate.of(2016, 3, 18);
var zdt = d.atTime(LocalTime.NOON).atZone(ZoneId.of("America/New_York")); //2016-03-18T12:00-04:00[America/New_York]

// switch time zone retaining the local date-time if possible
zdt.withZoneSameLocal(ZoneId.of("Europe/Berlin")); // 2016-03-18T12:00+01:00[Europe/Berlin]

// switch time zone and retain the instant
zdt.withZoneSameInstant(ZoneId.of("Europe/Berlin")); // 2016-03-18T17:00+01:00[Europe/Berlin]

Get and manipulate values from a ZonedDateTime

ZonedDateTime implements the same methods as LocalDateTime for getting or setting values. See the examples above for LocalDateTime.

Calculate values across daylight savings transitions

When adding to or subtracting from a ZonedDateTime instance, the calculation is different depending on whether date or time units are passed.

  • Addition/subtraction of date units are made on the local timeline.
  • Addition/subtraction of time units are made on the instant timeline.

This example shows the difference for a daylight saving transition.

// assume the system default time zone is CET; we define a time as 2016-03-18 at 17:00 local time
var zdt = ZonedDateTime.parse("2016-03-18T17:00+01:00[Europe/Berlin]");

// adding a date unit of 2 weeks, crossing a daylight saving transition
zdt.plusWeeks(2); // 2016-04-01T17:00+02:00[Europe/Berlin] (still 17:00)

// adding a time unit of 2 weeks (2 * 7 * 24)
zdt.plusHours(2 * 7 * 24); // 2016-04-01T18:00+02:00[Europe/Berlin] (now 18:00)

Period

Period is a date-based amount of time in the ISO-8601 calendar system, such as '2 years, 3 months and 4 days'.

// parse and format ISO 8601 period strings
Period.parse("P1Y10M").toString(); // 'P1Y10M'

// obtain a Period of 10 years, 5 month and 30 days
Period.of(10, 5, 30).toString(); // 'P10Y5M30D'

// 10 years
Period.ofYears(10).toString(); // 'P10Y'

// add 45 days to a Period
Period.ofYears(10)
  .plusDays(45)
  .toString(); // 'P10Y45D'

// normalize a Period of years and month
Period.of(1, 37, 0)
  .normalized()
  .toString(); // 'P4Y1M'

// add/subtract from a Period
Period.ofYears(10)
  .plusMonths(10)
  .minusDays(42)
  .toString(); // 'P10Y10M-42D'

// add a Period to LocalDate
var p = Period.ofMonths(1);
LocalDate.parse("2012-12-12").plus(p); // '2013-01-12';
LocalDate.parse("2012-01-31").plus(p); // '2012-02-29';
LocalDateTime.parse("2012-05-31T12:00").plus(p); // '2012-06-30T12:00';

// calculate the Period between two Dates
Period.between(LocalDate.parse("2012-06-30"), LocalDate.parse("2012-08-31")); // 'P2M1D'

Duration

Duration is a time-based amount of time, such as '34.5 seconds'.

// obtain a Duration of 10 hours
Duration.ofHours(10).toString(); // 'PT10H'

// obtain a Duration of 10 days (10 x 24 hours)
Duration.ofDays(10).toString(); // 'PT240H'

// add/subtract a duration from a LocalDateTime
var dt = LocalDateTime.parse("2012-12-24T12:00");

dt.plus(Duration.ofHours(10).plusMinutes(30)).toString(); // '2012-12-24T22:30'
dt.minus(Duration.ofHours(12).multipliedBy(10)).toString(); // '2012-12-19T12:00'

// calculate the duration between two time-based values
var dt1 = LocalDateTime.parse("2012-12-24T12:00");

Duration.between(dt1, dt1.plusHours(10)).toString(); // 'PT10H'

Formatting / Parsing

Formatting

To format a date and/or time, create a DateTimeFormatter and pass it to the .format method of a LocalDate, LocalTime, LocalDateTime, or ZonedDateTime instance.

js-joda built-in DateTimeFormatter parses and formats dates and times from /to ISO 8601 as specified in RFC 3339.

const d = LocalDateTime.parse('2018-04-28T12:34')

d.format(DateTimeFormatter.ofPattern('M/d/yyyy')) // 4/28/2018
d.format(DateTimeFormatter.ofPattern('HH:mm')) // 12:34

Formatting with locales

Non-numeric date and time formats need to know what language to use, for things like names of months and days of the week. For example, the format eeee (d MMMM) might return Saturday (28 April) in English, and samedi (28 avril) in French. If you try to use a locale-dependent format pattern without specifying the locale, you'll get this error message:

ERROR: Pattern using (localized) text not implemented, use @js-joda/locale plugin!

To specify a locale, you'll need to import the @js-joda/timezone and @js-joda/locale plugins. The simplest way to use @js-joda/locale is to install one of the locale-specific builds from npm:

You can then use localized format strings as follows.

import '@js-joda/timezone' // Just needs to be imported; registers itself automatically
import { Locale } from '@js-joda/locale_fr' // Get `Locale` from the prebuilt package of your choice
import { DateTimeFormatter, LocalDateTime } from 'js-joda'

const d = LocalDateTime.parse('2018-04-28T12:34')
const formatter = DateTimeFormatter
  .ofPattern('eeee (d MMMM)')
  .withLocale(Locale.FRANCE)
d.format(formatter) // samedi (28 avril)

Note: If internationalization is an important aspect of your application, you might consider using the standard Intl.DateTimeFormat API instead. It is built into modern browsers, supports a comprehensive set of locales, and handles a lot of internationalization subtleties that go beyond the scope of this library. To do this, you would need to convert your js-joda dates and times to JavaScript Date objects just before outputting them:

const ldt = LocalDateTime.parse('2018-04-28T12:34')

const jsDate = convert(ldt).toDate() // Convert to JavaScript `Date` object

var options = { 
  weekday: 'long', 
  year: 'numeric', 
  month: 'long', 
  day: 'numeric', 
  hour: 'numeric', 
  minute: '2-digit' 
}

jsDate.toLocaleDateString('en-US', options) // Saturday, April 28, 2018, 12:34 PM 
jsDate.toLocaleDateString('de-DE', options) // Samstag, 28. April 2018, 12:34
jsDate.toLocaleDateString('ar-EG', options) // السبت، ٢٨ أبريل ٢٠١٨ ١٢:٣٤ م
jsDate.toLocaleDateString('ko-KR', options) // 2018년 4월 28일 토요일 오후 12:34

Format patterns

Date and time formats are specified by date and time pattern strings using Java SimpleDateFormat codes.

Symbol Meaning Presentation Examples
G era number/text 1; 01; AD; Anno Domini
u year year 2004; 04
y year-of-era year 2004; 04
D day-of-year number 189
M month-of-year number/text 7; 07; Jul; July; J
d day-of-month number 10
Q quarter-of-year number/text 3; 03; Q3
Y week-based-year year 1996; 96
w week-of-year number 27
W week-of-month number 27
e localized day-of-week number 2; Tue; Tuesday; T
E day-of-week number/text 2; Tue; Tuesday; T
F week-of-month number 3
a am-pm-of-day text PM
h clock-hour-of-am-pm (1-12) number 12
K hour-of-am-pm (0-11) number 0
k clock-hour-of-am-pm (1-24) number 0
H hour-of-day (0-23) number 0
m minute-of-hour number 30
s second-of-minute number 55
S fraction-of-second fraction 978
A milli-of-day number 1234
n nano-of-second number 987654321
N nano-of-day number 1234000000
V time-zone ID zone-id America/Los_Angeles; Z; -08:30
z time-zone name zone-name Pacific Standard Time; PST
X zone-offset 'Z' for zero offset-X Z; -08; -0830; -08:30; -083015; -08:30:15;
x zone-offset offset-x +0000; -08; -0830; -08:30; -083015; -08:30:15;
Z zone-offset offset-Z +0000; -0800; -08:00;
p pad next pad modifier 1
' escape for text delimiter
'' single quote literal '
[ optional section start
] optional section end
{} reserved for future use

Parsing

Parsing is similar to formatting, the same DateTimeFormatter pattern are used as for formatting.

Customized parser can be build with the DateTimeFormatter.

Simple parser example

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

const formatter = DateTimeFormatter.ofPattern('M/d/yyyy');
const date = LocalDate.parse('4/28/2018', formatter);
console.log(date.toString()); // 2018-04-28

http date parser example

Example for an HTTP dates formatter as specified in RFC 7321, like returned by javascript native Date toUTCString method.

This formatter requires the @js-joda/locale package. This formatter is built-in since @js-joda/locale@4.2.0 -> RFC_1123_DATE_TIME

import { DateTimeFormatter, ZonedDateTime } from '@js-joda/core';
import '@js-joda/timezone'
import { Locale } from '@js-joda/locale';

const df = DateTimeFormatter.ofPattern('EEE, dd MMM yyyy HH:mm:ss z').withLocale(Locale.ENGLISH);
const z = ZonedDateTime.parse('Tue, 05 Oct 2021 17:08:24 GMT', df);
console.log(z.toString()); // 2021-10-05T17:08:24+01:00[GMT]

Built-in DateTimeFormatter

Formatter Example
ISO_LOCAL_DATE '2011-12-03'
ISO_LOCAL_TIME '10:15:30'
ISO_LOCAL_DATE_TIME '2011-12-03T10:15:30'
ISO_INSTANT '2011-12-03T10:15:30Z'
ISO_OFFSET_DATE_TIME '2011-12-03T10:15:30+01:00'
ISO_ZONED_DATE_TIME '2011-12-03T10:15:30+01:00[Europe/Paris]'
BASIC_ISO_DATE '20111203'
ISO_OFFSET_DATE '2011-12-03+01:00'
ISO_OFFSET_TIME '10:15:30+01:00'
ISO_ORDINAL_DATE '2012-337'
ISO_WEEK_DATE '2012-W48-6'
ISO_DATE '2011-12-03+01:00', '2011-12-03'
ISO_TIME '10:15:30+01:00', '10:15:30'
ISO_DATE_TIME '2011-12-03T10:15:30+01:00[Europe/Paris]'
RFC_1123_DATE_TIME 'Tue, 05 Oct 2021 17:08:24 GMT'
requires @js-joda/locale

Usage example for a built-in DateTimeFormatter

const localDate = LocalDate.parse('2012-12-12', DateTimeFormatter.ISO_LOCAL_DATE);
const dateAsString = LocalDate.now().format(DateTimeFormatter.ISO_LOCAL_DATE);

Hint

Whenever you stumble over a Cannot read property 'localeString' of null error, its probably because the locale of the formatter is not set.

In that case add the @js-joda/locale package to your project and set the locale of the formatter, eg DateTimeFormatter.ofPattern('eeee (d MMMM)').withLocale(Locale.FRANCE).

Using locale package

Motivation

Implementation of locale specific functionality for js-joda, providing function not implemented in js-joda core

Especially this implements patterns elements to print and parse locale specific dates.

Node

In a node environment, the best choice is to install @js-joda/locale and the cldr data cldr-data separately. With that you have support for all locales defined in the cldr project. The timezone package is required as soon as timezone locales come into play.

    npm install @js-joda/core
    npm install @js-joda/timezone
    npm install cldr-data
    npm install cldrjs
    npm install @js-joda/locale

Browser

In a browser environment, to save space, the better choice is to install one of the pre-built packages for a certain language. The pre-built packages contain the implementation and all cldr data required for that specific locale.

For more information check the README.md in the locale package.

    npm install @js-joda/locale_en
    npm install @js-joda/timezone

Usage

const {
    DateTimeFormatter,
    ZonedDateTime,
    ZoneId,
} = require('@js-joda/core');
require('@js-joda/timezone');

const {
    Locale,
} = require('@js-joda/locale_en-us');

var zdt = ZonedDateTime.of(2016, 1, 1, 0, 0, 0, 0, ZoneId.of('Europe/Berlin'));
console.log('en_US formatted string:', 
    zdt.format(
        DateTimeFormatter
            .ofPattern('eeee MMMM dd yyyy GGGG, hh:mm:ss a zzzz, \'Week \' ww, \'Quarter \' QQQ')
            .withLocale(Locale.US)));

this will output en_US formatted string: Friday January 01 2016 Anno Domini, 12:00:00 AM Central European Time, Week 01, Quarter Q1

Links

For more information check the README.md in the locale package.

Customizing js-joda

This package is extensible, allowing you to create your own custom temporal calculations. See the temporal interface documentation for more information.

Custom temporal adjuster

// implement a TemporalAdjuster that the next or same even day of month
var nextOrSameEvenDay = {
  adjustInto: function(t) {
    return t.dayOfMonth() % 2 === 0 ? t : t.plusDays(1);
  }
};

LocalDateTime.parse("2012-12-23T12:00").with(nextOrSameEvenDay); // '2012-12-24T12:00'
LocalDate.parse("2012-12-24").with(nextOrSameEvenDay); // '2012-12-24'

Custom temporal fields and temporal units

See the source for temporal/IsoFields as an example how to implement custom fields and units. IsoFields implements fields and units for an ISO week-based year.

Custom formatter and queries

The following example implements a parser for a local date with an optional local time. It returns either a LocalDate or a LocalDateTime, depending on the parsed fields.

// build a custom date time formatter where the time field is optional
var OPTIONAL_FORMATTER = DateTimeFormatter.ofPattern(
  'yyyy-MM-dd['T'HH:mm[:ss]]'
);

// create a temporal query that create a new Temporal depending on the existing fields
dateOrDateTimeQuery = {
  queryFrom: function(temporal) {
    var date = temporal.query(TemporalQueries.localDate());
    var time = temporal.query(TemporalQueries.localTime());
    if (time == null) return date;
    else return date.atTime(time);
  }
};

localDate = OPTIONAL_FORMATTER.parse('2012-12-24', dateOrDateTimeQuery);
localDateTime = OPTIONAL_FORMATTER.parse(
  '2012-12-24T23:59',
  dateOrDateTimeQuery
);

Convert from/ to native js

Convert from Date to a js-joda temporal

use method nativeJs to convert from native javascript Date to a js-joda Temporal.

Hint: nativeJs accepts moment objects as well.

Be aware of that a moment and Date always represent a timestamp (a certain point in time with a timezone). Therefore, the most reasonable convert is to an Instant or ZonedDateTime.

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

const instant = Instant.from(nativeJs(new Date()))

// or with momentjs object
const instant = Instant.from(nativeJs(moment()))

Convert from js-joda temporal to a Date

Use method convert to convert a LocalDate | LocalDateTime | ZonedDateTime | Instant to javascript Date.

const date = convert(ZonedDateTime.now()).toDate();