如何找到任意日期的最近工作日?

时间:2011-11-30 05:26:51

标签: java jodatime

使用JodaTime,是否有一种优雅的方式可以找到给定日期的星期几?我最初认为setCopy()会是它,但是这会将当天设置为特定日期在同一周。因此,如果ld2011-11-27day为“星期一”,则以下函数会返回2011-11-21,而不是2011-11-28

    // Note that "day" can be _any_ day of the week, not just weekdays.
    LocalDate getNearestDayOfWeek(LocalDate ld, String day) {
        return ld.dayOfWeek().setCopy(day);
    }

各种输入的所需输出:

2011-12-04, Monday    => 2011-12-05
2011-12-04, Tuesday   => 2011-12-06
2011-12-04, Wednesday => 2011-12-07
2011-12-04, Thursday  => 2011-12-01
2011-12-04, Friday    => 2011-12-02
2011-12-04, Saturday  => 2011-12-03
2011-12-04, Sunday    => 2011-12-04

2011-12-05, Monday    => 2011-12-05
2011-12-05, Tuesday   => 2011-12-06
2011-12-05, Wednesday => 2011-12-07
2011-12-05, Thursday  => 2011-12-08
2011-12-05, Friday    => 2011-12-02
2011-12-05, Saturday  => 2011-12-03
2011-12-05, Sunday    => 2011-12-04

下面是我想出的解决方案,它适用于我当前情况下的特殊限制,但我很乐意帮助找到一个始终有效的完全通用的解决方案。

    LocalDate getNearestDayOfWeek(LocalDate ld, String day) {
        LocalDate target = ld.dayOfWeek().setCopy(day);
        if (ld.getDayOfWeek() > DateTimeConstants.SATURDAY) {
            target = target.plusWeeks(1);
        }
        return target;
    }

6 个答案:

答案 0 :(得分:5)

在Jodatime中,这种事情应该是三行或四行:

   /** Given a reference LocalDate and a day of week, eg DateTimeConstants.MONDAY 
       Returns the nearest date with that day of week */
   public static LocalDate getNearestDayOfWeek(LocalDate t0,int dow) {
        LocalDate t1 = t0.withDayOfWeek(dow);
        LocalDate t2 = t1.isBefore(t0) ? t1.plusWeeks(1) : t1.minusWeeks(1);
        return  Math.abs(Days.daysBetween(t1, t0).getDays()) < 
                Math.abs(Days.daysBetween(t2, t0).getDays()) ? t1 : t2;
   }

或更紧凑和高效:

public static LocalDate getNearestDayOfWeek(LocalDate t0, int dow) {
    LocalDate t1 = t0.withDayOfWeek(dow);
    if (t1.isBefore(t0.minusDays(3)))       return t1.plusWeeks(1);
    else if (t1.isAfter(t0.plusDays(3)))    return t1.minusWeeks(1);
    else return t1;
}

如果你想以星期几的形式传递星期几:

public static LocalDate getNearestDayOfWeek(LocalDate t0, String dow) {
    return getNearestDayOfWeek(t0,t0.dayOfWeek().setCopy(dow).getDayOfWeek());
}

示例:

    // prints 2011-11-28
   public static  void  main(String[] args) throws Exception {
        LocalDate today = new LocalDate(2011,11,27);
        int dow = DateTimeConstants.MONDAY;
        System.out.println(getNearestDayOfWeek(today ,dow ));
   }

答案 1 :(得分:2)

这是解决问题的方法。我对JodaTime有点了解,但并不是所有的类和方法。我假设在给定日期的情况下,您可以获得一周中的某一天以及下一个或之前的日期。

有三种情况。

  1. 特定日期的 dayOfTheWeek 是工作日。返回日期。
  2. dayOfTheWeek 是星期六。从您的日期减去1天。返回日期 - 1天。
  3. dayOfTheWeek 是星期天。将1天添加到您的日期。返回日期+一天。
  4. 如果 dayOfTheWeek 是枚举类型,那么case语句将以直接的方式处理任务。

答案 2 :(得分:2)

通过定义一周中最接近的天数的间隔来查找一周中最近的一天。 Joda将周定义为周一开始。因此,如果今天是星期二,并且星期几设置为星期日,那么日期将是下一个星期日,而不是之前的星期日。如果将一周的第一天重新定义为星期日,则返回的日期将是上一个星期日。以下代码不受本周第一天定义的影响。

DateTime getNearestDayOfWeek(DateTime dateTime, String day) {
  //Create an interval containing the nearest days of the week.
  DateTime begin = dateTime.minusHours(DateTimeConstants.HOURS_PER_WEEK/2).dayOfWeek().roundHalfCeilingCopy();
  DateTime end   = dateTime.plusHours(DateTimeConstants.HOURS_PER_WEEK/2).dayOfWeek().roundHalfCeilingCopy();
  Interval interval = new Interval(begin, end);

  //Adjust nearest day to be within the interval. Doesn't depend on definition of first day of the week.
  DateTime nearest  = dateTime.dayOfWeek().setCopy(day);
  if (interval.isAfter(nearest))  //nearest is before the interval
    return nearest.plusWeeks(1);
  else if (interval.isBefore(nearest))  //nearest is after the interval
    return nearest.minusWeeks(1);
  else 
    return nearest;
}

答案 3 :(得分:2)

像这样的东西。对于dayOfWeek参数,使用org.joda.time.DateTimeConstants中定义的常量:

public LocalDate getNext(int dayOfWeek) {
    LocalDate today = new LocalDate();
    return getNext(dateOfWeek, today);
}

public LocalDate getNext(int dayOfWeek, LocalDate fromDate) {
    int dayOffset = DateTimeConstants.DAYS_PER_WEEK - dayOfWeek + 1;
    LocalDate weekContainingDay = fromDate.plusDays(dayOffset);

    return weekContainingDay.withDayOfWeek(dayOfWeek);
}

用法:

LocalDate nextSunday = foo.getNext(DateTimeConstants.SUNDAY);

答案 4 :(得分:1)

Java 8 中有一个很好的 API,称为 TemporalAdjuster

    LocalDate today = LocalDate.now();
    TemporalAdjuster adjustToNextWed = TemporalAdjusters.next(DayOfWeek.WEDNESDAY);
    TemporalAdjuster adjustToNexOrSametWed = TemporalAdjusters.nextOrSame(DayOfWeek.WEDNESDAY);

    LocalDate nextWed = today.with(adjustToNextWed);
    LocalDate nextWedOrToday = today.with(adjustToNextOrSameWed);

java.time.temporal.TemporalAdjusters 类包含工厂一些常见用例:

firstDayOfMonth, lastDayOfMonth, firstDayOfNextMonth, firstDayOfYear, lastDayOfYear, firstDayOfNextYear, firstInMonth, lastInMonth, dayOfWeekInMonth, next, nextOrSame, previous, previousOrSame

如您所见,“最近”没有工厂,但您可以创建自己的工厂。检查这些方法的来源后,制作自己的方法非常简单:

    // This implementation is expanded and verbose for clarity,
    // see simplified version below
    public static TemporalAdjuster nearest(DayOfWeek dayOfWeek) {
        int targetDay = dayOfWeek.getValue(); // range: +1..+7
        return (temporal) -> {
            int originalDay = temporal.get(DAY_OF_WEEK); // range: +1..+7
            // difference between the target (1..7) and original (1..7) weekdays
            int adjustDays = targetDay - originalDay; // range: -6..+6
            if (adjustDays <= -4) {
                // if the adjustment is 4 or more days ago,
                // next week is closer:
                adjustDays += 7;
            }
            if (adjustDays >= 4) {
                // if the adjustment is 4 or more days in future,
                // previous week is closer:
                adjustDays -= 7;
            }
            return temporal.plus(adjustDays, DAYS);
        };
    }

使用它非常简单:

LocalDate date = LocalDate.now(); // some date to adjust, from your code
date.with(nearest(DayOfWeek.TUESDAY)); // the nearest Tuesday from the date

可以简化为单个表达式,将 -6 到 +6 的范围转换为 -3..+3:

    public static TemporalAdjuster nearest(DayOfWeek dayOfWeek) {
        int targetDay = dayOfWeek.getValue();
        return (temporal) -> {
            int originalDay = temporal.get(DAY_OF_WEEK);
            final int adjustDays = ((targetDay - originalDay + 10)  % 7) - 3;
            return temporal.plus(adjustDays, DAYS);
        };
    }

完整示例:

以下是针对所有天数组合进行测试的完整实现。 您可以运行该程序来验证结果,产生如下输出:

...
original day: MONDAY, target day THURSDAY, difference: -3
  nearest THURSDAY to MONDAY 2021-05-03 is 2021-05-06

original day: MONDAY, target day FRIDAY, difference: -4
  nearest FRIDAY to MONDAY 2021-05-03 is 2021-04-30
...
original day: MONDAY, target day FRIDAY, difference: 4
  nearest FRIDAY to MONDAY 2021-05-03 is 2021-04-30
....

解决方案:

// file TestNearest.java
class TestNearest {

    public static LocalDate nearestDayOfWeek(LocalDate originalDate, DayOfWeek dayOfWeek) {
        return originalDate.with(nearest(dayOfWeek));
    }

    public static TemporalAdjuster nearest(DayOfWeek dayOfWeek) {
        int targetDay = dayOfWeek.getValue();
        return (temporal) -> {
            int originalDay = temporal.get(ChronoField.DAY_OF_WEEK);
            final int adjustment = ((targetDay - originalDay + 10)  % 7) - 3;
            return temporal.plus(adjustment, ChronoUnit.DAYS);
        };
    }

    public static void main(String[] args) {
        for (int original = 1; original <= 7; original++) {
            for (int target = 1; target <= 7; target++) {
                final DayOfWeek originalDayOfWeek = DayOfWeek.of(original);
                final DayOfWeek targetDayOfWeek = DayOfWeek.of(target);
                // Create a test date:
                final LocalDate testOriginalDate = LocalDate.now()
                        .with(TemporalAdjusters.dayOfWeekInMonth(1, originalDayOfWeek));
                final LocalDate nearestDate = nearestDayOfWeek(testOriginalDate, targetDayOfWeek);
                debug(testOriginalDate, targetDayOfWeek, nearestDate);
            }
        }
    }

    private static void debug(LocalDate original, DayOfWeek target, LocalDate result) {
        System.out.println("original day: " + original.getDayOfWeek() +
                ", target day " + target +
                ", difference: " + (target.getValue() - original.getDayOfWeek().getValue()));
        System.out.println("  nearest " + (result.getDayOfWeek()) + " to " +
                (original.getDayOfWeek()) + " " +
                original +
                " is " +
                result);
        System.out.println();
    }
}

答案 5 :(得分:0)

基于Richard Povinelli的回答,但更新为使用Java Time(从Java 8开始)

public static LocalDate getNearestDayOfWeek(LocalDate date, DayOfWeek dayOfWeek) {
    LocalDate start = date.minusDays(3);
    LocalDate end = date.plusDays(3);
    LocalDate guessDate = date.with(dayOfWeek);
    // the nearest day is between start and end, so we adjust our guess if required
    if (guessDate.isAfter(end)) {
        // guessed one week to late
        return guessDate.minusWeeks(1);
    } else if (guessDate.isBefore(start)) {
        // guessed one week to early
        return guessDate.plusWeeks(1);
    } else {
        // the guess was correct
        return guessDate;
    }
}