4

週末を無視して、2 つの特定の時間インスタンス間の分数を取得しようとしています。これは私がやったことです。

public static final List<Integer> NON_WORKING_DAYS;
static {
    List<Integer> nonWorkingDays = new ArrayList<Integer>();
    nonWorkingDays.add(Calendar.SATURDAY);
    nonWorkingDays.add(Calendar.SUNDAY);
    NON_WORKING_DAYS = Collections.unmodifiableList(nonWorkingDays);
}

public static int getMinsBetween(Date d1, Date d2, boolean onlyBusinessDays)
{
    int minsBetween = (int)((d2.getTime() - d1.getTime()) / (1000 * 60));
    int minsToSubtract = 0;
    if(onlyBusinessDays){
        Calendar dateToCheck = Calendar.getInstance();
        dateToCheck.setTime(d1);
        Calendar dateToCompare = Calendar.getInstance();
        dateToCompare.setTime(d2);


        //moving the first day of the week to Tues so that a Sat, sun and mon fall in the same week, easy to adjust dates
        dateToCheck.setFirstDayOfWeek(Calendar.TUESDAY);
        dateToCompare.setFirstDayOfWeek(Calendar.TUESDAY);

        //moving the dates out of weekends
        if(!isBusinessDay(dateToCheck, NON_WORKING_DAYS)){
            dateToCheck.set(Calendar.DAY_OF_WEEK, Calendar.SATURDAY);
            dateToCheck.set(Calendar.HOUR, 0);
            dateToCheck.set(Calendar.MINUTE, 0);
            dateToCheck.set(Calendar.SECOND, 0);
            dateToCheck.set(Calendar.MILLISECOND, 0);
        }

        if(!isBusinessDay(dateToCompare, NON_WORKING_DAYS)){
            dateToCompare.set(Calendar.DAY_OF_WEEK, Calendar.MONDAY);
            dateToCompare.set(Calendar.HOUR, 0);
            dateToCompare.set(Calendar.MINUTE, 0);
            dateToCompare.set(Calendar.SECOND, 0);
            dateToCompare.set(Calendar.MILLISECOND, 0);
        }

        for(; dateToCheck.getTimeInMillis() < dateToCompare.getTimeInMillis() ; dateToCheck.add(Calendar.DAY_OF_MONTH, 1)){
            if(isBusinessDay(dateToCheck, NON_WORKING_DAYS)){
                minsToSubtract = minsToSubtract + 1440;
            }
        }
    minsBetween = minsBetween - minsToSubtract; 
    }
    return minsBetween;
}


private static boolean isBusinessDay(Calendar dateToCheck, List<Integer> daysToExclude){
    for(Integer dayToExclude : daysToExclude){
        if(dayToExclude != null && dayToExclude == dateToCheck.get(Calendar.DAY_OF_WEEK)) {
            return true; 
        }
        else continue;
    }
    return false;
}

私の論理が正しいかどうか、そうでない場合はどうすればよいか教えてもらえますか? 週末に月が変わると、このコードがどのように動作するかはよくわかりません。

一部のテスト ケースで期待される出力:

  1. 金曜日の午後 6 時、月曜日の午前 6 時 - 12 時間以内に戻る必要があります
  2. 土曜日の午後 12 時、日曜日の午後 12 時 - 0 時間を返す必要があります
  3. 土曜日の午後 12 時、月曜日の午前 6 時 - 6 時間を返す必要があります
4

2 に答える 2

2

Joda-Timeには、コードの複雑さを軽減するための便利な関数が多数付属しているため、Java での日付操作に関するものにはすべてJoda-Timeを使用することを強くお勧めします。

このコードでは JodaTime を使用しています。

public static final List<Integer> NON_WORKING_DAYS;
static {
    List<Integer> nonWorkingDays = new ArrayList<Integer>();
    nonWorkingDays.add(DateTimeConstants.SATURDAY);
    nonWorkingDays.add(DateTimeConstants.SUNDAY);
    NON_WORKING_DAYS = Collections.unmodifiableList(nonWorkingDays);
}

public static Minutes getMinsBetween(DateTime d1, DateTime d2,
        boolean onlyBusinessDays) {

    BaseDateTime startDate = onlyBusinessDays && !isBusinessDay(d1) ?
                new DateMidnight(d1) : d1;
    BaseDateTime endDate = onlyBusinessDays && !isBusinessDay(d2) ?
                new DateMidnight(d2) : d2;

    Minutes minutes = Minutes.minutesBetween(startDate, endDate);

    if (onlyBusinessDays) {
        DateTime d = new DateTime(startDate);

        while (d.isBefore(endDate)) {
            if (!isBusinessDay(d)) {
                Duration dayDuration = new Duration(d, d.plusDays(1));
                minutes = minutes.minus(int) dayDuration.getStandardMinutes());
            }
            d = d.plusDays(1);
        }
    }
    return minutes;
}

private static boolean isBusinessDay(DateTime dateToCheck) {
    return !NON_WORKING_DAYS.contains(dateToCheck.dayOfWeek().get());
}

このコードをテストすると、次の結果が得られます。

DateTime d1 = new DateTime(2013, 1, 4, 18, 0); // a Friday, 6 pm
DateTime d2 = new DateTime(2013, 1, 7, 6, 0);  // the following Monday, 6 am

Minutes minutes = getMinsBetween(d1, d2, true);
System.out.println(minutes.toStandardHours().getHours()); // outputs "12" (in hours)

d1 = new DateTime(2013, 1, 5, 12, 0); // a Saturday, 12 pm
d2 = new DateTime(2013, 1, 6, 12, 0); // the following Sunday, 12 pm

minutes = getMinsBetween(d1, d2, true);
System.out.println(minutes.toStandardHours().getHours()); // outputs "0" (in hours)

d1 = new DateTime(2013, 1, 5, 12, 0); // a Saturday, 12 pm
d2 = new DateTime(2013, 1, 7, 6, 0);  // the following Monday, 6 am

minutes = getMinsBetween(d1, d2, true);
System.out.println(minutes.toStandardHours().getHours()); // outputs "6" (in hours)

週末に月が変わるケースをテストしました: 3 月 29 日金曜日 (午後 6 時) から 4 月 1 日月曜日 (午前 6 時) まで:

d1 = new DateTime(2013, 3, 29, 18, 0);
d2 = new DateTime(2013, 4, 1, 6, 0);

minutes = getMinsBetween(d1, d2, true);
System.out.println(minutes.toStandardHours().getHours());

結果は12時間なので、月替りに効きます。


私の最初の解決策は、夏時間を正しく処理していませんでした。夏時間が変更された日は 24 時間にならないため、分を差し引くときに実際の各日の期間を決定する必要があります。

if (!isBusinessDay(d)) {
    Duration dayDuration = new Duration(d, d.plusDays(1));
    minutes = minutes.minus(int) dayDuration.getStandardMinutes());
}
于 2013-01-03T10:44:32.613 に答える
1

JodaTime は進むべき道なので、@KatjaChristiansen は正しい方向に進んでいます。Java Calendar を使用する必要がある場合、私のソリューションは次のようになります。

private static final long MILLIS_OF_WEEK = TimeUnit.DAYS.toMillis(7);
private static final long MILLIS_OF_WORKWEEK = TimeUnit.DAYS.toMillis(5);

public static int getMinsBetween(Date d1, Date d2, boolean onlyBusinessDays) {
    long duration = d2.getTime() - d1.getTime();
    if (onlyBusinessDays) {
        Date sat = toSaturdayMidnight(d1);
        long timeBeforeWeekend = Math.max(sat.getTime() - d1.getTime(), 0);
        if (duration > timeBeforeWeekend) {
            Date mon = toMondayMidnight(d2);
            long timeAfterWeekend = Math.max(d2.getTime() - mon.getTime(), 0);
            long numberOfWeekends = Math.max((duration / MILLIS_OF_WEEK) - 1, 0);
            duration = numberOfWeekends * MILLIS_OF_WORKWEEK + timeBeforeWeekend + timeAfterWeekend;
        }
    }
    return (int) TimeUnit.MILLISECONDS.toMinutes(duration);
}

private static Date toMondayMidnight(Date date) {
    Calendar cal = Calendar.getInstance();
    cal.setTime(date);
    switch (cal.get(Calendar.DAY_OF_WEEK)) {
        case Calendar.SATURDAY:
        case Calendar.SUNDAY:
            cal.add(Calendar.DAY_OF_MONTH, 7);
    }
    cal.set(Calendar.DAY_OF_WEEK, Calendar.MONDAY);
    toMidnight(cal);
    return cal.getTime();
}

private static Date toSaturdayMidnight(Date date) {
    Calendar cal = Calendar.getInstance();
    cal.setTime(date);
    cal.set(Calendar.DAY_OF_WEEK, Calendar.SATURDAY);
    toMidnight(cal);
    return cal.getTime();
}

private static void toMidnight(Calendar cal) {
    cal.set(Calendar.HOUR_OF_DAY, 0);
    cal.set(Calendar.MINUTE, 0);
    cal.set(Calendar.SECOND, 0);
    cal.set(Calendar.MILLISECOND, 0);
}

これらのテストはパスします:

@Test
public void testWithinSameDay() {
    assertMinsBetween(30, "2013-01-03 9:00", "2013-01-03 9:30");
}
@Test
public void testOverWeekend() {
    assertMinsBetween(60, "2013-01-04 23:30", "2013-01-07 0:30");
}
@Test
public void testWeekendStart() {
    assertMinsBetween(30, "2013-01-05 23:30", "2013-01-07 0:30");
}
@Test
public void testTwoWeeks() {
    assertMinsBetween((int) TimeUnit.DAYS.toMinutes(10), "2013-01-08 23:30", "2013-01-22 23:30");
}
@Test
public void testTwoWeeksAndOneDay() {
    assertMinsBetween((int) TimeUnit.DAYS.toMinutes(11), "2013-01-08 23:30", "2013-01-23 23:30");
}
@Test
public void testOneWeekMinusOneDay() {
    assertMinsBetween((int) TimeUnit.DAYS.toMinutes(4), "2013-01-09 23:30", "2013-01-15 23:30");
}
private void assertMinsBetween(int expected, String start, String end) {
    try {
        assertEquals(expected, getMinsBetween(FORMAT.parse(start), FORMAT.parse(end), true));
    }
    catch (ParseException e) {
        throw new IllegalStateException(e);
    }
}
于 2013-01-03T11:01:58.683 に答える