iCal4j 4 will introduce some of the biggest changes since creation of the library, based on support for the new Java date/time api. This includes a number of API changes and inevitably some change to behaviour, hopefully for the better.
The most notable change is the deprecation of custom date and date-time classes in favour of implementations of the
java.time.Temporal interface. Whilst the ical4j Date class has an direct replacement in
java.time.LocalDate, there are now multiple
types representing date-time values, previously supported by the DateTime class.
Previously date formatting was encapsulated by the implementation in Date and DateTime classes. As Temporal types don’t implement
formatting we have introducted a
CalendarDateFormat class to support parsing and formatting of temporal string values.
Many date-based iCalendar properties also support additional information not captured by temporal types, such as timezone rules and support for “floating” date-time values that use the local timezone when calculating recurrences, etc.
To support consistent application of timezone rules the
TemporalAdapter class provides additional temporal operations through
encapsulation of a temporal instance. For example, the introduction of
Temporal support means that string parsing/formatting is no longer
encapsulated by the date/time object. In order to support string manipulation
TemporalAdapter includes such related functions.
Some iCalendar properties support a list of date/time values, and so the DateList class has been enhanced to provide consistency of rules across an entire temporal list.
A period of time is represented by either two dates or a date and a duration. A Period encapsulates both the formatting functions
and operations across both temporal and duration types. NOTE: The ical4j Period is not to be confused with the Java
Period which is
a representation of a time interval in days, months, etc.
To provide consistency of rules across multiple periods the [PeriodList] class has been enhanced to support these changes to temporal types.
In addition to parsing and formatting string values, in older versions of ical4j an iCalendar property with a date/date-time value would
also encapsulate timezone rules for date-time values. Date properties will no longer directly include timezone rules, but rather will derive
appropriate rules from the
TzId parameter value.
The new Java date/time API also introduces a new mechanism for defining timezone rules and identifiers. The old
java.util.Timezone implementation encapsulates both zone rules and identifier, however with the new API the identifer is defined by a
ZoneId and zone rules provided via
ZoneRulesProvider implementations that generate rules for zone identifiers.
Whilst most of the requirements of the iCalendar specification can be mapped to the new Java date/time API there are some challenges with how the API has been implemented.
The new Java date/time API includes an abstract class
java.time.zome.ZoneId of which there are two subclasses that represent timezone identifiers. The implementation does not allow further subclassing, which means we are required to use these two implementations and conform to the rules applied to them.
ZoneRegion subclass requires that all identifiers consist of characters in the set of
[a-zA-Z0-9/~._+-] and does not
begin with characters in the set of
[0-9/~._+-]. Notably this means that identifiers cannot include spaces or commas, which excludes
quite a few TZID values generated by Microsoft Outlook (e.g.
Canberra, Melbourne, Sydney). It also excludes the iCalendar specification
of globally unique timezones that begin with a forward slash (
To work around this incompatibility we can either encode the timezone identifier (e.g. replace spaces with
+, prefix with
ical4j~, etc.), or alternatively generate an entirely new unique string that we internally map to the
TZID value. The latter approach will likely be the best solution due to further challenges with
ZoneRulesProvider implementations below.
The new date/time API provides support for registering new timezone rules via the
ZoneRulesProvider service interface. A number of challenges are presented by this implementation, including:
- Use of
java.util.ServiceLoaderto load implementations. This is potentially a challenge for Android/OSGi compatibility.
- All zone identifiers must be known when registering the implementation. This means that we cannot add additional timezone definitions loaded from parsed calendar objects, but rather we need to register a new provider instance for each calendar object.
- A zone identifer cannot be registered twice, so multiple providers must provide a mutually exclusive set of zone identifiers.
To manage the challenges with
ZoneRulesProvider we are refining the TimeZoneRegistry implementation to support
ZoneId-compatible identifiers. The new implementation will manage mapping the
TZID value to a unique identifier that
conforms to the requirements of the new date/time API.
Each parser calendar object will manage it’s own TimeZoneRegistry so that it does not have conflicts with other calendar objects. Global zone rules will also be provided for calendar objects that use
TZID values but don’t include