iCal4j 4 and the new Java date/time API
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.