package com.orgzly.org.datetime; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.joda.time.DateTime; import org.joda.time.DurationFieldType; import org.joda.time.Interval; import org.joda.time.Period; import org.joda.time.PeriodType; import org.joda.time.ReadableInstant; import java.util.ArrayList; import java.util.List; public class OrgDateTimeUtils { private static final int MAX_INSTANTS_IN_INTERVAL = 100; public static List<DateTime> getTimesInInterval( @NotNull OrgDateTime orgDateTime, @NotNull ReadableInstant fromTime, @Nullable ReadableInstant beforeTime, boolean useRepeater, int limit) { return getTimesInInterval(orgDateTime, fromTime, beforeTime, useRepeater, null, limit); } /** * Returns list of {@link DateTime} that belong to specified {@link OrgDateTime} * and are within specified time interval. * * @param orgDateTime {@link OrgDateTime} * @param fromTime Inclusive * @param beforeTime Exclusive. Can be null in which case limit has to be specified * @param useRepeater Use repeater from {@link OrgDateTime} * @param warningPeriod Deadline warning period * @param limit When {@code orgTime} has a repeater, limit the number of results to this number * * @return List of times within specified interval */ public static List<DateTime> getTimesInInterval( @NotNull OrgDateTime orgDateTime, @NotNull ReadableInstant fromTime, @Nullable ReadableInstant beforeTime, boolean useRepeater, @Nullable OrgInterval warningPeriod, int limit) { List<DateTime> result = new ArrayList<>(); DateTime time = new DateTime(orgDateTime.getCalendar()); // System.out.println(orgDateTime + " (" + time + ") " + fromTime + " - " + beforeTime); /* If Org time has no repeater or it should be ignored, * just check if time part is within the interval. */ if (!useRepeater || !orgDateTime.hasRepeater() || orgDateTime.getRepeater().getValue() == 0) { time = applyWarningPeriod(time, warningPeriod); if (!time.isBefore(fromTime) && (beforeTime == null || time.isBefore(beforeTime))) { result.add(time); } } else { if (beforeTime == null && limit == 0) { throw new IllegalArgumentException("When beforeTime is not specified, limit is mandatory"); } if (limit > MAX_INSTANTS_IN_INTERVAL) { limit = MAX_INSTANTS_IN_INTERVAL; } OrgRepeater repeater = orgDateTime.getRepeater(); time = applyWarningPeriod(time, warningPeriod); if (time.isBefore(fromTime)) { /* How many periods between time and start of interval. */ Interval gap = new Interval(time, fromTime); Period intervalPeriod = gap.toPeriod(OrgDateTimeUtils.getPeriodType(repeater.getUnit())); int units = intervalPeriod.getValue(0); /* How many units to add to get just after the start of interval. * This is multiples of repeater's value. */ int repeatTimes = (units + repeater.getValue() - 1) / repeater.getValue(); // ceil int addUnits = repeater.getValue() * repeatTimes; /* Time just after the interval we are interested in. */ time = time.withFieldAdded(OrgDateTimeUtils.getDurationFieldType(repeater.getUnit()), addUnits); System.out.println( "gap: " + gap + " intervalPeriod: " + intervalPeriod + " units: " + units + " repeatTimes: " + repeatTimes + " addUnits: " + addUnits + " time: " + time); } // Shift time until it's out of the specified interval or limit is reached while (beforeTime == null || time.isBefore(beforeTime)) { System.out.println("WIP " + time + ", " + beforeTime); result.add(time); /* Check if limit has been reached. */ if (limit > 0 && result.size() >= limit) { break; } /* Shift. */ time = time.withFieldAdded( OrgDateTimeUtils.getDurationFieldType(repeater.getUnit()), repeater.getValue() ); } } return result; } private static DateTime applyWarningPeriod(DateTime time, OrgInterval period) { if (period != null) { return time.withFieldAdded( OrgDateTimeUtils.getDurationFieldType(period.getUnit()), -1 * period.getValue()); } else { return time; } } public static DateTime applyDelayPeriod(DateTime time, OrgInterval period) { if (period != null) { return time.withFieldAdded( OrgDateTimeUtils.getDurationFieldType(period.getUnit()), period.getValue()); } else { return time; } } private static PeriodType getPeriodType(OrgInterval.Unit unit) { switch (unit) { case HOUR: return PeriodType.hours(); case DAY: return PeriodType.days(); case WEEK: return PeriodType.weeks(); case MONTH: return PeriodType.months(); case YEAR: return PeriodType.years(); default: throw new IllegalArgumentException("Unknown unit " + unit); } } public static DurationFieldType getDurationFieldType(OrgInterval.Unit unit) { switch (unit) { case HOUR: return DurationFieldType.hours(); case DAY: return DurationFieldType.days(); case WEEK: return DurationFieldType.weeks(); case MONTH: return DurationFieldType.months(); case YEAR: return DurationFieldType.years(); default: throw new IllegalArgumentException("Unknown unit " + unit); } } }