Как найти ближайший день недели для произвольной даты?

есть ли элегантный способ найти ближайший день недели для данной даты с помощью JodaTime? Я изначально думал setCopy() было бы, но это устанавливает день в конкретный день на той же неделе. Таким образом, если ld is 2011-11-27 и day is "Monday" следующая функция возвращает 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;
    }

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 ));
   }

вот подход к проблеме. Я немного знаю о JodaTime, но не все классы и методы. Я предполагаю, что, учитывая дату, вы можете получить день недели и следующие или предыдущие даты.

есть три случая.

  1. на для выбора для конкретной даты-это будний день. Дата возврата.
  2. на для выборав субботу. Вычесть 1 день от даты. Возвращает дату - 1 день.
  3. на для выборавоскресенье. Добавить 1 день к дате. Верните дату + один день.

Если для выбора является перечисляемым типом, тогда оператор case будет обрабатывать задачу прямым способом.


это находит ближайший день недели, определяя интервал ближайших дней недели. Джода определяет неделю как начинающуюся в понедельник. Поэтому, если сегодня вторник, а день недели назначен на воскресенье, дата будет на следующее воскресенье, а не Предыдущее. Если первый день недели переопределен как воскресенье, возвращаемая дата будет для предыдущего воскресенья. Следующий код не определяется определением первого дня недели.

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;
}

что-то вроде этого. Для параметра dayOfWeek используйте константы, определенные в org.Джода.время.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);

на основе ответа Ричарда Повинелли, но обновлено для использования времени Java (начиная с 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;
    }
}