Intro To iCal4J

Introduction

Ben Fortuna\’s iCal4J library is a well-established Java implementation
of the RFC 5545 international standard for calendar events. In this
article I will show you how to use iCal4J to create a iCalendar event in
Java and export it to a .ics file, and how to read in a .ics file and
extract the useful information out of it. ICalendar files can be
imported into the popular calendar applications including Microsoft
Outlook™ and Google Calendar™.

Set up your iCal4J Properties File

Put the file in your Java resources directory and call it
ical4j.properties. Add the following key-value pairs:

net.fortuna.ical4j.timezone.cache.impl=net.fortuna.ical4j.util.MapTimeZoneCache

Generating a .ics File

Note: iCal4J has several classes which have the same name as date and
time classes in the Java standard library, such as Calendar, Date,
TimeZone, and Duration. For clarity, where I reference Java standard
library date and time classes, in order to distinguish them from iCal4J
I have used fully-qualified class names, eg java.time.Duration.

First we will create an empty iCalendar, and then we will start to add
different types of events to it.

//Create an iCal4J Calendar
Calendar c = new Calendar();
//"EN" is the language.
c.getProperties().add(new ProdId("-//Company Name//Product name and version//EN"));
c.getProperties().add(Version.VERSION_2_0);
c.getProperties().add(CalScale.GREGORIAN);

//Add an event to the calendar
c.getComponents().add(/* next you will create an event to add here */ null );
String icsExport = c.toString();

If you take a look at the contents of the icsExport String in your
debugger (or print it to the console) you will see an iCalendar file.
You may find it to be more readable if you copy it into a text editor.

Now we will create a simple, single event (non-recurring) which starts
on 5th March 2021 at midday Australian Eastern Daylight Savings Time,
and lasts for one hour.

private VEvent oneHourSingleEvent() {
    //Create a new calendar event that starts on 5th March 2021 at midday Australian Eastern Daylight Savings Time and goes for 1 hour.
    VEvent vEvent = new VEvent();
    PropertyList<Property> eventProps = vEvent.getProperties();
    java.time.ZonedDateTime now = java.time.ZonedDateTime.now();

    //Create a unique ID for the event
    String uidTimestamp = java.time.format.DateTimeFormatter
                            .ofPattern("uuuuMMdd'T'hhmmssXX")
                            .format(now);
    //In the real world this could be a number from a generated sequence:
    String uidSequence = "/" + (int) Math.ceil(Math.random() * 1000);
    String uidDomain = "@your.domain.com";
    eventProps.add(new Uid(uidTimestamp + uidSequence + uidDomain));

    //Add a start datetime and a duration
    TimeZoneRegistry tzReg = TimeZoneRegistryFactory.getInstance().createRegistry();
    TimeZone timezone = tzReg.getTimeZone("Australia/Sydney");
    try {
        eventProps.add(new DtStart(new DateTime("20210305T120000", timezone)));
    } catch (ParseException e) {
        e.printStackTrace();
    }
    eventProps.add(new Duration(java.time.Duration.ofHours(1)));

    //Add title and description
    eventProps.add(new Summary("The title of the event"));
    eventProps.add(new Description("Some longer description of the event."));
    return vEvent;
}

Next, add this event to the calendar, run it and view the result.

    c.getComponents().add(oneHourSingleEvent());

It should look something like the iCalendar below. Note that each line
terminates with a carriage return and a new line character, \r\n. If
you copy+paste the example below for use as a test, make sure to include
the correct line terminators or it may be invalid!

BEGIN:VCALENDAR
PRODID:-//Your Company Name//Your product name and version//EN
VERSION:2.0
CALSCALE:GREGORIAN
BEGIN:VEVENT
DTSTAMP:20210305T061107Z
UID:20210305T051107+1100/633@your.domain.com
DTSTART;TZID=Australia/Sydney:20210305T120000
DURATION:PT1H
SUMMARY:The title of the event
DESCRIPTION:Some longer description of the event.
END:VEVENT
END:VCALENDAR

Try saving the event in a text file with a .ics extension, and importing
it into your favourite calendar application.

Parsing a .ics file

Parsing a .ics file is very simple. The CalendarBuilder takes any object
which implements Reader, which could be a FileReader or, as in the
example below, a StringReader. You can use the code that you\’ve already
written so far in this tutorial to inject a valid serialised iCalendar
string, so you don\’t need to worry about getting the line terminators
right (remember \r\n!). The example parses the calendar, validates it,
and pulls out the useful attributes that we had added earlier, so that
you have an idea of how it works.

public Calendar importCalendar(String calendarSerialized) throws Exception {
    Calendar c = new CalendarBuilder().build(new StringReader(calendarSerialized));
    c.validate();
    VEvent event = (VEvent) c.getComponent(Component.VEVENT);
    DtStamp dtStamp = event.getDateStamp();
    Uid uid = event.getUid();
    DtStart startDate = event.getStartDate();
    String tz = startDate.getTimeZone().getVTimeZone().getTimeZoneId().getValue();
    PropertyList props = event.getProperties();
    Duration d1 = props.getProperty(Property.DURATION);//Any property can be retrieved this way.
    Duration d2 = event.getDuration();//d2 is the same as d1.
    Summary summary = event.getSummary();
    Description description = event.getDescription();
    return c;
}

Conclusion

There you have it! These examples should be enough to get you started
with the iCal4J library and give you and understanding of how it handles
iCalendars. If you need more examples, clone the project\’s Git
repository and take a look at the test cases, which are a rich source of
knowledge.

Further Reading

iCal4J: https://github.com/ical4j/ical4j

Java: https://docs.oracle.com/en/java/javase/11/docs/api/index.html

Acknowledgements

The author acknowledges the traditional custodians of the Daruk and the
Eora People and pays respect to the Elders past and present.

Oracle® and Java® are registered trademarks of Oracle and/or its
affiliates.

Other names may be trademarks of their respective owners.