在Joda Time

时间:2017-03-10 05:34:10

标签: java jodatime

在给定开始日期,结束日期和周期性的情况下,是否可以使用Joda Time获取间隔列表?

每周期间的示例:

* start date: WED 2017-03-01 00:00
* end date: TUE 2017-03-14 00:00
* periodicity: MON 04:00 - WED 06:00

结果应该是适合开始和结束日期的所有时段,因此根据示例:

WED 2017-03-01 00:00 - WED 2017-03-01 06:00
MON 2017-03-06 04:00 - WED 2017-03-08 06:00
MON 2017-03-13 04:00 - TUE 2017-03-14 00:00

有没有人知道如何在Joda Time中这样做?最好是通用的,i。即与其他类型的期间,如每日/每周/每月/每年/等。

非常感谢您的专业知识!

1 个答案:

答案 0 :(得分:0)

在深入了解Joda Time后,我自己找到了解决方案。我在这里发布,也许其他人也需要它。如果有人对Joda Time更熟悉,请随时改进代码。

算法:

  • 使用给定期间创建部分
  • 将部分应用于间隔开始日期并创建期间开始和结束日期
  • 将期间开始和结束日期都移动到间隔开始日期之前
  • 如果期末结束日期在期间开始日期之前,则将其提前1个持续时间步骤
  • 持续时间段的提前期间,直到期间开始日期在间隔结束日期之后

我真的不想做自己的计算,所以算法只使用api,因此可以使用优化(例如for循环来获取星期几)。

所以如果有人需要代码,这里是:

Periodicity.java

import org.joda.time.DurationFieldType;

/**
 * Supported periodicity: daily, weekly, yearly, week of year
 */
public enum Periodicity {

    DAILY( DurationFieldType.days(), 1),
    WEEKLY( DurationFieldType.weeks(), 1),
    YEARLY( DurationFieldType.years(), 1),
    WEEK_OF_YEAR( DurationFieldType.weekyears(), 1),
    ;

    DurationFieldType durationFieldType;
    int durationAmount;

    private Periodicity( DurationFieldType durationFieldType, int durationAmount) {
        this.durationFieldType = durationFieldType;
        this.durationAmount = durationAmount;
    }

    public DurationFieldType getDurationFieldType() {
        return durationFieldType;
    }

    public int getDurationAmount() {
        return durationAmount;
    }

}

IntervalCreator.java

import java.util.ArrayList;
import java.util.List;

import org.joda.time.DateTime;
import org.joda.time.DateTimeConstants;
import org.joda.time.DateTimeFieldType;
import org.joda.time.DateTimeZone;
import org.joda.time.DurationFieldType;
import org.joda.time.Interval;
import org.joda.time.MutableDateTime;
import org.joda.time.Partial;

public class IntervalCreator {

    MutableDateTime startInstant;
    MutableDateTime endInstant;
    Partial startPartial;
    Partial endPartial;
    DurationFieldType durationFieldType;
    int durationAmount;

    MutableDateTime mutableStartInstant;
    MutableDateTime mutableEndInstant;

    public IntervalCreator(DateTime startInstant, DateTime endInstant, Partial startPartial, Partial endPartial, Periodicity periodicity) {

        this.startInstant = new MutableDateTime(startInstant, DateTimeZone.UTC);
        this.endInstant = new MutableDateTime(endInstant, DateTimeZone.UTC);
        this.startPartial = startPartial;
        this.endPartial = endPartial;
        this.durationFieldType = periodicity.getDurationFieldType();
        this.durationAmount = periodicity.getDurationAmount();
    }

    /**
     * Apply partial datetime to given instant 
     * @param mutableInstant
     * @param partial
     */
    private void applyPartial(MutableDateTime mutableInstant, Partial partial) {

        for (int i = 0; i < partial.getFields().length; i++) {

            DateTimeFieldType fieldType = partial.getFieldTypes()[i];

            if (fieldType == DateTimeFieldType.dayOfWeek()) {

                // find day of week by going backwards in time
                // in opposite to DateTime the MutableDateTime doesn't have a withDayOfWeek() method
                int dayOfWeek = partial.getValue(i);

                while (mutableInstant.getDayOfWeek() != dayOfWeek) {
                    mutableInstant.addDays(-1);
                }

            } else if (fieldType == DateTimeFieldType.minuteOfDay()) {

                mutableInstant.setMinuteOfHour(partial.getValue(i));

            } else if (fieldType == DateTimeFieldType.hourOfDay()) {

                mutableInstant.setHourOfDay(partial.getValue(i));

            } else if (fieldType == DateTimeFieldType.dayOfMonth()) {

                mutableInstant.setDayOfMonth(partial.getValue(i));

            } else if (fieldType == DateTimeFieldType.monthOfYear()) {

                mutableInstant.setMonthOfYear(partial.getValue(i));

            } else if (fieldType == DateTimeFieldType.weekOfWeekyear()) {

                mutableInstant.setWeekOfWeekyear(partial.getValue(i));
                mutableInstant.setDayOfWeek(DateTimeConstants.MONDAY); // reset weekday to monday

            } else {

                throw new IllegalArgumentException("Illegal DateTimeFieldType: " + fieldType);

            }
        }

    }

    /**
     * Add specified duration
     * @param instant
     */
    private void nextInstant(MutableDateTime instant) {
        addDuration( instant, durationAmount);
    }

    /**
     * Subtract specified duration
     * @param instant
     */
    private void previousInstant(MutableDateTime instant) {
        addDuration( instant, -durationAmount);
    }

    /**
     * Convenience method to add a duration to an instant
     * @param instant
     * @param amount
     */
    private void addDuration(MutableDateTime instant, int amount) {

        if (durationFieldType == DurationFieldType.days()) {
            instant.addDays(amount);
        } else if (durationFieldType == DurationFieldType.weeks()) {
            instant.addWeeks(amount);
        } else if (durationFieldType == DurationFieldType.years()) {
            instant.addYears(amount);
        } else if (durationFieldType == DurationFieldType.weekyears()) {
            instant.addWeekyears(amount);
        } else {
            throw new IllegalArgumentException("Illegal duration field type: " + durationFieldType);
        }

    }

    /**
     * Create interval slices
     * @return
     */
    public List<Interval> createIntervals() {

        this.mutableStartInstant = new MutableDateTime(startInstant, DateTimeZone.UTC);
        this.mutableEndInstant = new MutableDateTime(endInstant, DateTimeZone.UTC);

        applyPartial(mutableStartInstant, startPartial);
        applyPartial(mutableEndInstant, endPartial);

        List<Interval> list = new ArrayList<>();

        // search previous valid start date before the global start date
        while (mutableStartInstant.isAfter(startInstant)) {
            previousInstant(mutableStartInstant);
        }

        // search previous valid end date
        while (mutableEndInstant.isAfter(startInstant)) {
            previousInstant(mutableEndInstant);
        }

        // correct the end date in case it is before the start date (e. g. if
        // you have 22:00 - 02:00; or 00:00 - 00:00)
        if (mutableEndInstant.compareTo(mutableStartInstant) <= 0) {
            nextInstant(mutableEndInstant);
        }

        // advance in period steps until the local start date exceeds the
        // global end date
        while (mutableStartInstant.compareTo(endInstant) < 0) {

            // clip local start date at global start date bounds, if
            // necessary
            MutableDateTime clippedStartDate = mutableStartInstant;
            if (mutableStartInstant.compareTo(startInstant) <= 0 && mutableEndInstant.compareTo(startInstant) >= 0) {
                clippedStartDate = startInstant;
            }

            // clip local end date at global end date bounds, if necessary
            MutableDateTime clippedEndDate = mutableEndInstant;
            if (mutableStartInstant.compareTo(endInstant) <= 0 && mutableEndInstant.compareTo(endInstant) >= 0) {
                clippedEndDate = endInstant;
            }

            // create period; ensure the interval is valid (it might not be
            // if the initial interval is entirely before the global start date
            if (clippedStartDate.compareTo(startInstant) >= 0 && clippedEndDate.compareTo(endInstant) <= 0) {

                Interval period = new Interval(clippedStartDate, clippedEndDate);

                list.add(period);

            }

            // next duration
            nextInstant(mutableStartInstant);
            nextInstant(mutableEndInstant);

        }

        return list;

    }

}

和一个测试类

JodaTimeTest.java

import java.util.List;
import java.util.Locale;

import org.joda.time.DateTime;
import org.joda.time.DateTimeConstants;
import org.joda.time.DateTimeFieldType;
import org.joda.time.DateTimeZone;
import org.joda.time.Interval;
import org.joda.time.Partial;
import org.joda.time.format.DateTimeFormat;
import org.joda.time.format.DateTimeFormatter;

public class JodaTimeTest {

    public static DateTimeFormatter formatter = DateTimeFormat.forPattern("w E yyyy-MM-dd HH:mm");

    public static List<Interval> createDailyPeriod() {

        DateTime startInstant = new DateTime(2017, 3, 22, 0, 0, 0, 0, DateTimeZone.UTC);
        DateTime endInstant = new DateTime(2017, 3, 30, 0, 0, 0, 0, DateTimeZone.UTC);

        DateTimeFieldType[] fields = new DateTimeFieldType[] { DateTimeFieldType.hourOfDay(), DateTimeFieldType.minuteOfDay() };
        Partial startPartial = new Partial( fields, new int[] { 23, 0 });
        Partial endPartial = new Partial( fields, new int[] { 2, 0 });

        IntervalCreator intervalCreator = new IntervalCreator(startInstant, endInstant, startPartial, endPartial, Periodicity.DAILY);
        List<Interval> intervals = intervalCreator.createIntervals();

        log(intervals);

        return intervals;

    }

    public static List<Interval> createWeeklyPeriod() {

        DateTime startInstant = new DateTime(2017, 3, 1, 0, 0, 0, 0, DateTimeZone.UTC);
        DateTime endInstant = new DateTime(2017, 3, 14, 0, 0, 0, 0, DateTimeZone.UTC);

        DateTimeFieldType[] fields = new DateTimeFieldType[] { DateTimeFieldType.dayOfWeek(), DateTimeFieldType.hourOfDay(), DateTimeFieldType.minuteOfDay() };
        Partial startPartial = new Partial(fields, new int[] { DateTimeConstants.MONDAY, 4, 0 });
        Partial endPartial = new Partial(fields, new int[] { DateTimeConstants.WEDNESDAY, 6, 0 });

        IntervalCreator intervalCreator = new IntervalCreator(startInstant, endInstant, startPartial, endPartial, Periodicity.WEEKLY);
        List<Interval> intervals = intervalCreator.createIntervals();

        log(intervals);

        return intervals;

    }

    public static List<Interval> createYearlyPeriod() {

        DateTime startInstant = new DateTime(2012, 3, 1, 0, 0, 0, 0, DateTimeZone.UTC);
        DateTime endInstant = new DateTime(2017, 3, 14, 0, 0, 0, 0, DateTimeZone.UTC);

        DateTimeFieldType[] fields = new DateTimeFieldType[] { DateTimeFieldType.monthOfYear(), DateTimeFieldType.dayOfMonth(), DateTimeFieldType.hourOfDay(), DateTimeFieldType.minuteOfDay() };
        Partial startPartial = new Partial(fields, new int[] { 2, 27, 4, 0 });
        Partial endPartial = new Partial(fields, new int[] { 3, 16, 6, 0 });

        IntervalCreator intervalCreator = new IntervalCreator(startInstant, endInstant, startPartial, endPartial, Periodicity.YEARLY);
        List<Interval> intervals = intervalCreator.createIntervals();

        log(intervals);

        return intervals;

    }

    public static List<Interval> createYearlyPeriodByCalendarWeek() {

        DateTime startInstant = new DateTime(2012, 3, 1, 0, 0, 0, 0, DateTimeZone.UTC);
        DateTime endInstant = new DateTime(2017, 1, 10, 0, 0, 0, 0, DateTimeZone.UTC);

        DateTimeFieldType[] fields = new DateTimeFieldType[] { DateTimeFieldType.weekOfWeekyear(), DateTimeFieldType.hourOfDay(), DateTimeFieldType.minuteOfDay() };
        Partial startPartial = new Partial(fields, new int[] { 1, 2, 0 });
        Partial endPartial = new Partial(fields, new int[] { 3, 6, 0 });

        IntervalCreator intervalCreator = new IntervalCreator(startInstant, endInstant, startPartial, endPartial, Periodicity.WEEK_OF_YEAR);
        List<Interval> intervals = intervalCreator.createIntervals();

        log(intervals);

        return intervals;

    }

    private static void log(List<Interval> intervals) {

        for (int i = 0; i < intervals.size(); i++) {
            Interval interval = intervals.get(i);
            System.out.println("Interval " + i + ": " + formatter.print(interval.getStart()) + " - " + formatter.print(interval.getEnd()));
        }

    }


    public static void main(String[] args) {

        Locale.setDefault(Locale.ENGLISH);

        System.out.println("daily:");
        createDailyPeriod();

        System.out.println("\nweekly:");
        createWeeklyPeriod();

        System.out.println("\nyearly:");
        createYearlyPeriod();

        System.out.println("\nyearly by calendar week:");
        createYearlyPeriodByCalendarWeek();

        System.exit(0);
    }
}

测试输出:

daily:
Interval 0: 12 Wed 2017-03-22 00:00 - 12 Wed 2017-03-22 02:00
Interval 1: 12 Wed 2017-03-22 23:00 - 12 Thu 2017-03-23 02:00
Interval 2: 12 Thu 2017-03-23 23:00 - 12 Fri 2017-03-24 02:00
Interval 3: 12 Fri 2017-03-24 23:00 - 12 Sat 2017-03-25 02:00
Interval 4: 12 Sat 2017-03-25 23:00 - 12 Sun 2017-03-26 02:00
Interval 5: 12 Sun 2017-03-26 23:00 - 13 Mon 2017-03-27 02:00
Interval 6: 13 Mon 2017-03-27 23:00 - 13 Tue 2017-03-28 02:00
Interval 7: 13 Tue 2017-03-28 23:00 - 13 Wed 2017-03-29 02:00
Interval 8: 13 Wed 2017-03-29 23:00 - 13 Thu 2017-03-30 00:00

weekly:
Interval 0: 9 Wed 2017-03-01 00:00 - 9 Wed 2017-03-01 06:00
Interval 1: 10 Mon 2017-03-06 04:00 - 10 Wed 2017-03-08 06:00
Interval 2: 11 Mon 2017-03-13 04:00 - 11 Tue 2017-03-14 00:00

yearly:
Interval 0: 9 Thu 2012-03-01 00:00 - 11 Fri 2012-03-16 06:00
Interval 1: 9 Wed 2013-02-27 04:00 - 11 Sat 2013-03-16 06:00
Interval 2: 9 Thu 2014-02-27 04:00 - 11 Sun 2014-03-16 06:00
Interval 3: 9 Fri 2015-02-27 04:00 - 12 Mon 2015-03-16 06:00
Interval 4: 8 Sat 2016-02-27 04:00 - 11 Wed 2016-03-16 06:00
Interval 5: 9 Mon 2017-02-27 04:00 - 11 Tue 2017-03-14 00:00

yearly by calendar week:
Interval 0: 1 Mon 2012-12-31 02:00 - 3 Mon 2013-01-14 06:00
Interval 1: 1 Mon 2013-12-30 02:00 - 3 Mon 2014-01-13 06:00
Interval 2: 1 Mon 2014-12-29 02:00 - 3 Mon 2015-01-12 06:00
Interval 3: 1 Mon 2016-01-04 02:00 - 3 Mon 2016-01-18 06:00
Interval 4: 1 Mon 2017-01-02 02:00 - 2 Tue 2017-01-10 00:00