6

I am looking for a Java class where I can specify a set of date rules, such as "every 3rd sunday" and "the first occurrence of a monday every second month". I want to be able to get something like an infinite iterator out of it (.next() would return the next date matching the rules set).

I think I'd be able to build it myself - but calendars are a hassle, and it feels like something similar should exist already. I hate being the one to reinvent a crappier wheel.

Is anyone aware of something like this? I have been looking at JODA, and it seems to lay the groundwork for it, but does not appear to give the full functionality I want..

Jacob Hansson
  • 149
  • 3
  • 7
  • 19

4 Answers4

7

I don't think there's any readily made iterators for joda-time or Java Calendar API for that matter, however with joda it's so easy that you should just go with it. For example after re-familiarizing myself with joda after a few months pause I made this in about 10 minutes:

public class InstantIterator implements Iterator<Instant>,
                                        Iterable<Instant> {

    private Instant current;
    private final Instant original;
    private Duration skip;
    private List<Instant> skipThese;

    public InstantIterator(Instant startFrom, Duration skip) {
        this.current = original = startFrom;
        this.skip = skip;
        skipThese = new Vector<Instant>();
    }

    public boolean hasNext() {
        return true;
    }

    public Instant next() {
        Instant currentNext = current.toInstant();
        current = current.plus(skip);
        while (skipThese.contains(currentNext)) {
            currentNext = current.toInstant();
            current = current.plus(skip);
        }
        return currentNext;
    }

    public void remove() {
        skipThese.add(current.toInstant());
    }

    public Iterator<Instant> iterator() {
        return this;
    }

    public void rewind() {
        current = original.toInstant();
    }

    public void resetRemoved() {
        skipThese.clear();
    }

    public void resetIterator() {
        rewind();
        resetRemoved();
    }
}

Joda Time is awesome :-)

Esko
  • 29,022
  • 11
  • 55
  • 82
  • While not important at all, I just want to say that this is the second Iterator I've ever made which supports remove(), usually I've found it to be too cumbersome to support it. – Esko May 11 '09 at 11:32
0

tl;dr

Easy, with java.time.

For 3rd Thursday:

LocalDate.now().with(                                             // Get today's date, then adjust.
    TemporalAdjusters.dayOfWeekInMonth( 3 , DayOfWeek.THURSDAY )  // Define adjuster to move to third Thursday of same month.
)                                                                 // Instantiates another `LocalDate` object (java.time uses immutable objects).

java.time

The troublesome old date-time classes bundled with the earliest versions of Java are now obsolete, supplanted by the java.time classes.

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

LocalDate ld = LocalDate.of( 2018 , Month.FEBRUARY , 17 ) ;

For any LocalDate, you can adjust into the ordinal occurrence of a day-of-week within the same month (ex: third Thursday, 2nd Tuesday, etc.). Specify the day-of-week using DayOfWeek enum.

To make the adjustment, use a TemporalAdjuster implementations found in the TemporalAdjusters class.

TemporalAdjuster thirdThursdayAdjuster = TemporalAdjusters.dayOfWeekInMonth​( 3 , DayOfWeek.THURSDAY ) ;  // Third-Thursday. 

Apply that adjuster to any LocalDate, producing another LocalDate object.

LocalDate thirdThursday = ld.with( thirdThursdayAdjuster ) ;

something like an infinite iterator out of it (.next() would return the next date matching

Simply move from one LocalDate to another. You could call LocalDate::addMonths. Or if dealing with a month at a time, use the YearMonth class.

TemporalAdjuster thirdThursdayAdjuster = TemporalAdjusters.dayOfWeekInMonth​( 3 , DayOfWeek.THURSDAY ) ;  // Third-Thursday. 
List<LocalDate> dozenThirdThursdays = new ArrayList<>( 12 ) ;
YearMonth start = YearMonth.of( 2018 , Month.MARCH ) ;
for( int i = 1 ; i <= 12 ; i ++ ) {
    YearMonth ym = start.plusMonths( i ) ;
    LocalDate firstOfMonth = ym.atDay( 1 ) ;
    LocalDate thirdThursday = firstOfMonth.with( thirdThursdayAdjuster ) ;
    dozenThirdThursdays.add( thirdThursday ) ;
}

About java.time

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

The Joda-Time project, now in maintenance mode, advises migration to the java.time classes.

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

Where to obtain the java.time classes?

The ThreeTen-Extra project extends java.time with additional classes. This project is a proving ground for possible future additions to java.time. You may find some useful classes here such as Interval, YearWeek, YearQuarter, and more.

Basil Bourque
  • 303,325
  • 100
  • 852
  • 1,154
0

My company has something very much like you want but it's for .NET so it probably won't do you any good.

BCS
  • 75,627
  • 68
  • 187
  • 294
0

You could have a look at Quartz which is designed for job scheduling.

Peter Lawrey
  • 525,659
  • 79
  • 751
  • 1,130