A Java library for parsing and building iCalendar data models

Overview

iCal4j - iCalendar parser and object model

Table of Contents

  1. Introduction - What is iCal4j?
  2. Setup - Download and installation of iCal4j
  3. Usage - The iCal4j object model and how to use it
  4. Reference - Specification
  5. Configuration options
  6. Limitations - CUA compatibility, etc.
  7. Development - Guide for contributing to the iCalj project

Introduction

iCal4j is a Java library used to read and write iCalendar data streams as defined in RFC2445. The iCalendar standard provides a common data format used to store information about calendar-specific data such as events, appointments, to-do lists, etc. All of the popular calendaring tools, such as Lotus Notes, Outlook and Apple's iCal also support the iCalendar standard.

  • For a concise description of the goals and directions of iCal4j please take a look at the open issues.

  • You will find examples of how to use iCal4j in the wiki and throughout the API documentation.

  • Detailed descriptions of changes included in each release may be found in the CHANGELOG.

  • iCal4j was created with the help of Open Source software.

Setup

System requirements

  • Version 3.x - Java 8 or later
  • Version 2.x - Java 7 or later

Dependencies

In the interests of portability and compatibility with as many environments as possible, the number of dependent libraries for iCal4j is kept to a minimum. The following describes the required (and optional) dependencies and the functionality they provide.

  • slf4j-api [required] - A logging meta-library with integration to different logging framework implementations. Used in all classes that require logging.

  • commons-lang3 [required] - Provides enhancements to the standard Java library, including support for custom equals() and hashcode() implementations. Used in all classes requiring custom equality implementations.

  • commons-collections4 [required] - Provides enhancements to the standard Java collections API, including support for closures. Used in net.fortuna.ical4j.validate.Validator implementations to reduce the duplication of code in validity checks.

  • javax.cache.cache-api [optional*] - Supports caching timzeone definitions. * NOTE: when not included you must set a value for the net.fortuna.ical4j.timezone.cache.impl configuration

  • commons-codec [optional] - Provides support for encoding and decoding binary data in text form. Used in net.fortuna.ical4j.model.property.Attach

  • groovy-all [optional] - The runtime for the Groovy language. Required for library enhancements such as iCalendar object construction using the net.fortuna.ical4j.model.ContentBuilder DSL. This library is optional for all non-Groovy features of iCal4j.

  • bndlib [optional] - A tool for generating OSGi library metadata and packaging OSGi bundles. This library is not a runtime requirement, and is used only to generate version information in the javadoc API documentation.

Release Downloads

Install with Maven

Install with Gradle

Usage

Examples

Reference

Specifications

Configuration

net.fortuna.ical4j.parser=net.fortuna.ical4j.data.HCalendarParserFactory

net.fortuna.ical4j.timezone.registry=net.fortuna.ical4j.model.DefaultTimeZoneRegistryFactory

net.fortuna.ical4j.timezone.update.enabled={true|false}

net.fortuna.ical4j.factory.decoder=net.fortuna.ical4j.util.DefaultDecoderFactory

net.fortuna.ical4j.factory.encoder=net.fortuna.ical4j.util.DefaultEncoderFactory

net.fortuna.ical4j.recur.maxincrementcount=1000

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

Compatibility Hints

Relaxed Parsing

ical4j.parsing.relaxed={true|false}

iCal4j now has the capability to "relax" its parsing rules to enable parsing of *.ics files that don't properly conform to the iCalendar specification (RFC2445)

This property is intended as a general relaxation of parsing rules to allow for parsing otherwise invalid calendar files. Initially enabling this property will allow for the creation of properties and components with illegal names (e.g. Mozilla Calendar's "X" property). Note that although this will allow for parsing calendars with illegal names, validation will still identify such names as an error in the calendar model.

  • You can relax iCal4j's unfolding rules by specifying the following system property:

     ical4j.unfolding.relaxed={true|false}
    

Note that I believe this problem is not restricted to Mozilla calendaring products, but rather may be caused by UNIX/Linux-based applications relying on the default newline character (LF) to fold long lines (KOrganizer also seems to have this problem). This is, however, still incorrect as by definition long lines are folded using a (CRLF) combination.

I've obtained a couple of samples of non-standard iCalendar files that I've included in the latest release (0.9.11). There is a Sunbird, phpicalendar, and a KOrganizer sample there (open them in Notepad on Windows to see what I mean).

It seems that phpicalendar and KOrganizer always use LF instead of CRLF, and in addition KOrganizer seems to fold all property parameters and values (similar to Mozilla Calendar/Sunbird).

Mozilla Calendar/Sunbird uses CRLF to fold all property parameter/values, however it uses just LF to fold long lines (i.e. longer than 75 characters).

The latest release of iCal4j includes changes to UnfoldingReader that should work correctly with Mozilla Calendar/Sunbird, as long as the ical4j.unfolding.relaxed system property is set to true.

KOrganizer/phpicalendar files should also work with the relaxed property, although because ALL lines are separated with just LF it also relies on the StreamTokenizer to correctly identify LF as a newline on Windows, and CRLF as a newline on UNIX/Linux. The API documentation for Java 1.5 says that it does do this, so if you still see problems with parsing it could be a bug in the Java implementation.

The full set of system properties may be found in net.fortuna.ical4j.util.CompatibilityHints.

iCal4j and Timezones

net.fortuna.ical4j.timezone.date.floating={true|false}

Supporting timezones in an iCalendar implementation can be a complicated process, mostly due to the fact that there is not a definitive list of timezone definitions used by all iCalendar implementations. This means that an iCalendar file may be produced by one implementation and, if the file does not include all definitions for timezones relevant to the calendar properties, an alternate implementation may not know how to interpret the timezone identified in the calendar (or worse, it may interpret the timezone differently to the original implementation). All of these possibilities mean unpredictable behaviour which, to put it nicely, is not desireable.

iCal4j approaches the problem of timezones in two ways: The first and by far the preferred approach is for iCalendar files to include definitions for all timezones referenced in the calendar object. To support this, when an existing calendar is parsed a list of VTimeZone definitions contained in the calendar is constructed. This list may then be queried whenever a VTimeZone definition is required.

The second approach is to rely on a registry of VTimeZone definitions. iCal4j includes a default registry of timezone definitions (derived from the Olson timezone database - a defacto standard for timezone definitions), or you may also provide your own registry implementation from which to retreieve timezones. This approach is required when constructing new iCalendar files.

Note that the intention of the iCal4j model is not to provide continuous validation feedback for every change in the model. For this reason you are free to change timezones on Time objects, remove or add TzId parameters, remove or add VTimeZone definitions, etc. without restriction. However when validation is run (automatically on output of the calendar) you will be notified if the changes are invalid.

Validation

ical4j.validation.relaxed={true|false}

Micosoft Outlook compatibility

ical4j.compatibility.outlook={true|false}

Behaviour:

  • Enforces a folding length of 75 characters (by default ical4j will fold at 73 characters)
  • Allows for spaces when parsing a WEEKDAY list

Microsoft Outlook also appears to provide quoted TZID parameter values, as follows:

DTSTART;TZID="Pacific Time (US & Canada),Tijuana":20041011T223000

Lotus Notes compatibility

ical4j.compatibility.notes={true|false}

Limitations

Development

Building with Gradle

iCal4j includes the Gradle wrapper for a simpler and more consistent build.

Run unit tests

./gradlew clean test

Build a new release

./gradlew clean test release -Prelease.forceVersion=2.0.0

Upload release binaries and packages

RELEASE_VERSION=2.0.0 ./gradlew uploadArchives uploadDist

Redistribution

If you intend to use and distribute iCal4j in your own project please follow these very simple guidelines:

  • Make a copy of the LICENSE, rename it to LICENSE.ical4j, and save it to the directory where you are re-distributing the iCal4j JAR.

  • I don't recommend extracting the iCal4j classes from its JAR and package in another JAR along with other classes. It may lead to version incompatibilites in the future. Rather I would suggest to include the ical4j.jar in your classpath as required.

Contributing

Open source software is made stronger by the community that supports it. Through participation you not only contribute to the quality of the software, but also gain a deeper insight into the inner workings.

Contributions may be in the form of feature enhancements, bug fixes, test cases, documentation and forum participation. If you have a question, just ask. If you have an answer, write it down.

And if you are somehow constrained from participation, through corporate policy or otherwise, consider financial support. After all, if you are profiting from open source it's only fair to give something back to the community that make it all possible.

Comments
  • Access to WeakHashMap must be synchronized

    Access to WeakHashMap must be synchronized

    ...if a thread might be changing the map.

    It's unfortunate that you're stuck with the thread-unsafe SimpleDateFormat here, since it seems like trying to work around it has caused trouble several times.

    enhancement 
    opened by ztravis 19
  • Asia/Karachi timezone calculates offset +6 instead of +5

    Asia/Karachi timezone calculates offset +6 instead of +5

    Hi,

    Applies to: ical4j 3.0.21, but seems to also happen with 4.0.0-alpha8 (couldn't test everything)

    Asia/Karachi currently has a fixed offset of +5 (no DST). It had DST in the history, but it doesn't apply anymore. The zone file seems to be correct (but maybe you can have a look; see test below).

    ical4j however calculates an offset of +6 for current dates (e.g. in year 2021), because the observance with the latest onset is not detected correctly (because the latest onset is not calculated correctly).

    Expected result: 20210106T200000 Asia/Karachi should be resolved with offset +5 Actual result: 20210106T200000 Asia/Karachi is resolved with offset +6, shifting events by one hour

    Example problem report: https://forums.bitfire.at/post/13800

    package com.example.test;
    
    import net.fortuna.ical4j.model.DateTime;
    import net.fortuna.ical4j.model.TimeZone;
    import net.fortuna.ical4j.model.TimeZoneRegistry;
    import net.fortuna.ical4j.model.TimeZoneRegistryFactory;
    import net.fortuna.ical4j.model.component.Observance;
    
    import org.junit.Test;
    
    import static org.junit.Assert.assertEquals;
    
    public class TestKarachi {
    
        /* ical4j 3.0.21, but seems to also happen with 4.0.0-alpha8 (couldn't test everything) */
    
        @Test
        public void testTzKarachi() throws Exception {
            TimeZoneRegistry tzReg = TimeZoneRegistryFactory.getInstance().createRegistry();
    
            TimeZone origKarachi = tzReg.getTimeZone("Asia/Karachi");
            TimeZone karachi = origKarachi;
    
            long ts1 = 1609945200000L;       // 20210106T150000Z; 20210106T200000 Asia/Karachi
            DateTime dt1 = new DateTime(ts1);
            dt1.setUtc(true);
    
            System.err.println("Applicable observance: " + karachi.getVTimeZone().getApplicableObservance(dt1));
            /* prints:
    
               BEGIN:DAYLIGHT
               TZNAME:PKST
               TZOFFSETFROM:+0500
               TZOFFSETTO:+0600
               DTSTART:20020407T000000
               RDATE:20080601T000000
               RDATE:20090415T000000
               END:DAYLIGHT
    
               which is wrong. Should be this one:
    
               BEGIN:STANDARD
               TZNAME:PKT
               TZOFFSETFROM:+0600
               TZOFFSETTO:+0500
               DTSTART:20081101T000000
               RRULE:FREQ=YEARLY;UNTIL=20091031T180000Z
               END:STANDARD
            */
    
            for (Observance obs : karachi.getVTimeZone().getObservances()) {
                System.err.print("Found observance: " + obs.toString());
                System.err.println("Latest onset: " + obs.getLatestOnset(dt1));
                System.err.println();
            }
            /* prints:
    
            Found observance: BEGIN:STANDARD
            TZNAME:+0530
            TZOFFSETFROM:+042812
            TZOFFSETTO:+0530
            DTSTART:19070101T000000
            END:STANDARD
            Latest onset: 19061231T193148Z
    
            Found observance: BEGIN:DAYLIGHT
            TZNAME:+0630
            TZOFFSETFROM:+0530
            TZOFFSETTO:+0630
            DTSTART:19420901T000000
            END:DAYLIGHT
            Latest onset: 19420831T183000Z
    
            Found observance: BEGIN:STANDARD
            TZNAME:+0530
            TZOFFSETFROM:+0630
            TZOFFSETTO:+0530
            DTSTART:19451015T000000
            END:STANDARD
            Latest onset: 19451014T173000Z
    
            Found observance: BEGIN:STANDARD
            TZNAME:+05
            TZOFFSETFROM:+0530
            TZOFFSETTO:+0500
            DTSTART:19510930T000000
            END:STANDARD
            Latest onset: 19510929T183000Z
    
            Found observance: BEGIN:STANDARD
            TZNAME:PKT
            TZOFFSETFROM:+0500
            TZOFFSETTO:+0500
            DTSTART:19710326T000000
            END:STANDARD
            Latest onset: 19710325T190000Z
    
            Found observance: BEGIN:DAYLIGHT
            TZNAME:PKST
            TZOFFSETFROM:+0500
            TZOFFSETTO:+0600               // <- this is what is used for 2021
            DTSTART:20020407T000000
            RDATE:20080601T000000
            RDATE:20090415T000000        // <-- newer than 20081031T180000Z so this one is used
            END:DAYLIGHT
            Latest onset: 20090414T190000Z
    
            Found observance: BEGIN:STANDARD
            TZNAME:PKT
            TZOFFSETFROM:+0600
            TZOFFSETTO:+0500
            DTSTART:20021006T000000
            END:STANDARD
            Latest onset: 20021005T180000Z
    
            Found observance: BEGIN:STANDARD
            TZNAME:PKT
            TZOFFSETFROM:+0600
            TZOFFSETTO:+0500                         // <- this is what should be used for 2021
            DTSTART:20081101T000000
            RRULE:FREQ=YEARLY;UNTIL=20091031T180000Z
            END:STANDARD
            Latest onset: 20081031T180000Z                // <- this should be 20091031T180000Z
             */
    
            assertEquals(5, karachi.getOffset(ts1)/3600000);  // fails!
    
            DateTime dt2 = new DateTime("20210106T200000", karachi);
            assertEquals(1609945200000L, dt2.getTime());    // fails!
        }
    
    }
    

    Thanks for looking at it!

    opened by rfc2822 16
  • exceptions to a recurring range not handled correctly by ComponentGroup; period not taken into account

    exceptions to a recurring range not handled correctly by ComponentGroup; period not taken into account

    The situation is simple; I have a recurring vevent on weekdays, for example mo, tu, th and fr. Then I create an exception by moving tu to we. Now I have two events in the ical file

    BEGIN:VEVENT DTSTART;TZID=Europe/Rome:20200303T061500 DTEND;TZID=Europe/Rome:20200303T071500 DTSTAMP:20200304T064905Z UID:[email protected] RECURRENCE-ID;TZID=Europe/Rome:20200304T061500 CREATED:20200202T092719Z DESCRIPTION: LAST-MODIFIED:20200301T212302Z LOCATION: SEQUENCE:3 STATUS:CONFIRMED SUMMARY:precond TRANSP:OPAQUE END:VEVENT

    BEGIN:VEVENT DTSTART;TZID=Europe/Rome:20200203T061500 DTEND;TZID=Europe/Rome:20200203T071500 RRULE:FREQ=WEEKLY;WKST=MO;BYDAY=FR,MO,TH,WE DTSTAMP:20200304T064905Z UID:[email protected] CREATED:20200202T092719Z DESCRIPTION: LAST-MODIFIED:20200212T071944Z LOCATION: SEQUENCE:2 STATUS:CONFIRMED SUMMARY:precond TRANSP:OPAQUE END:VEVENT

    If I now ask ical4j which is the getLatestRevision for the UID, I always get the one valid on 20200203, because that logic does not take any timeframe into account. If I then calculate the calculateRecurrenceSet for the day before, I get no periods. But on the day for the exception, the original recurring event should be used.

    Using group.calculateRecurrenceSet does not work either, because then I get all periods, both from the recurring and the exception. Shouldn't getLatestRevision take the period into account?

    in review 
    opened by tbee 15
  • Make JCache optional

    Make JCache optional

    After upgrading to 2.1.1 my application fails with this error:

    Caused by: javax.cache.CacheException: No CachingProviders have been configured
    at javax.cache.Caching$CachingProviderRegistry.getCachingProvider(Caching.java:381) ~[cache-api-1.0.0.jar:na]
    at javax.cache.Caching$CachingProviderRegistry.getCachingProvider(Caching.java:351) ~[cache-api-1.0.0.jar:na]
    at javax.cache.Caching.getCachingProvider(Caching.java:142) ~[cache-api-1.0.0.jar:na]
    at net.fortuna.ical4j.model.TimeZoneLoader.cacheInit(TimeZoneLoader.java:285) ~[ical4j-2.1.1.jar:na]
    at net.fortuna.ical4j.model.TimeZoneLoader.<init>(TimeZoneLoader.java:83) ~[ical4j-2.1.1.jar:na]
    at net.fortuna.ical4j.model.TimeZoneRegistryImpl.<init>(TimeZoneRegistryImpl.java:125) ~[ical4j-2.1.1.jar:na]
    at net.fortuna.ical4j.model.TimeZoneRegistryImpl.<init>(TimeZoneRegistryImpl.java:116) ~[ical4j-2.1.1.jar:na]
    at net.fortuna.ical4j.model.DefaultTimeZoneRegistryFactory.createRegistry(DefaultTimeZoneRegistryFactory.java:48) ~[ical4j-2.1.1.jar:na]
    at net.fortuna.ical4j.data.CalendarBuilder.<init>(CalendarBuilder.java:105) ~[ical4j-2.1.1.jar:na]
    

    I understand that this was introduced with #192 and adding a JCache provider on the class path fixes that issue. However I would've prefered a smooth upgrade where adding a cache provider is optional.

    opened by malkusch 13
  • IllegalAccessError: no such constructor: net.fortuna.ical4j.util.JCacheTimeZoneCache.<init>()void/newInvokeSpecial since upgrade to 3.0.0

    IllegalAccessError: no such constructor: net.fortuna.ical4j.util.JCacheTimeZoneCache.()void/newInvokeSpecial since upgrade to 3.0.0

    I did the recent upgrade to 3.0.0 and I'm running on Java-8 still. Since that upgrade I have again a JCache ClassNotFound issue, but this time I do have set the property net.fortuna.ical4j.timezone.cache.impl: net.fortuna.ical4j.util.MapTimeZoneCache. That is the relevant stack trace:

    Caused by: java.lang.BootstrapMethodError: java.lang.IllegalAccessError: no such constructor: net.fortuna.ical4j.util.JCacheTimeZoneCache.<init>()void/newInvokeSpecial
    	at net.fortuna.ical4j.model.TimeZoneLoader.cacheInit(TimeZoneLoader.java:275) ~[ical4j-3.0.0.jar:na]
    	at net.fortuna.ical4j.model.TimeZoneLoader.<init>(TimeZoneLoader.java:81) ~[ical4j-3.0.0.jar:na]
    	at net.fortuna.ical4j.model.TimeZoneRegistryImpl.<init>(TimeZoneRegistryImpl.java:125) ~[ical4j-3.0.0.jar:na]
    	at net.fortuna.ical4j.model.TimeZoneRegistryImpl.<init>(TimeZoneRegistryImpl.java:116) ~[ical4j-3.0.0.jar:na]
    	at net.fortuna.ical4j.model.DefaultTimeZoneRegistryFactory.createRegistry(DefaultTimeZoneRegistryFactory.java:48) ~[ical4j-3.0.0.jar:na]
    	at net.fortuna.ical4j.data.CalendarBuilder.<init>(CalendarBuilder.java:105) ~[ical4j-3.0.0.jar:na]
    	at de.malkusch.trashcollection.infrastructure.schedule.ical.VEventRepository.downloadVEvents(VEventRepository.java:46) ~[classes/:na]
    	at de.malkusch.trashcollection.infrastructure.schedule.ical.VEventRepository.<init>(VEventRepository.java:35) ~[classes/:na]
    	at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method) ~[na:1.8.0_172]
    	at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62) ~[na:1.8.0_172]
    	at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45) ~[na:1.8.0_172]
    	at java.lang.reflect.Constructor.newInstance(Constructor.java:423) ~[na:1.8.0_172]
    	at org.springframework.beans.BeanUtils.instantiateClass(BeanUtils.java:170) ~[spring-beans-5.0.7.RELEASE.jar:5.0.7.RELEASE]
    	... 80 common frames omitted
    Caused by: java.lang.IllegalAccessError: no such constructor: net.fortuna.ical4j.util.JCacheTimeZoneCache.<init>()void/newInvokeSpecial
    	at java.lang.invoke.MethodHandleNatives.linkMethodHandleConstant(MethodHandleNatives.java:483) ~[na:1.8.0_172]
    	... 93 common frames omitted
    Caused by: java.lang.NoClassDefFoundError: javax/cache/configuration/Configuration
    	at java.lang.invoke.MethodHandleNatives.resolve(Native Method) ~[na:1.8.0_172]
    	at java.lang.invoke.MemberName$Factory.resolve(MemberName.java:975) ~[na:1.8.0_172]
    	at java.lang.invoke.MemberName$Factory.resolveOrFail(MemberName.java:1000) ~[na:1.8.0_172]
    	at java.lang.invoke.MethodHandles$Lookup.resolveOrFail(MethodHandles.java:1394) ~[na:1.8.0_172]
    	at java.lang.invoke.MethodHandles$Lookup.linkMethodHandleConstant(MethodHandles.java:1750) ~[na:1.8.0_172]
    	at java.lang.invoke.MethodHandleNatives.linkMethodHandleConstant(MethodHandleNatives.java:477) ~[na:1.8.0_172]
    	... 93 common frames omitted
    Caused by: java.lang.ClassNotFoundException: javax.cache.configuration.Configuration
    	at java.net.URLClassLoader.findClass(URLClassLoader.java:381) ~[na:1.8.0_172]
    	at java.lang.ClassLoader.loadClass(ClassLoader.java:424) ~[na:1.8.0_172]
    	at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:349) ~[na:1.8.0_172]
    	at java.lang.ClassLoader.loadClass(ClassLoader.java:357) ~[na:1.8.0_172]
    	... 99 common frames omitted
    

    The weird thing is, when I'm debugging the relevant code. I see that the offending Optional in TimeZoneLoader.java:275 has in fact an instance of MapTimeZoneCache. So it should never come to the orElseGet() part. However it seems that Java-8 is not happy with the non existing JCache from that constructor. Does it work for you when you have no JCache on the class path?

    As workaround putting the JCache-API into the class path fixes that issue for me:

    <dependency>
      <groupId>javax.cache</groupId>
      <artifactId>cache-api</artifactId>
    </dependency>
    
    opened by malkusch 12
  • Caused by: java.lang.NoClassDefFoundError: Could not initialize class net.fortuna.ical4j.model.TimeZoneRegistryFactory

    Caused by: java.lang.NoClassDefFoundError: Could not initialize class net.fortuna.ical4j.model.TimeZoneRegistryFactory

    I am trying to upgrade the ical4j from v1.0.3 to v3.2.5, once upgrade I am getting below error: Caused by: java.lang.NoClassDefFoundError: Could not initialize class net.fortuna.ical4j.model.TimeZoneRegistryFactory

    Jave: 11.

    opened by Ahamed4 11
  • Incorrect next date computed for `RRule` if `tzurl.org` is unreachable

    Incorrect next date computed for `RRule` if `tzurl.org` is unreachable

    Describe the bug

    ical4j version - 3.0.27

    Hello - we noticed an issue when invoking net.fortuna.ical4j.model.Recur#getNextDate if the method net.fortuna.ical4j.model.TimeZoneRegistry#getTimeZone fails to update the timezone definition via tzurl.org.

    Given the following code:

            RRule rRule = new RRule(recurringExpression);
            Recur recur = rRule.getRecur();
            net.fortuna.ical4j.model.TimeZoneRegistry registry =
                    net.fortuna.ical4j.model.TimeZoneRegistryFactory.getInstance().createRegistry();
            net.fortuna.ical4j.model.TimeZone tz = registry.getTimeZone(zoneId.getId());
            Date date = recur.getNextDate(
                    new net.fortuna.ical4j.model.DateTime(new Date(seedTimestamp), tz),
                    new net.fortuna.ical4j.model.DateTime(new Date(timestamp), tz));
    

    given the following values for the params:

    recurringExpression: FREQ=DAILY;BYDAY=MO,TU,WE,TH,FR zoneid: America/Toronto seedTimestamp: 1634547600000L (Monday, October 18, 2021 9:00:00 AM UTC / Monday, October 18, 2021 5:00:00 AM [GMT-04:00] DST) timestamp: 1646906400001L (Thursday, March 10, 2022 10:00:00.001 AM UTC / Thursday, March 10, 2022 5:00:00.001 AM [GMT-05:00])

    the expected output for the date is supposed to be 1646992800000, ie Friday, March 11, 2022 10:00:00 AM UTC / Friday, March 11, 2022 5:00:00 AM [GMT-05:00]. However, when this code was being executed we had the following exception in our logs:

    java.io.IOException: Server returned HTTP response code: 503 for URL: http://tzurl.sgp1.cdn.digitaloceanspaces.com/zoneinfo/America/Toronto.ics
    

    consequently, registry.getTimeZone(zoneId.getId()); returned null and the date was computed as Friday, March 11, 2022 9:00:00 AM UTC / Friday, March 11, 2022 4:00:00 AM [GMT-05:00]

    reading the timezone docs, it seems we can disable the remote updates via the following property:

            System.setProperty("net.fortuna.ical4j.timezone.update.enabled", "false");
    

    and we continue to work correctly.

    I was wondering if this is safe to disable for the long term? Are there any issues we should be aware of?

    Also, if the remote update fails, is returning null intentional? Or should it provide the timezone as constructed from the zoneinfo files provided in the library?

    bug 
    opened by moadi 11
  • Long time to build from a ics file

    Long time to build from a ics file

    Describe the bug I try to read a quite large ics file (1.4Mb, 2400 events) and it takes nearly one minute

    To Reproduce Steps to reproduce the behavior: I use the java library, version 3.0.6, with this code :

        URL icsUrl = new URL(url);
        HttpURLConnection connexion = (HttpURLConnection)icsUrl.openConnection();
    
        try (InputStream flux = connexion.getInputStream())
        {  
            CalendarBuilder builder = new CalendarBuilder();
            Calendar calendar = builder.build(flux);
        }
    

    The builder.build(flux) takes nearly 1 minute (I tried to download directly the ics file and it takes less than 1sec)

    Is there any way to read it faster ?

    in review 
    opened by pgametys 10
  • Timezone issue

    Timezone issue

    Describe the bug Google Mail/Calendar: Time in ICS file is correct (MESZ)

    To Reproduce Add appended ICS file to apple calendar

    Expected behavior Time zone is correctly used

    Actual result 2 hours difference in appointment start date within apple calendar. Appointment should be from 3 to 4 PM, but in apple calendar it is from shown to be from 5 to 6 PM.

    What is the preferred way of making the time zone works correctly on as many as possible devices?

    testappointment.ics.zip

    Bildschirmfoto 2019-09-16 um 13 29 01

    opened by crudolf 9
  • Accept empty PRIORITY values in compatibility mode

    Accept empty PRIORITY values in compatibility mode

    Some buggy servers like Horde send empty values for properties which need a value, for instance PRIORITY:

    SUMMARY: ...
    PRIORITY:                            <- !!
    STATUS:COMPLETED
    

    which causes a NumberFormatException/ParserException. See also https://forums.bitfire.at/post/11777

    I suggest to ignore such properties completely (instead of stopping with an exception) when compatibility mode is active.

    enhancement 
    opened by rfc2822 9
  • Declare stable Java module name

    Declare stable Java module name

    Currently this library does not define a stable Java module name. Thus this automatic module name is derived from it's JAR filename to be ical4j.

    It would be useful to set a stable automatic module name to what it will eventually be when the library natively supports the JPMS (see #345). net.fortuna.ical4j is one possibility but would take ownership of the package of the same name. This would conflict with other projects being subpackages thereof (e.g. net.fortuna.ical4j.vcard).

    Maybe the best solution would be moving the net.fortuna.ical4j package from this project to net.fortuna.ical4j.core with the next big version. That way the module name net.fortuna.ical4j.core would not conflict with the other projects.

    For more details about Java module names see: sormuras/modules#about-java-module-names

    opened by mkroening 9
  • Timezone for Iceland is incorrect.

    Timezone for Iceland is incorrect.

    Describe the bug The timezone for Iceland (provided in Atlantic/Reykjavik is incorrect. It should be Atlantic/Reykjavik and behave in any and all ways as UTC.

    opened by helgaw 1
  • ParserException, Invalid value [ALL] for Range parameter while parsing ICS

    ParserException, Invalid value [ALL] for Range parameter while parsing ICS

    Describe the bug We have an ics file (received by us from google) which contains following line in ICS

    X-LOTUS-RECURID;RANGE=ALL:20220309T173000Z

    The presence of above line in ics causes the ics parsing to fail as shown below in example code.

    To Reproduce Sample code to reproduce

    
    import net.fortuna.ical4j.data.CalendarBuilder;
    import net.fortuna.ical4j.model.Calendar;
    
    import java.io.ByteArrayInputStream;
    
    import static java.nio.charset.StandardCharsets.UTF_8;
    
    public class ParseTest {
    
      public static void main(String[] args) {
        String ics = "BEGIN:VCALENDAR\n" +
          "VERSION:2.0\n" +
          "PRODID://Random Org\n" +
          "METHOD:CANCEL\n" +
          "CALSCALE:GREGORIAN\n" +
          "BEGIN:VEVENT\n" +
          "DTSTAMP:20250121T043511Z\n" +
          "UID:ASDASDASDASDASDASDASDas\n" +
          "SUMMARY:ABC\n" +
          "DTSTART:20250201T063000Z\n" +
          "DTEND:20250201T073000Z\n" +
          "CREATED:20250211T063000Z\n" +
          "SEQUENCE:1\n" +
          "TRANSP:OPAQUE\n" +
          "ORGANIZER;[email protected]:mailto:[email protected]\n" +
          "LAST-MODIFIED:20250121T043511Z\n" +
          "X-LOTUS-RECURID;RANGE=ALL:20220309T173000Z\n" +
          "END:VEVENT\n" +
          "END:VCALENDAR\n";
        Calendar calendar;
        try (ByteArrayInputStream inputStream = new ByteArrayInputStream(ics.getBytes(UTF_8))) {
          calendar = new CalendarBuilder().build(inputStream);
        } catch (Exception e) {
          System.out.println(e);
        }
      }
    }
    

    Output of code : net.fortuna.ical4j.data.ParserException: Error at line 17:Invalid value [ALL]

    Expected behavior The ics should parse as property is part of user defined Attribute as it start with X.

    Screenshots If applicable, add screenshots to help explain your problem.

    Environment (please complete the following information):

    • OS: Linux/Mac
    • Java Version: 8
    • iCal4j Version: 3.2.7

    Additional context Add any other context about the problem here.

    opened by luvk1412 1
  • java.text.ParseException: Unparseable date:

    java.text.ParseException: Unparseable date: "20221207T170935" in ical4j when specifying time zone Australia/Lord_Howe

    I am getting a parsing exception while I am trying the following code: `import java.time.LocalDateTime; import java.time.format.DateTimeFormatter;

    import net.fortuna.ical4j.model.TimeZone; import net.fortuna.ical4j.model.TimeZoneRegistry; import net.fortuna.ical4j.model.TimeZoneRegistryFactory; import net.fortuna.ical4j.model.property.DtStart;

    public class Timezone {

    public static void main(String[] args) {
        
        
        TimeZoneRegistry registry = TimeZoneRegistryFactory.getInstance().createRegistry();
        TimeZone tz;
        LocalDateTime now = LocalDateTime.now();
        final DateTimeFormatter ICS_DATE_FORMATTER =
                DateTimeFormatter.ofPattern("yyyyMMdd'T'HHmmss");
        
        //tz = registry.getTimeZone("Asia/Calcutta");
        tz = registry.getTimeZone("Australia/Lord_Howe");
        DtStart dtstart;
        try {
    	dtstart = new DtStart(now.format(ICS_DATE_FORMATTER),tz);
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }    
    }
    

    }` java.text.ParseException: Unparseable date: "20221207T170935" at java.base/java.text.DateFormat.parse(DateFormat.java:395) at net.fortuna.ical4j.model.DateTime.setTime(DateTime.java:418) at net.fortuna.ical4j.model.DateTime.(DateTime.java:325) at net.fortuna.ical4j.model.property.DateProperty.setValue(DateProperty.java:137) at net.fortuna.ical4j.model.property.DtStart.(DtStart.java:146) at Timezone.main(Timezone.java:33)

    I have used ical4j 3.0.19 jar and their dependency jar .

    Its working for all the timezone except for the timezone "Australia/Lord_Howe".

    I am expecting the Dtstart value as :

    DTSTART;TZID=Australia/Lord_Howe:20221207T170935

    bug 
    opened by SwatiSatrusalya 3
  • Created and LastModified use wrong extend from DateProperty

    Created and LastModified use wrong extend from DateProperty

    Describe the bug The classes Created and LastModified are both extending DateProperty<Instant> and UtcProperty which indicates that the return value of getDate should be a Instant representing a UTC timestamp. But in my case the return value of getDate is a ZonedDateTime. To workaround the issue I am just getting the DateProperty and cast it to a ZonedDateTime:

            DateProperty<Temporal> creationTime = component.getRequiredProperty("CREATED");
            entry.setCreated(((ZonedDateTime) creationTime.getDate()).withZoneSameInstant(ZoneOffset.UTC).toInstant());
    

    To Reproduce Example VEvent which is causing the issue:

    BEGIN:VEVENT
    UID:[email protected]
    DTSTART;TZID=Europe/Berlin:20221108T090000
    DTEND;TZID=Europe/Berlin:20221108T121500
    DTSTAMP;TZID=Europe/Berlin:20221203T100650
    LAST-MODIFIED;TZID=Europe/Berlin:20221108T182747
    CREATED;TZID=Europe/Berlin:20220821T084513
    SUMMARY:Test
    DESCRIPTION:Test
    LOCATION:Virtual-Room-26
    END:VEVENT
    

    Expected behavior Either the created and LastModified classes are extening correctly from DateProperty or the return value of getDate is actually an Instant.

    Screenshots image image

    Environment (please complete the following information):

    • OS: Windows 11 22H2
    • Java Version 17.0.1 (2021-10-19 LTS)
    • iCal4j Version 4.0.0-beta4
    bug 
    opened by derklaro 3
  • Use ZonedDateTime instead of Instant for UTC times

    Use ZonedDateTime instead of Instant for UTC times

    Use ZonedDateTime with timezone set to UTC instead of Instant for timestamps like 20221101T170000Z.

    Currently, Instant is used when parsing datetime with 'Z'. As far as I understand, Instant is not a proper representation of date and time in UTC. This showed up when I parsed .ics file with timestamps in this format (with 'Z') and two-week recurrence. When calculating recurring dates, as is demonstrated in RecurSpec, I got an error:

    java.time.temporal.UnsupportedTemporalTypeException: Unsupported unit: Weeks

    That is because Instant does not support week-based operations like adding. However, I feel that this operation (adding two weeks to a UTC timestamp) should not be an issue. Parsing this kind of timestamp as ZonedDateTime with UTC fixes this problem.

    However, I am completely new user of ical4j and iCalendar in general. This is literally the first time I am using both. Therefore I am unsure if this would be, conceptually, the right approach.

    Still more tests are failing, so far I have only modified a few of them. I will have a look at the remaining failing tests after it is confirmed that this change is a good idea.

    opened by jirijakes 0
  • new method in Recur to get dates as a java.util.stream.Stream

    new method in Recur to get dates as a java.util.stream.Stream

    Is your feature request related to a problem? Please describe. Recur.getDates has better performances that iterating using Recur.getNextDate, however the drawback is that it produces all dates in memory.

    Describe the solution you'd like A new method (e.g. Recur.getDatesStream) that returns a java.util.stream.Stream The method could be similar to getDates, but it won't actually calculate all the dates that would be calculated as they are consumed by the stream. To build the Stream it could use java.util.stream.Stream#iterate

    Describe alternatives you've considered Using getDates().stream() still creates the list of dates before streaming, keeping them all in memory Tried to create the Steam using java.util.stream.Stream#iterate and calling getNextDate, but it has bad performances and fails in some scenarios

    enhancement 
    opened by fmossott 11
Owner
iCal4j
iCalendar tools for Java
iCal4j
Joda-Time is the widely used replacement for the Java date and time classes prior to Java SE 8.

Joda-Time Joda-Time provides a quality replacement for the Java date and time classes. The design allows for multiple calendar systems, while still pr

Joda.org 4.9k Dec 27, 2022
Backport of functionality based on JSR-310 to Java SE 6 and 7. This is NOT an implementation of JSR-310.

ThreeTen backport project JSR-310 provides a new date and time library for Java SE 8. This project is the backport to Java SE 6 and 7. See the main ho

ThreeTen 541 Jan 8, 2023
This is a picker view for android , support linkage effect, timepicker and optionspicker

This is a picker view for android , support linkage effect, timepicker and optionspicker

Bigkoo 13.2k Jan 3, 2023
Library which allows the use and rendering of Blockbench models and animations in a Minecraft server by using generated resource packs and armorstands

Hephaestus Engine Hephaestus Engine is a library which allows the visualization of block bench models and animations in a Minecraft server by the use

Unnamed Team 109 Dec 21, 2022
Spring Data Redis extensions for better search, documents models, and more

Object Mapping (and more) for Redis! Redis OM Spring extends Spring Data Redis to take full advantage of the power of Redis. Project Stage Snapshot Is

Redis 303 Dec 29, 2022
Implementation of Enhancing cubes with models to describe multidimensional data.

Implementation of Enhancing cubes with models to describe multidimensional data.

Business Intelligence Group - University of Bologna 2 Dec 15, 2022
Java library for representing, parsing and encoding URNs as in RFC2141 and RFC8141

urnlib Java library for representing, parsing and encoding URNs as specified in RFC 2141 and RFC 8141. The initial URN RFC 2141 of May 1997 was supers

SLUB 24 May 10, 2022
A Java library for quickly and efficiently parsing and writing UUIDs

fast-uuid fast-uuid is a Java library for quickly and efficiently parsing and writing UUIDs. It yields the most dramatic performance gains when compar

Jon Chambers 142 Jan 1, 2023
A distributed data integration framework that simplifies common aspects of big data integration such as data ingestion, replication, organization and lifecycle management for both streaming and batch data ecosystems.

Apache Gobblin Apache Gobblin is a highly scalable data management solution for structured and byte-oriented data in heterogeneous data ecosystems. Ca

The Apache Software Foundation 2.1k Jan 4, 2023
An authorization library that supports access control models like ACL, RBAC, ABAC in Java

jCasbin News: still worry about how to write the correct jCasbin policy? Casbin online editor is coming to help! Try it at: http://casbin.org/editor/

Casbin 2k Dec 30, 2022
Screaming fast JSON parsing and serialization library for Android.

#LoganSquare The fastest JSON parsing and serializing library available for Android. Based on Jackson's streaming API, LoganSquare is able to consiste

BlueLine Labs 3.2k Dec 18, 2022
Java library for parsing report files from static code analysis.

Violations Lib This is a Java library for parsing report files like static code analysis. Example of supported reports are available here. A number of

Tomas Bjerre 127 Nov 23, 2022
Yet another Java annotation-based command parsing library, with Dependency Injection inspired by Guice

Commander A universal java command parsing library Building This project uses Gradle. Clone this repository: git clone https://github.com/OctoPvP/Comm

OctoDev 4 Oct 2, 2022
A small library for parsing ItemStacks from a human-readable format

easy-item A small library for parsing ItemStacks from a human-readable format (1.16.5+, Java 11) TODO: Maybe add serialization (item to human-readable

Maximilian Dorn 3 Dec 4, 2021
HATEOAS with HAL for Java. Create hypermedia APIs by easily serializing your Java models into HAL JSON.

hate HATEOAS with HAL for Java. Create hypermedia APIs by easily serializing your Java models into HAL JSON. More info in the wiki. Install with Maven

null 20 Oct 5, 2022
Model import deployment framework for retraining models (pytorch, tensorflow,keras) deploying in JVM Micro service environments, mobile devices, iot, and Apache Spark

The Eclipse Deeplearning4J (DL4J) ecosystem is a set of projects intended to support all the needs of a JVM based deep learning application. This mean

Eclipse Foundation 12.7k Dec 30, 2022