Rafael
Rafael

Reputation: 1487

Recurring events library

I'm trying to implement an agenda function to my app, but until now I haven't found any library that can help me with this. I've read about the Calendar Provider and Google Calendar API but didn't find anything on their docs about creating events only for my app.

At the moment I've created a database to store my events with the following structure:

id - Integer used as primary key for the event;
user_id - Integer used to bound the user;
start_date - Date of the event;
end_date - Date with the end of the event's recurrence.
rec_sunday - Boolean if the event reoccurs on sunday.
rec_monday - Boolean if the event reoccurs on monday.
rec_tuesday - Boolean if the event reoccurs on tuesday.
rec_wednesday - Boolean if the event reoccurs on wednesday.
rec_thursday - Boolean if the event reoccurs on thursday.
rec_friday - Boolean if the event reoccurs on friday.
rec_saturday - Boolean if the event reoccurs on saturday.
recurrence - Boolean if the event has recurrence, so I won't need to check for all others booleans.

For now, I just need some logic to create the events in an ArrayList plus the recurring events, with their dates, so after that I can sort them with a Comparator using the date as base.

Thanks for any help.

UPDATE

Thanks to Basil Bourque's amazing answer I'm now using the ThreeTenABP library. It's an amazing library and it was much more easy to use than I thought.

Upvotes: 2

Views: 5711

Answers (2)

Basil Bourque
Basil Bourque

Reputation: 338594

The other Answer by Ali may be the way to go, using iCal4j.

But to directly answer your Question…

java.time

The java.time framework is built into Java 8 and later. These classes supplant the old troublesome date-time classes such as java.util.Date, .Calendar, & java.text.SimpleDateFormat.

To learn more, see the Oracle Tutorial. And search Stack Overflow for many examples and explanations.

Much of the java.time functionality is back-ported to Java 6 & 7 in ThreeTen-Backport and further adapted to Android in ThreeTenABP.

DayOfWeek

The java.time.DayOfWeek enum would be handy here.

EnumSet is a special implementation of Set that uses bitmaps internally to represent a collection of enum elements in a very little amount of memory with very fast operations. The idea here is to use an EnumSet rather than your boolean-per-day-of-week approach. No need for the extra boolean about recurrence as you can just see if the EnumSet has any elements or not; no elements means no recurrence.

LocalDate

The LocalDate class represents a date-only value without time-of-day and without time zone.

By the way, in standard date-handling, a week starts on Monday and ends on Sunday. While you may not want to present a week that way to your users, I suggest sticking with the standard internally. This Monday-to-Sunday approach is the order defined in the DayOfWeek enum as well.

Your class

Define a class for Event.

public class Event {
    Integer id ;
    Integer user_id ;
    LocalDate start , stop ;
    Set< DayOfWeek > recurrence ; 
}

You can initialize the recurrence EnumSet with an empty set if you wish.

recurrence = EnumSet.noneOf( DayOfWeek.class );

Then add items. You might do this with an if statement when retrieving data from database where if the SQL BOOLEAN column value of row is TRUE call the .add method here.

recurrence.add( DayOfWeek.MONDAY );

Check size of set to see if this event is recurring.

Boolean isRecurring = ( recurrence.size() > 0 );

For sorting your objects, implement Comparable by simply delegating to the start member’s LocalDate implementation. I do not recall exact syntax for generics with Comparable, but here is the rough idea.

public class Event implements Comparable {
    …
    @Override
    public int compareTo( Event otherEvent ) {
        return this.start.compareTo( otherEvent.start ) ;
    }

To fill a List of each occurrence, do something like this loop.

A TemporalAdjuster manipulates a date-time object to generate a new date-time object. The TemporalAdjusters (note plural) class provides implementations, including the one we need next and/or nextOrSame. The start might need to be included as an occurrence; I'll leave that finessing to the reader as an exercise. ;-)

if( recurrences.size > 0 ) { // If any recurrences to schedule…
    List<LocalDate> occurrences = new ArrayList<>();
    LocalDate ld = this.start ;
    while ( ld.isBefore( this.stop ) ) {
        for( DayOfWeek dow , this.recurrence ) {
            ld = ld.with( TemporalAdjusters.next( dow ) );
            occurrences.add( ld );
…

Note the logic in the code above where we test for each date being before the stop date, not less-than-or-equal-to. This Half-Open approach is common in date-time work, where a span of time is defined with the beginning as inclusive while the ending is exclusive.

You may want to sanity-check the span of time between your start and stop. If an error was made resulting in an absurdly long gap, you could blow out memory with a huge List of occurrences. The Period class helps for this check.

if ( Period.between( start , stop ).getYears() > 10 )  {   // If over a decade, halt. 
…

Caveat: All the code above is untested and never run. Just off the top of my head. Hopefully enough to get you started.

For scheduling, consider the Quartz Job Scheduler.

Upvotes: 3

Owais Ali
Owais Ali

Reputation: 724

Have you looked into using iCal4j? It's available on Maven Central and allows saving/reading calendar from a file on the file system. The added benefit of using something based on standards is if in the future you want to sync to other calendars/calendar providers it will be (in theory) easier to accomplish.

iCal4j GitHub

Upvotes: 1

Related Questions