/** * Add a date to the list. The date will be updated to reflect the timezone of this list. * * @param date the date to add * @return true * @see List#add(java.lang.Object) */ public final boolean add(final Date date) { if (date instanceof DateTime) { if (isUtc()) { ((DateTime) date).setUtc(true); } else { ((DateTime) date).setTimeZone(getTimeZone()); } } else if (!Value.DATE.equals(getType())) { DateTime dateTime = new DateTime(date); dateTime.setTimeZone(getTimeZone()); return add((Object) dateTime); } return add((Object) date); }
/** * Constructs a new instance from the specified string value. * * @param aValue a string representation of a recurrence. * @throws ParseException thrown when the specified string contains an invalid representation of * an UNTIL date value */ public Recur(final String aValue) throws ParseException { final StringTokenizer t = new StringTokenizer(aValue, ";="); while (t.hasMoreTokens()) { final String token = t.nextToken(); if (FREQ.equals(token)) { frequency = nextToken(t, token); } else if (UNTIL.equals(token)) { final String untilString = nextToken(t, token); if (untilString != null && untilString.indexOf("T") >= 0) { until = new DateTime(untilString); // UNTIL must be specified in UTC time.. ((DateTime) until).setUtc(true); } else { until = new Date(untilString); } } else if (COUNT.equals(token)) { count = Integer.parseInt(nextToken(t, token)); } else if (INTERVAL.equals(token)) { interval = Integer.parseInt(nextToken(t, token)); } else if (BYSECOND.equals(token)) { secondList = new NumberList(nextToken(t, token), 0, 59, false); } else if (BYMINUTE.equals(token)) { minuteList = new NumberList(nextToken(t, token), 0, 59, false); } else if (BYHOUR.equals(token)) { hourList = new NumberList(nextToken(t, token), 0, 23, false); } else if (BYDAY.equals(token)) { dayList = new WeekDayList(nextToken(t, token)); } else if (BYMONTHDAY.equals(token)) { monthDayList = new NumberList(nextToken(t, token), 1, 31, true); } else if (BYYEARDAY.equals(token)) { yearDayList = new NumberList(nextToken(t, token), 1, 366, true); } else if (BYWEEKNO.equals(token)) { weekNoList = new NumberList(nextToken(t, token), 1, 53, true); } else if (BYMONTH.equals(token)) { monthList = new NumberList(nextToken(t, token), 1, 12, false); } else if (BYSETPOS.equals(token)) { setPosList = new NumberList(nextToken(t, token), 1, 366, true); } else if (WKST.equals(token)) { weekStartDay = nextToken(t, token); } // assume experimental value.. else { experimentalValues.put(token, nextToken(t, token)); } } validateFrequency(); }
/** * Returns the the next date of this recurrence given a seed date and start date. The seed date * indicates the start of the fist occurrence of this recurrence. The start date is the starting * date to search for the next recurrence. Return null if there is no occurrence date after start * date. * * @return the next date in the recurrence series after startDate * @param seed the start date of this Recurrence's first instance * @param startDate the date to start the search */ public final Date getNextDate(final Date seed, final Date startDate) { final Calendar cal = Dates.getCalendarInstance(seed); cal.setTime(seed); // optimize the start time for selecting candidates // (only applicable where a COUNT is not specified) if (getCount() < 1) { final Calendar seededCal = (Calendar) cal.clone(); while (seededCal.getTime().before(startDate)) { cal.setTime(seededCal.getTime()); increment(seededCal); } } int invalidCandidateCount = 0; int noCandidateIncrementCount = 0; Date candidate = null; final Value value = seed instanceof DateTime ? Value.DATE_TIME : Value.DATE; while (true) { final Date candidateSeed = Dates.getInstance(cal.getTime(), value); if (getUntil() != null && candidate != null && candidate.after(getUntil())) { break; } if (getCount() > 0 && invalidCandidateCount >= getCount()) { break; } if (Value.DATE_TIME.equals(value)) { if (((DateTime) seed).isUtc()) { ((DateTime) candidateSeed).setUtc(true); } else { ((DateTime) candidateSeed).setTimeZone(((DateTime) seed).getTimeZone()); } } final DateList candidates = getCandidates(candidateSeed, value); if (!candidates.isEmpty()) { noCandidateIncrementCount = 0; // sort candidates for identifying when UNTIL date is exceeded.. Collections.sort(candidates); for (final Iterator<Date> i = candidates.iterator(); i.hasNext(); ) { candidate = i.next(); // don't count candidates that occur before the seed date.. if (!candidate.before(seed)) { // Candidate must be after startDate because // we want the NEXT occurrence if (!candidate.after(startDate)) { invalidCandidateCount++; } else if (getCount() > 0 && invalidCandidateCount >= getCount()) { break; } else if (!(getUntil() != null && candidate.after(getUntil()))) { return candidate; } } } } else { noCandidateIncrementCount++; if ((maxIncrementCount > 0) && (noCandidateIncrementCount > maxIncrementCount)) { break; } } increment(cal); } return null; }
/** * Returns a list of start dates in the specified period represented by this recur. This method * includes a base date argument, which indicates the start of the fist occurrence of this * recurrence. The base date is used to inject default values to return a set of dates in the * correct format. For example, if the search start date (start) is Wed, Mar 23, 12:19PM, but the * recurrence is Mon - Fri, 9:00AM - 5:00PM, the start dates returned should all be at 9:00AM, and * not 12:19PM. * * @return a list of dates represented by this recur instance * @param seed the start date of this Recurrence's first instance * @param periodStart the start of the period * @param periodEnd the end of the period * @param value the type of dates to generate (i.e. date/date-time) * @param maxCount limits the number of instances returned. Up to one years worth extra may be * returned. Less than 0 means no limit */ public final DateList getDates( final Date seed, final Date periodStart, final Date periodEnd, final Value value, final int maxCount) { final DateList dates = new DateList(value); if (seed instanceof DateTime) { if (((DateTime) seed).isUtc()) { dates.setUtc(true); } else { dates.setTimeZone(((DateTime) seed).getTimeZone()); } } final Calendar cal = Dates.getCalendarInstance(seed); cal.setTime(seed); // optimize the start time for selecting candidates // (only applicable where a COUNT is not specified) if (getCount() < 1) { final Calendar seededCal = (Calendar) cal.clone(); while (seededCal.getTime().before(periodStart)) { cal.setTime(seededCal.getTime()); increment(seededCal); } } int invalidCandidateCount = 0; int noCandidateIncrementCount = 0; Date candidate = null; while ((maxCount < 0) || (dates.size() < maxCount)) { final Date candidateSeed = Dates.getInstance(cal.getTime(), value); if (getUntil() != null && candidate != null && candidate.after(getUntil())) { break; } if (periodEnd != null && candidate != null && candidate.after(periodEnd)) { break; } if (getCount() >= 1 && (dates.size() + invalidCandidateCount) >= getCount()) { break; } // if (Value.DATE_TIME.equals(value)) { if (candidateSeed instanceof DateTime) { if (dates.isUtc()) { ((DateTime) candidateSeed).setUtc(true); } else { ((DateTime) candidateSeed).setTimeZone(dates.getTimeZone()); } } final DateList candidates = getCandidates(candidateSeed, value); if (!candidates.isEmpty()) { noCandidateIncrementCount = 0; // sort candidates for identifying when UNTIL date is exceeded.. Collections.sort(candidates); for (final Iterator<Date> i = candidates.iterator(); i.hasNext(); ) { candidate = i.next(); // don't count candidates that occur before the seed date.. if (!candidate.before(seed)) { // candidates exclusive of periodEnd.. if (candidate.before(periodStart) || !candidate.before(periodEnd)) { invalidCandidateCount++; } else if (getCount() >= 1 && (dates.size() + invalidCandidateCount) >= getCount()) { break; } else if (!(getUntil() != null && candidate.after(getUntil()))) { dates.add(candidate); } } } } else { noCandidateIncrementCount++; if ((maxIncrementCount > 0) && (noCandidateIncrementCount > maxIncrementCount)) { break; } } increment(cal); } // sort final list.. Collections.sort(dates); return dates; }