Feature | java.util.Date/Calendar | Joda-Time v2.9.6 | Java-8 (JSR-310) | Time4J v5.x | Date4J |
---|---|---|---|---|---|
Immutability of value types | no | yes (some mutable types exist) | yes | yes | yes |
Compile-time type-safety based on generics | no | no | no | yes | no |
Modular structure | no | no | no | yes | no |
Fluent programming style (method chaining) | no | yes | yes | yes | no |
Nominal precision | milliseconds | milliseconds | nanoseconds | nanoseconds | nanoseconds |
Magic numbers versus enums | magic numbers | magic numbers | enums | enums | enums |
Rejects null parameters by NPE | yes | no (indicating default) | yes | yes | no (later NPE?) |
Prevents implicit defaults for locale and zone | no | no | no | yes | yes |
Standard way how to create value types | constructor | constructor | static factory method | static factory method | constructor |
Extension mechanism for value types | inheritance | strategy pattern (limited) | strategy pattern | strategy pattern | no |
Low-level interfaces | no | yes | yes | yes | no |
Generic manipulation interface | - | {property-concept} | TemporalAdjuster |
ChronoOperator<T> |
- |
Field-based operators/adjusters | no | {property-concept} | no | yes | no |
Separated concepts for fields and units | no | yes | yes | yes | no |
Preferred field-access (example for int-value) | get(Calendar.DAY_OF_YEAR) | getDayOfYear() | getDayOfYear() | get(PlainDate.DAY_OF_YEAR) | getDayOfYear() |
low-level type of field/element-value | int | int | long | V (generic) | Integer |
Feature | java.util.Date/Calendar | Joda-Time v2.8.1 | Java-8 (JSR-310) | Time4J v3.2 | Date4J |
---|---|---|---|---|---|
Count of public classes in main package | 6 | 59 | 18 | 33 | 3 |
Total count of public classes | 14 | 145 | 69 | 174 | 3 |
Size of jar-file in kByte | (part of JDK) | 608 | 497 (threeten-bp) | core: 402 i18n: 255 olson: 77 misc: 56 range: 74 |
35 |
Method count in class files | (part of JDK) | 3873 | 2498 (threeten-bp) | core: 2410 i18n: 657 olson: 278 misc: 196 range: 352 |
206 |
Feature | java.util.Date/Calendar | Joda-Time v2.9.6 | Java-8 (JSR-310) | Time4J v5.x | Date4J |
---|---|---|---|---|---|
ISO-types representing points in time | java.util.Date java.util.Calendar java.util.GregorianCalendar XMLGregorianCalendar |
DateTime MutableDateTime Instant LocalDate LocalTime LocalDateTime DateMidnight (deprecated) YearMonthDay (deprecated) TimeOfDay (deprecated) YearMonth MonthDay |
ZonedDateTime Instant LocalDate LocalTime LocalDateTime OffsetDateTime OffsetTime Year YearMonth MonthDay |
Moment PlainDate PlainTime PlainTimestamp ZonalDateTime CalendarYear CalendarQuarter CalendarMonth CalendarWeek AnnualDate |
DateTime |
Duration types (without start or end) | javax.xml.datatype.Duration | Duration Period MutablePeriod Years Months Weeks Days Hours Minutes Seconds |
Duration Period |
Duration<IsoUnit> Duration<CalendarUnit> Duration<ClockUnit> MachineTime<TimeUnit> MachineTime<SI> Years Quarters Months Weeks |
- |
Interval types (anchored on timeline) | - | Interval MutableInterval |
- | MomentInterval DateInterval ClockInterval TimestampInterval CalendarPeriod SimpleInterval |
- |
Feature | java.util.Date/Calendar | Joda-Time v2.9.6 | Java-8 (JSR-310) | Time4J v5.0 | Date4J |
---|---|---|---|---|---|
Dedicated value type for calendar date | - | org.joda.time.LocalDate | java.time.LocalDate | net.time4j.PlainDate | - |
Replacement value type for calendar date | java.util.GregorianCalendar | not an issue | not an issue | not an issue | hirondelle.date4j.DateTime |
Count of supported fields/elements | 10 | 12 | 25 | 32 | 6 |
Count of built-in manipulations (estimated) |
~21 | ~130 | ~56 | ~200 | ~8 |
Proleptic gregorian calendar rules | no | yes | yes | yes | yes |
ISO week model | yes | yes | yes | yes | no |
Localized week model | yes | no | yes | yes | no |
Support for julian/epoch days | - | DateTimeUtils |
JulianFields |
EpochDays |
getModifiedJulianDayNumber() |
Historic centuries | no | yes | no | yes | no |
Support for gregorian/julian cutover | GregorianCalendar |
GJChronology |
- | ChronoHistory |
- |
Easter calculation | - | - | - | Eastern- or Western-style | - |
Support for historic anomalies | - | - | - | Swedish calendar Triennal julian leap years New Year rules Year definition (after/before New Year) Byzantine era Spanish era A.U.C. Roman numerals |
- |
Date difference in calendar days | no | yes | yes | yes | yes |
Date difference in years, months, days |
no | yes | yes | yes | no |
Date difference in arbitrary multiple calendar units |
no | yes1) | no | yes | no |
Prevents mixed signs in date difference | - | no2) | no2) | yes | not an issue |
Alternative day overflow strategy when adding months |
no | no | no | yes | yes |
1) Joda-Time offers the customizable class PeriodType
for controlling which units to be used.
2) Mixed signs like in P1M-30D are avoided per default but can be enforced by user construction.
Discussion of algorithm and implications see Time4J-API.
Feature | java.util.Date/Calendar | Joda-Time v2.9.6 | Java-8 (JSR-310) | Time4J v5.0 | Date4J |
---|---|---|---|---|---|
Dedicated value type for wall time | - | org.joda.time.LocalTime | java.time.LocalTime | net.time4j.PlainTime | - |
Replacement value type for wall time | java.util.GregorianCalendar | not an issue | not an issue | not an issue | hirondelle.date4j.DateTime |
Count of supported fields/elements | 6 | 11 | 15 | 21 | 4 |
Count of built-in manipulations (estimated) |
~26 | ~110 | ~38 | ~180 | ~12 |
Time difference in single clock unit |
no | yes | yes | yes | no1) |
Time difference in arbitrary multiple clock units |
no | yes | no2) | yes | no |
Support for dayperiods (morning, evening etc.) |
no | no | no | yes | no |
Support for 24:00 | no | no | no | yes | no |
Support for decimal hours/minutes | no | no | no | yes | no |
Support for collecting day cycles | no | no | no | yes | no |
Support for convenient truncation | no | yes | yes | yes | yes |
Other rounding features available | no | yes | no | yes | no |
1) Only seconds are supported.
2) java.time.Duration
consists of seconds and nanos only. All other units like the pair [hours+minutes] require user-defined workarounds.
Feature | java.util.Date/Calendar | Joda-Time v2.9.6 | Java-8 (JSR-310) | Time4J v5.0 | Date4J |
---|---|---|---|---|---|
Dedicated value type for moment/instant | - | org.joda.time.Instant | java.time.Instant | net.time4j.Moment | - |
Replacement value type for moment/instant | java.util.GregorianCalendar | org.joda.time.DateTime | java.time.ZonedDateTime java.time.OffsetDateTime |
net.time4j.ZonalDateTime | -1) |
Field queries via built-in timezone | yes (GregorianCalendar ) |
yes (DateTime ) |
yes (ZonedDateTime ) |
yes (ZonalDateTime ) |
no |
Field manipulations via built-in timezone | yes (GregorianCalendar ) |
yes (DateTime ) |
yes (ZonedDateTime ) |
no | no |
Field queries via timezone parameter | no | no | no | yes (Moment ) 2) |
no |
Field manipulations via timezone parameter | no | no | no | yes (Moment ) 3) |
DateTime.changeTimeZone() |
Translation to decimal elapsed seconds | no | no | no | yes | no |
Support for Julian days (as decimal value) | no | yes | no | yes 4) | no |
Time difference as machine time | no | yes (Duration ) |
yes (Duration ) |
yes (MachineTime ) |
no |
Support for UTC leap seconds | no | no | no | yes | no |
Multiple time scales | no | no | no 5) | yes 6) | no |
1) hirondelle.date4j.DateTime
is not a global timestamp. A timezone-dependent conversion to/from elapsed milliseconds since UNIX epoch is offered.
2) Most elements (fields) implement ZonalElement
which defines parameterized queries for any timezone.
3) Many element interfaces define various operators of type ElementOperator
which define further zonal parameterized operators.
4) Offered by the class net.time4j.calendar.astro.JulianDay
in the calendar-module.
5) The Java time scale was defined as UTC-SLS, but cannot be implemented this way due to lack of built-in leap-second-table. Hence the real behaviour is just like in POSIX.
6) POSIX, UTC, GPS, TAI, TT, UT
Feature | java.util.Date/Calendar | Joda-Time v2.9.6 | Java-8 (JSR-310) | Time4J v5.0 | Date4J |
---|---|---|---|---|---|
Injectable clock abstraction | - | DateTimeUtils.MillisProvider |
java.time.Clock |
net.time4j.base.TimeSource |
- |
Type of clock abstraction | - | interface | abstract class | interface | - |
Timezone-related clock available | no | no | yes (java.time.Clock ) |
yes (ZonalClock ) |
no |
Built-in fixed clock | no | no 1) | yes | yes 2) | no |
Adjustable by fixed offset/duration | no | no 1) | yes | yes 2) | no |
Pulsed clock (ticks in truncated mode) | no | no 1) | yes | yes 2) | no |
Monotonic clock | no | no | no | yes | no |
HTTP-clock (default port 80) | no | no | no | yes | no |
DAYTIME-clock (default port 13) | no | no | no | yes | no |
SNTP-clock (default port 123) | no | no | no | yes | no |
1) Users have to implement their own MillisProvider
-interface. Easy for fixed clocks, less easy for other clock variants.
2) Time4J does not try to unify all clock variants in one class but use the delegate pattern with different clock implementations.
Feature | java.util.Date/Calendar | Joda-Time v2.9.6 | Java-8 (JSR-310) | Time4J v5.0 | Date4J |
---|---|---|---|---|---|
Independent timezone repository | yes | yes | yes | yes | no |
Facade for any time zone provider | no | yes | yes | yes | no |
Support for IANA-TZDB | yes | yes | yes | yes | no |
How to update TZDB-repository? | TZ-Updater-Tool | extra zone compiler | TZ-Updater-Tool | extra zone compiler | - |
Support for MS-Windows zones | no | no | no | yes | no |
Support for military zones | no | no | no | yes | no |
Simultaneous access to alternative timezone data | no | no | no 1) | yes 2) | no |
Queries for offset (raw + dst) | yes | yes | yes | yes | no |
Queries for gaps/overlaps | no | gaps | gaps/overlaps | gaps/overlaps | no |
Access to timezone history | no | limited 3) | yes | yes | no |
Dump of timezone history | no | no | no | yes | no |
Default transition strategy for invalid local time | push forward | abort | push forward | push forward | - |
Alternative transition strategies 4) | no | no | limited 5) | yes | no |
Geographical timezone offset (in decimal seconds) | no | no | no | yes | no |
Enums for standard timezone identifiers | no | no | no | yes | no |
1) JSR-310 (java.time) requires the user to supply a special implementation of ZoneRulesProvider
and to register it. This provider might use tzids with different group-prefixes. However, there is no built-in alternative so this option remains abstract.
2) Time4J is able to refer to timezones based on java.util.TimeZone
and simultaneously to other within independent TZDB-repo. This is done just via prefixing of tzid.
3) Support is given by the navigation method DateTimeZone.nextTransition(instant)
.
4) This affects the conversion from local timestamps to global timestamps (instants).
5) The class ZonedDateTime
offers two special methods withEarlierOffsetAtOverlap()
and withLaterOffsetAtOverlap()
.
Feature | java.util.Date/Calendar | Joda-Time v2.9.6 | Java-8 (JSR-310) | Time4J v5.4 | Date4J |
---|---|---|---|---|---|
Main entry point of format engine | SimpleDateFormat |
DateTimeFormatter |
DateTimeFormatter |
ChronoFormatter |
DateTime |
Builder-based format engine | no | yes | yes | yes | no |
Facade for alternative format engines | no | no | no | yes | no |
Supported target types | java.util.Date |
DateTime LocalDate LocalTime LocalDateTime millis since Unix epoch ReadWritableInstant MutableDateTime |
TemporalAccessor 1) |
T extends ChronoEntity<T> ZonalDateTime other types via Converter |
DateTime |
Pattern-based formats | yes | yes | yes | yes | yes 2) |
Multiple format patterns | no | no | no | yes 3) | no |
Parser for multiple formats | no | no | no | yes 4) | no |
Parser for multiple types | no | yes 5) | yes 6) | no | no |
Support for java.util.Formatter |
yes | no | yes | yes | no |
Print to some kind of Appendable |
yes (StringBuffer ) |
yes (Appendable ) |
yes (Appendable ) |
yes (Appendable ) |
no |
Print with field/element-positions | yes 7) | no | no | yes | no |
Predefined format attributes | 6 | 4 | 6 | 29 | 1 |
Custom format attributes | no | no | no | yes | no |
Sectional format attributes | no | no | no | yes | no |
Support for time scales | no | no | no | yes | no |
Optional sections | no | yes (with multiple parsers) | yes ([] for parsing) | yes (for print+parse) | no |
Adjacent digit parsing | yes | yes | yes 8) | yes | no |
Informative parser errors with position info | yes | yes | yes | yes | no |
Custom chrono printers or parsers | no | yes | no | yes | no |
Parse with default field values | no | no 9) | yes | yes | no |
Parse with custom text resources | no | no 10) | yes | yes | no |
Support for alternative era names | no | no | no | yes | no |
Support for ordinal numbers | no | no | no | yes | no |
Support for padding | min-count of pattern symbols | min-count of pattern symbols min/max-args in builder-methods |
min-count of pattern symbols min/max-args in builder-methods padNext() |
min-count of pattern symbols min/max-args in builder-methods padNext() padPrevious() pad-char-attribute |
no |
Support for ISO-duration-format (print+parse) | no | yes | no | yes | no |
Custom parseable duration format | no | yes (PeriodFormatterBuilder ) |
no | yes (Duration.Formatter ) |
no |
Support for relative times ("5 days ago") | no | no | no | yes | no |
Support for ("yesterday/today/tomorrow)") | no | no | no | yes | no |
Support for ("next Monday/last Sunday)") | no | no | no | yes | no |
Localized representations of duration | no | 14 languages | no | 90 languages | no |
Short or narrow unit names (i18n) | no | no | no | yes | no |
Support for plural rules in time units | no | yes (via regular expressions) | no | yes (no user-action required) | no |
1) JSR-310-formatters do not parse finished results but an intermediate result which can then be evaluated in static from()
-methods of real target types.
2) Date4J has only very limited parsing capabilities and cannot understand arbitrary pattern-based formats during parsing.
3) CLDR, CLDR_24, CLDR_DATE, DYNAMIC, SIMPLE_DATE_FORMAT, THREETEN (v4.x)
4) either via MultiFormatParser
or via or-operator "|" in patterns
5) using methods like parseDateTime()
, parseLocalDate()
etc.
6) by help of parseBest()
, forces the user to apply instanceOf-patterns
7) Based on AttributedCharacterIterator
-API.
8) Fractional seconds (before Java-9) and localized fields (i.e. week-of-year - before Java-9) do not take part into the protocol of adjacent digit parsing.
9) As exception: Default years are supported, however.
10) As exception: Custom timezone names are supported, however.
Feature | java.util.Date/Calendar | Joda-Time v2.9.6 | Java-8 (JSR-310) | Time4J v5.4 | Date4J |
---|---|---|---|---|---|
Independent i18n-resources | {part of Java} | no | {part of Java} 1) | yes | no |
Count of supported languages | ~46 | {delegate to Java} | ~46 | ~94 (plus Java-resources as fallback) |
{delegate to Java} |
Localized week definitions | yes | no | yes | yes | no |
Standalone forms for months etc. available | yes | no | yes | yes | no |
Parsing localized timezone names | yes | no | yes | yes | no |
Localized date/time-patterns | yes | {delegate to Java} | yes | yes | no |
1) The backport threetenbp has no own i18n-resources.
Feature | java.util.Date/Calendar | Joda-Time v2.9.6 | Java-8 (JSR-310) | Time4J v5.0 | Date4J |
---|---|---|---|---|---|
Moment/Instant-intervals | no | yes | no | yes | no |
Calendar date intervals | no | no | no | yes | no |
Clock time intervals | no | no | no | yes | no |
Plain timestamp intervals | no | no | no | yes | no |
Queries for duration | no | yes | no | yes | no |
Support for after/before/start/end-queries | no | yes | no | yes | no |
Support for gap-queries | no | yes | no | yes | no |
Support for intersect/overlap-queries | no | yes | no | yes | no |
Support for abut/meet-queries | no | yes | no | yes | no |
Support for Allen's interval algebra | no | no | no | yes | no |
Standard ISO-8601-format 1) | no | yes | no | yes | no |
Parsing general ISO-8601-formats 2) | no | no | no | yes | no |
Custom interval pattern formats | no | no | no | yes | no |
Support for open/closed-boundaries | no | no 3) | no | yes | no |
Support for infinite boundaries | no | no | no | yes | no |
Interval search tree | no | no | no | yes | no |
Algebra of single intervals | no | gap/overlap | no | collapse/move/toCanonical | no |
Algebra of interval collections | no | no | no | plus/minus/union/intersect/xor
withBlocks/withComplement/withGaps withIntersection/withSplits/withTimeWindow |
no |
Streaming (only on Java-8) | no | no | no | yes | no |
Day partitions | no | no | no | yes | no |
Interface for holidays | no | no | no | yes | no |
Recurrent intervals (ISO) | no | no | no | yes | no |
1) Defined as: yyyy-MM-dd'T'HH:mm:ss.SSSXXX/yyyy-MM-dd'T'HH:mm:ss.SSSXXX
2) Example: yyyy-DDD'T'HH:mmXXX/'T'HH:mm (higher-order-elements and offset left out on end component)
3) Only half-open intervals are supported.
java.util.Date/Calendar | Joda-Time v2.9.6 | Java-8 (JSR-310) | Time4J v5.4 | Date4J |
---|---|---|---|---|
ThaiBuddhist (only valid after 1940)
Japanese (since Meji era) |
Gregorian/Julian-Cutover
Julian (proleptic) Islamic (4 leap year patterns - no i18n) ThaiBuddhist (only valid after 1940) Coptic (no i18n) Ethiopic (no i18n) |
Islamic-Umalqura (Saudi-Arabia)
Japanese (since Meji era) Minguo (Taiwan) ThaiBuddhist (only valid after 1940) |
Badi (Bahai)
Chinese (since 1645) Coptic Dangi (old Korean) Ethiopic (incl. time and tabots) French revolutionary Hebrew (incl. time) Historic Christian Indian national (Saka) Islamic (8 leap year patterns + ICU4J-data) Islamic-Umalqura (Saudi-Arabia) Islamic-Diyanet (Turkey) Japanese (since midage) Juche (North Korea) Julian (proleptic) Minguo (Taiwan) Persian (3000 years) ThaiBuddhist (+ Rattanakosin era) Vietnamese |
- |
Feature | java.util.Date/Calendar | Joda-Time v2.9.6 | Java-8 (JSR-310) | Time4J v5.x | Date4J |
---|---|---|---|---|---|
Support for elapsed millis since UNIX epoch | yes | yes | yes | yes | yes |
Conversion to/from java.time (Java-8) |
yes | no | {not an issue} | yes | no |
Conversion to/from java.util.Date |
{not an issue} | yes | yes 1) | yes | no |
Conversion to/fromjava.sql.Date/Time/Timestamp |
{part of Java 6} | via other libraries like Hibernate, Jadira-Usertype etc. |
built into sql classes | bridge class JDBCAdapter |
only as String |
Support for JPA | yes (built-in) | @Convert-annotation 2) | @Convert-annotation 2) | @Convert-annotation 2) | @Convert-annotation 2) |
Conversion to/from XMLGregorianCalendar |
{part of Java 6} | no | no | yes | no |
1) Support for the conversion between old Java and JSR-310 was intentionally restricted to the old-Java-side where such conversion methods exist.
2) The converter mechanism requires at least JPA 2.1.
Feature | java.util.Date/Calendar | Joda-Time-Android v2.9.3 | ThreeTenABP v1.0.5 | Time4A v3.19-2016d | Date4J |
---|---|---|---|---|---|
Management of app resources | {part of Android-API} | assets in AAR-library | assets in AAR-library | assets in AAR-library | {not relevant} |
APK-method-count (theoretical upper bound) | {part of Android-API} | 4756 | 2831 | 6243 | {not relevant < 10?} |
APK-method-count (with Proguard 1)) | {part of Android-API} | ~1400 | ~1100 | ~2800 | {not relevant < 10?} |
Independent timezone data | {part of Android-API} | yes | yes | yes | no |
Access to Android timezone data with own API | {part of Android-API} | no 2) | no 2) | yes 3) | no 2) |
Tracking system-tz-updates | {part of Android-API} | yes | yes | yes | no |
Independent i18n-resources | {part of Android-API} | no | no 4) | yes | no |
Real-time access of system clock | {part of Android-API} | no | no | yes 5) | no |
Sensible for user preference of 12/24-hour-format | {part of Android-API} | no | no | yes | no |
Prefetch resources in the background | no | no | no | yes | no |
1) The real APK-method-count depends on the use-case. Given numbers refer to the typical use-case of formatting the current local time.
2) Users have to directly use java.util.TimeZone
which has a very different API.
3) Time4J either offers SystemClock.inPlatformView()
or prefixing of timezone identifiers (with "java.util.TimeZone~").
4) Threeten-BP is not a perfect backport of JSR-310 since latter one refers to resources only available on Java-8, for example the umalqura-variant of the islamic calendar defined in JSR-310 is not supported on Android.
5) The monotonic clock of Time4A automatically chooses elapsedRealtime()
.
API/library | Today in default timezone and minutes | ISO-week-date [2015-W52-5] | What is next Wednesday at 17:45 in Paris? |
---|---|---|---|
java.util.Date/Calendar | GregorianCalendar gcal = new GregorianCalendar(); int year = gcal.get(Calendar.YEAR); int month = gcal.get(Calendar.MONTH)+1; int day = gcal.get(Calendar.DAY_OF_MONTH); int hour = gcal.get(Calendar.HOUR_OF_DAY); gcal.set(year,month-1,day,hour,0,0); gcal.set(Calendar.MILLISECOND,0); |
GregorianCalendar gcal = new GregorianCalendar(2000,1,1); gcal.setWeekDate(2015,52,5); |
{complex workaround}1) |
Joda-Time v2.9.1 | LocalDateTime now = LocalDateTime.now(); now = now.minuteOfHour().roundFloorCopy(); |
LocalDate d= new LocalDate(); d = d.weekyear().setCopy(2015); d = d.weekOfWeekyear().setCopy(52); d = d.withDayOfWeek(5); |
{complex workaround}1) |
Java-8 (JSR-310) | import static java.time.temporal.ChronoUnit.*; LocalDateTime now = LocalDateTime.now(); now = now.truncatedTo(MINUTES); |
import static java.time.temporal.IsoFields.*; import static java.time.DayOfWeek.*; LocalDate d = LocalDate.of(2000,1,1); d = d.with(WEEK_BASED_YEAR,2015); d = d.with(WEEK_OF_WEEK_BASED_YEAR,52); d = d.with(FRIDAY); |
import static java.time.temporal.TemporalAdjusters.*; import static java.time.DayOfWeek.*; LocalDate d = LocalDate.now().with(next(WEDNESDAY)); LocalDateTime ldt = ldt.atTime(17, 45); ZonedDateTime zdt = ldt.atZone(ZoneId.of("Europe/Paris")); LocalDateTime result = zdt.toLocalDateTime(); |
Time4J v3.11/4.8 | import static net.time4j.PlainTime.*; import static net.time4j.ClockUnit.*; PlainTimestamp now = SystemClock.inLocalView().now(); now = now.with(PRECISION,MINUTES); |
import static net.time4j.Weekday.*; PlainDate d= PlainDate.of(2015,52,FRIDAY); |
import static net.time4j.PlainDate.*; import static net.time4j.Weekday.*; PlainDate d = SystemClock.inLocalView().today(); d = d.with(DAY_OF_WEEK.setToNext(WEDNESDAY)); PlainTimestamp tsp = d.atTime(17, 45); Moment m = tsp.inStdTimezone(); tsp = m.toZonalTimestamp(EUROPE.PARIS); |
Date4J | DateTime today = DateTime.now(TimeZone.getDefault()); today = today.truncate(DateTime.Unit.MINUTE); |
{complex workaround}2) | {complex workaround}1) |
1) No built-in solution for changing to next Wednesday
2) No support for ISO-weekdates