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.

Dates

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.

Formats

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.

TemporalAdapter

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.

DateList

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.

Period

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.

PeriodList

To provide consistency of rules across multiple periods the [PeriodList] class has been enhanced to support these changes to temporal types.

Date properties

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.

Timezones

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.

Challenges

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.

Zone identifiers

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.

Specifically, the 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.

ZoneRulesProvider

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.ServiceLoader to 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.

TimeZoneRegistry

To manage the challenges with ZoneId and ZoneRulesProvider we are refining the TimeZoneRegistry implementation to support registration of 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 VTIMEZONE definitions.