3

We're generating an ICS file from Java code, and initially entered the times in UTC format.

We found that when creating a recurring appointment, with a DST change between the first and last date, some meetings are created 1 hour before or after the correct time.

We've done testings, and found that if we don't include the full definition of the timezone time changes it's not possible to make it correctly work. For example, for a meeting in Boston, the following definition works:

BEGIN:VTIMEZONE
TZID:Eastern Time (US & Canada)
BEGIN:STANDARD
DTSTART:16011104T020000
RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=11
TZOFFSETFROM:-0400
TZOFFSETTO:-0500
END:STANDARD
BEGIN:DAYLIGHT
DTSTART:16010311T020000
RRULE:FREQ=YEARLY;BYDAY=2SU;BYMONTH=3
TZOFFSETFROM:-0500
TZOFFSETTO:-0400
END:DAYLIGHT
END:VTIMEZONE

Is there a way to get this information from any existing Web Service or Web site? or do we need to maintain the definitions of all the application involved countries?.

We've found the existence of the ICal4j library, but it seems to provide methods to generate the structure of the .ics file, but not the information of the timezones as we need.

Anyone knows an easier way to make an .ics file to work correctly in different timezones, with recurrent appointments when there's a time change between the first and last dates?.

Darin Dimitrov
  • 1,023,142
  • 271
  • 3,287
  • 2,928

1 Answers1

0

I know I'm late on this, but just wanted to provide a solution with iCal4j for anyone coming up on this issue.

Indeed time zones are not implicitly managed in iCalendar standard, you must explicitly add the VTIMEZONE definition in your ICS file (RFC). You cannot just use the time zone identifier (TZID ).

You can refer to this answer for an explanation about why time zone information is needed.

So here is an example using iCal4j :

// get timezone
final TimeZoneRegistry registry = TimeZoneRegistryFactory.getInstance().createRegistry();
final TimeZone timezone = registry.getTimeZone("Europe/Zurich");
final VTimeZone tz = timezone.getVTimeZone();

// event start date
final java.util.Calendar startDate = new GregorianCalendar();
startDate.setTimeZone(timezone);
startDate.set(2016, java.util.Calendar.MARCH, 8, 9, 0, 0);

// event end date
final java.util.Calendar endDate = new GregorianCalendar();
endDate.setTimeZone(timezone);
endDate.set(2016, java.util.Calendar.MARCH, 8, 17, 0, 0);

// create event
final DateTime start = new DateTime(startDate.getTime(), timezone);
final DateTime end = new DateTime(endDate.getTime(), timezone);
final VEvent meeting = new VEvent(start, end, "Test");
meeting.getProperties().add(new RandomUidGenerator().generateUid());
meeting.getProperties().add(new Location("Somewhere"));

// create calendar
final net.fortuna.ical4j.model.Calendar icsCalendar = new net.fortuna.ical4j.model.Calendar();
icsCalendar.getProperties().add(new ProdId("-//Events Calendar//iCal4j 1.0//EN"));
icsCalendar.getProperties().add(CalScale.GREGORIAN);
icsCalendar.getProperties().add(Version.VERSION_2_0);

// add the timezone definition <- this is what you are missing
icsCalendar.getComponents().add(tz);

// add event to calendar
icsCalendar.getComponents().add(meeting);

The important line in the one which adds the VTimeZone component to the Calendar. So you don't need to get the definition from a web service.

You can refer the iCal4j documentation.

Then you should end-up with something like this (for the Europe/Zurich time zone) :

BEGIN:VCALENDAR
PRODID:-//Events Calendar//iCal4j 1.0//EN
CALSCALE:GREGORIAN
VERSION:2.0

BEGIN:VEVENT
DTSTAMP:20160307T231014Z
DTSTART;TZID=Europe/Zurich:20160308T090000
DTEND;TZID=Europe/Zurich:20160308T170000
SUMMARY:Test
TZID:Europe/Zurich
UID:45c269ed-dbc0-435e-bb3d-ba152dfbf0db
LOCATION:Somewhere
END:VEVENT

BEGIN:VTIMEZONE
TZID:Europe/Zurich
LAST-MODIFIED:20201011T015911Z
TZURL:http://tzurl.org/zoneinfo/Europe/Zurich
X-LIC-LOCATION:Europe/Zurich
X-PROLEPTIC-TZNAME:LMT
BEGIN:STANDARD
TZNAME:BMT
TZOFFSETFROM:+003408
TZOFFSETTO:+002946
DTSTART:18530716T000000
END:STANDARD
BEGIN:STANDARD
TZNAME:CET
TZOFFSETFROM:+002946
TZOFFSETTO:+0100
DTSTART:18940601T000000
END:STANDARD
BEGIN:DAYLIGHT
TZNAME:CEST
TZOFFSETFROM:+0100
TZOFFSETTO:+0200
DTSTART:19410505T010000
RRULE:FREQ=YEARLY;UNTIL=19420504T000000Z;BYMONTH=5;BYDAY=1MO
END:DAYLIGHT
BEGIN:STANDARD
TZNAME:CET
TZOFFSETFROM:+0200
TZOFFSETTO:+0100
DTSTART:19411006T020000
RRULE:FREQ=YEARLY;UNTIL=19421005T000000Z;BYMONTH=10;BYDAY=1MO
END:STANDARD
BEGIN:DAYLIGHT
TZNAME:CEST
TZOFFSETFROM:+0100
TZOFFSETTO:+0200
DTSTART:19810329T020000
RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU
END:DAYLIGHT
BEGIN:STANDARD
TZNAME:CET
TZOFFSETFROM:+0200
TZOFFSETTO:+0100
DTSTART:19810927T030000
RRULE:FREQ=YEARLY;UNTIL=19950924T010000Z;BYMONTH=9;BYDAY=-1SU
END:STANDARD
BEGIN:STANDARD
TZNAME:CET
TZOFFSETFROM:+0200
TZOFFSETTO:+0100
DTSTART:19961027T030000
RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU
END:STANDARD
END:VTIMEZONE

END:VCALENDAR
Yann39
  • 14,285
  • 11
  • 56
  • 84