Получить все пятницы в диапазоне дат в Java

Недавно я столкнулся с задачей, когда мне нужно получить все пятницы в диапазоне дат. Я написал небольшой фрагмент кода и был удивлен, увидев странное поведение.

Ниже мой код:

public class Friday {
    public static void main(String[]args){
        String start = "01/01/2009";
        String end = "12/09/2013";
        String[] startTokens = start.split("/");
        String[] endTokens = end.split("/");
        Calendar  startCal = new GregorianCalendar(Integer.parseInt(startTokens[2]),Integer.parseInt(startTokens[1])-1,Integer.parseInt(startTokens[0]));
        Calendar endCal = new GregorianCalendar(Integer.parseInt(endTokens[2]),Integer.parseInt(endTokens[1])-1, Integer.parseInt(endTokens[0]));

        int startYear = Integer.parseInt(startTokens[2]);
        int endYear = Integer.parseInt(endTokens[2]); 


        int startWeek = startCal.get(Calendar.WEEK_OF_YEAR);
        int endWeek = endCal.get(Calendar.WEEK_OF_YEAR);

        Calendar cal = new GregorianCalendar();
        cal.set(Calendar.DAY_OF_WEEK, Calendar.FRIDAY);
    //  cal.setMinimalDaysInFirstWeek(7);
        ArrayList<String> main = new ArrayList<String>();
        while(startYear <= endYear ){
               cal.set(Calendar.YEAR, startYear);
               System.out.println(cal.getMinimalDaysInFirstWeek());
                if(startYear == endYear){
                    main.addAll(getFridays(startWeek, endWeek, cal));
                }
                else{
                    main.addAll(getFridays(startWeek, 52, cal));
                    startWeek = 1;
                }
                startYear =startYear +1;
        }

        for(String s: main){
            System.err.println(s);
        }
    }
    public static ArrayList<String> getFridays(int startWeek, int endWeek, Calendar cal){
        ArrayList<String> fridays = new ArrayList<String>();
        while(startWeek <= endWeek){
            cal.set(Calendar.WEEK_OF_YEAR, startWeek);
            fridays.add(cal.getTime().toString());
            startWeek = startWeek+1;
        }
        return fridays;
    }
}

Теперь, когда я запустил код, я заметил, что пятницы 2011 года отсутствуют. После некоторой отладки и онлайн-просмотра я понял, что Calendar.WEEK_OF_YEAR является специфичным для локали, и я должен использовать setMinimalDaysInFirstWeek(7) для его исправления.

Так раскоментирована соответствующая строка в приведенном выше коде.

Из того, что я понял, теперь первая неделя года должна начинаться с полной недели года.

Например, 1 января 2010 года в пятницу. Но он не должен появляться в результатах, поскольку я настроил его на то, чтобы эта неделя начиналась с 3 января. Но теперь я все еще вижу 1 января как пятницу

Я очень смущен. Может кто-нибудь объяснить, почему это происходит?

Эти статьи Stackoverflow немного помогли мне:

Почему dec 31 2010 возвращает 1 как неделю года?

Понимание java.util.Calendar WEEK_OF_YEAR

Ответ 1

Вот более простой способ, используя замечательную библиотеку http://www.joda.org/joda-time/:

String start = "01/01/2009";
String end = "12/09/2013";
DateTimeFormatter pattern = DateTimeFormat.forPattern("dd/MM/yyyy");
DateTime startDate = pattern.parseDateTime(start);
DateTime endDate = pattern.parseDateTime(end);

List<DateTime> fridays = new ArrayList<>();

while (startDate.isBefore(endDate)){
    if ( startDate.getDayOfWeek() == DateTimeConstants.FRIDAY ){
        fridays.add(startDate);
    }
    startDate = startDate.plusDays(1);
}

в конце этого, у вас будет пятница в пятничном массиве. Простой?

Или, если вы хотите ускорить процесс, как только вы зайдете в пятницу, вы можете перейти от использования дней, к использованию недель:

String start = "01/01/2009";
String end = "12/09/2013";
DateTimeFormatter pattern = DateTimeFormat.forPattern("dd/MM/yyyy");
DateTime startDate = pattern.parseDateTime(start);
DateTime endDate = pattern.parseDateTime(end);

List<DateTime> fridays = new ArrayList<>();
boolean reachedAFriday = false;
while (startDate.isBefore(endDate)){
    if ( startDate.getDayOfWeek() == DateTimeConstants.FRIDAY ){
        fridays.add(startDate);
        reachedAFriday = true;
    }
    if ( reachedAFriday ){
        startDate = startDate.plusWeeks(1);
    } else {
        startDate = startDate.plusDays(1);
    }
}

Ответ 2

Во-первых, я бы не стал беспокоиться за несколько недель. Установите календарь в начало диапазона и выясните, какой из них DOW, затем увеличивайте его до следующей пятницы, а затем просто добавьте 7 дней, пока вы не достигнете конца диапазона.

На самом деле, поскольку вы всегда только продвигаетесь вперед, должно быть что-то вроде:

int daysToAdd = FridayDOW - currentDOW;
if (daysToAdd < 0) daysToAdd += 7; 
Date startDate = currentDate.add(Calendar.DAYS, daysToAdd);

Да, вот так.

Хорошо, на самом деле, для пинков, вот он в Java 8:

@Test
public void canFindAllFridaysInRange(){
    start = LocalDate.of(2013, 5, 10);
    end = LocalDate.of(2013, 8,30);

    DayOfWeek dowOfStart = start.getDayOfWeek();
    int difference = DayOfWeek.FRIDAY.getValue() - dowOfStart.getValue();
    if (difference < 0) difference += 7;

    List<LocalDate> fridaysInRange = new ArrayList<LocalDate>();

    LocalDate currentFriday = start.plusDays(difference);
    do {
        fridaysInRange.add(currentFriday);
        currentFriday = currentFriday.plusDays(7);
    } while (currentFriday.isBefore(end));

    System.out.println("Fridays in range: " + fridaysInRange);
}

Полюбоваться новыми классами! Конечно, лямбда будет конденсировать это дальше.

Ответ 3

Этот код напечатает все даты с пятницей.

public class Friday {
   public static void main(String[] args) throws ParseException {
     String start = "01/01/2013";
     String end = "12/01/2013";
     SimpleDateFormat dateFormat=new SimpleDateFormat("dd/MM/yyyy");
     Calendar scal=Calendar.getInstance();
     scal.setTime(dateFormat.parse(start));
     Calendar ecal=Calendar.getInstance();
     ecal.setTime(dateFormat.parse(end));

     ArrayList<Date> fridayDates=new ArrayList<>();

     while(!scal.equals(ecal)){
         scal.add(Calendar.DATE, 1);
         if(scal.get(Calendar.DAY_OF_WEEK)==Calendar.FRIDAY){
             fridayDates.add(scal.getTime());
         }
     }

     System.out.println(fridayDates);
 }
}

Ответ 4

TL;DR

someLocalDate.with(                                      // Date-only value without time-of-day and without time zone, represented by `LocalDate` class.
    TemporalAdjusters.nextOrSame ( DayOfWeek.FRIDAY ) )  // Moving from one `LocalDate` object to another, to find the next Friday unless the starting date is already a Friday.
)                                                        // Return a `LocalDate` object.

java.time

Другие ответы устарели. Старые классы java.util.Date/.Calendar были вытеснены в Java 8, а затем новой java.time. Библиотека Joda-Time отличная, она по-прежнему поддерживается и даже вдохновляет java.time. Но команда Joda-Time рекомендует перейти к java.time, как только это будет удобно.

LocalDate

Классы java.time включают LocalDate для значения даты только без времени и времени. См. Tutorial.

Сначала проанализируйте свои строки ввода, чтобы получить объекты LocalDate.

String inputStart = "01/01/2009";
String inputStop = "12/09/2013";  // 258 Fridays.
// String inputStop = "01/01/2009";  // 0 Friday.
// String inputStop = "01/02/2009";  // 1 Friday.

DateTimeFormatter formatter = DateTimeFormatter.ofPattern ( "MM/dd/yyyy" );

LocalDate start = LocalDate.parse ( inputStart , formatter );
LocalDate stop = LocalDate.parse ( inputStop , formatter );

В вашем собственном коде try-catch для исключения в случае плохих входов. И убедитесь, что stop действительно тот же или более поздний, чем start.

TemporalAdjusters

Структура java.time включает в себя интерфейс TemporalAdjuster как способ изменения значений даты и времени. Например, получение следующей или той же самой пятницы для какой-либо конкретной даты. В свою начальную дату вызовите with(TemporalAdjuster adjuster) и передайте предварительно определенную реализацию TemporalAdjuster из класса TemporalAdjusters (обратите внимание на множественное число s). См. Tutorial.

List<LocalDate> fridays = new ArrayList<> ();  // Collect each Friday found.
LocalDate nextOrSameFriday = start.with ( TemporalAdjusters.nextOrSame ( DayOfWeek.FRIDAY ) );
// Loop while we have a friday in hand (non-null) AND that friday is not after our stop date (isBefore or isEqual the stop date).
while ( ( null != nextOrSameFriday ) & (  ! nextOrSameFriday.isAfter ( stop ) ) ) {
    fridays.add ( nextOrSameFriday );  //  Remember this friday.
    nextOrSameFriday = nextOrSameFriday.plusWeeks ( 1 );  // Move to the next Friday, setting up for next iteration of this loop.
}

Дамп для консоли.

System.out.println ( "From: " + start + " to: " + stop + " are " + fridays.size () + " Fridays: " + fridays );

От: 2009-01-01 по: 2013-12-09 - 258 пятниц: [2009-01-02, 2009-01-09, 2009-01-16, 2009-01-23, 2009-01-16- 30, 2009-02-06, 2009-02-13, 2009-02-20, 2009-02-27, 2009-03-06, 2009-03-13, 2009-03-20, 2009-03-27, 2009-04-03, 2009-04-10, 2009-04-17, 2009-04-24, 2009-05-01, 2009-05-08, 2009-05-15, 2009-05-22, 2009- 05-29, 2009-06-05, 2009-06-12, 2009-06-19, 2009-06-26, 2009-07-03, 2009-07-10, 2009-07-17, 2009-07- 24, 2009-07-31, 2009-08-07, 2009-08-14, 2009-08-21, 2009-08-28, 2009-09-04, 2009-09-11, 2009-09-18, 2009-09-10, 2009-10-30, 2009-10-23, 2009-10-30, 2009-11-06, 2009-11-13, 2009- 11-20, 2009-11-27, 2009-12-04, 2009-12-11, 2009-12-18, 2009-12-25, 2010-01-01, 2010-01-08, 2010-01- 15, 2010-01-22, 2010-01-29, 2010-02-05, 2010-02-12, 2010-02-19, 2010-02-26, 2010-03-05, 2010-03-12, 2010-03-19, 2010-03-26, 2010-04-02, 2010-04-09, 2010-04-16, 2010-04-23, 2010-04-30, 2010-05-07, 2010- 05-14, 2010-05-21, 2010-05-28, 2010-06-04, 2010-06-11, 2010-06-18, 2010-06-25, 2010-07-02, 2010-07-09, 2010-07-16, 2010-07-23, 2010-07-30, 2010-08-06, 2010-08-13, 2010-08-20, 2010-08-27, 2010- 09-03, 2010-09-10, 2010-09-17, 2010-09-24, 2010-10-01, 2010-10-08, 2010-10-15, 2010-10-22, 2010-10- 29, 2010-11-05, 2010-11-12, 2010-11-19, 2010-11-26, 2010-12-03, 2010-12-10, 2010-12-17, 2010-12-24, 2010-12-31, 2011-01-07, 2011-01-14, 2011-01-21, 2011-01-28, 2011-02-04, 2011-02-11, 2011-02-18, 2011- 02-25, 2011-03-04, 2011-03-11, 2011-03-18, 2011-03-25, 2011-04-01, 2011-04-08, 2011-04-15, 2011-04- 22, 2011-04-29, 2011-05-06, 2011-05-13, 2011-05-20, 2011-05-27, 2011-06-03, 2011-06-10, 2011-06-17, 2011-06-24, 2011-07-01, 2011-07-08, 2011-07-15, 2011-07-22, 2011-07-29, 2011-08-05, 2011-08-12, 2011- 08-19, 2011-08-26, 2011-09-02, 2011-09-09, 2011-09-16, 2011-09-23, 2011-09-30, 2011-10-07, 2011-10- 14, 2011-10-21, 2011-10-28, 2011-11-04, 2011-11-11, 2011-11-18, 2011-11-25, 2011-12-02, 2011-12-09, 2011-12-16, 2011-12-23, 2011-12-30, 2012-01-06, 2012-01-13, 2012-01-20, 2012-01-27, 2012-02-03, 201 2-02-10, 2012-02-17, 2012-02-24, 2012-03-02, 2012-03-09, 2012-03-16, 2012-03-23, 2012-03-30, 2012- 04-06, 2012-04-13, 2012-04-20, 2012-04-27, 2012-05-04, 2012-05-11, 2012-05-18, 2012-05-25, 2012-06-18, 01, 2012-06-08, 2012-06-15, 2012-06-22, 2012-06-29, 2012-07-06, 2012-07-13, 2012-07-20, 2012-07-27, 2012-08-03, 2012-08-10, 2012-08-17, 2012-08-24, 2012-08-31, 2012-09-07, 2012-09-14, 2012-09-21, 2012- 09-28, 2012-10-05, 2012-10-12, 2012-10-19, 2012-10-26, 2012-11-02, 2012-11-09, 2012-11-16, 2012-11- 23, 2012-11-30, 2012-12-07, 2012-12-14, 2012-12-21, 2012-12-28, 2013-01-04, 2013-01-11, 2013-01-18, 2013-01-25, 2013-02-01, 2013-02-08, 2013-02-15, 2013-02-22, 2013-03-01, 2013-03-08, 2013-03-15, 2013- 03-22, 2013-03-29, 2013-04-05, 2013-04-12, 2013-04-19, 2013-04-26, 2013-05-03, 2013-05-10, 2013-05- 17, 2013-05-24, 2013-05-31, 2013-06-07, 2013-06-14, 2013-06-21, 2013-06-28, 2013-07-05, 2013-07-12, 2013-07-19, 2013-07-26, 2013-08-02, 2013-08-09, 2013-08-16, 2013-08-23, 2013-08-30, 2013-09-06, 2013- 09 -13 2013-201-20 2013 2013, 2013-11-15, 2013-11-22, 2013-11-29, 2013-12-06]


О java.time

Структура java.time встроена в Java 8 и более поздних версий. Эти классы вытесняют неприятные старые legacy классы времени, такие как java.util.Date, Calendar и SimpleDateFormat.

Проект Joda-Time, теперь режим обслуживания, советуем перейти к классам java.time.

Чтобы узнать больше, см. Учебник Oracle. И поиск Qaru для многих примеров и объяснений. Спецификация JSR 310.

Использование JDBC-драйвера, совместимого с JDBC 4.2 или позже вы можете обменивать java.time-объекты непосредственно с вашей базой данных. Нет необходимости в строках или java.sql. * Classes.

Где получить классы java.time?

  • Java SE 8, Java SE 9, а затем
    • Встроенный.
    • Часть стандартного Java API с объединенной реализацией.
    • Java 9 добавляет некоторые незначительные функции и исправления.
  • Java SE 6 и Java SE 7
    • Большая часть функциональных возможностей java.time обратно переносится на Java 6 и 7 в ThreeTen-Backport.
  • Android
    • Более поздние версии реализаций пакетов Android классов java.time.
    • Для более раннего Android проект ThreeTenABP адаптирует ThreeTen-Backport (упомянутый выше). См. Как использовать ThreeTenABP....

Проект ThreeTen-Extra расширяет java.time с дополнительными классами. Этот проект является доказательством возможных будущих дополнений к java.time. Здесь вы можете найти полезные классы, такие как Interval, YearWeek, YearQuarter и больше.

Ответ 5

Вот решение, основанное на новых потоковых функциях Java-8 и использующее мою библиотеку Time4J ( v4.18 или новее):

String start = "01/01/2009";
String end = "12/09/2013";

ChronoFormatter<PlainDate> f =
    ChronoFormatter.ofDatePattern("dd/MM/yyyy", PatternType.CLDR, Locale.ROOT);

PlainDate startDate =
    f.parse(start).with(PlainDate.DAY_OF_WEEK.setToNextOrSame(Weekday.FRIDAY));
PlainDate endDate = f.parse(end);

Stream<PlainDate> fridays =
    DateInterval.stream(Duration.of(1, CalendarUnit.WEEKS), startDate, endDate);

fridays.forEachOrdered(System.out::println);

// output
2009-01-02 
2009-01-09 
... 
2013-08-30 
2013-09-06

// other example: list of fridays in ISO-8601-format
List<String> result =
  DateInterval.between(startDate, endDate)
    .stream(Duration.of(1, CalendarUnit.WEEKS))
    .map((date) -> date.toString()) // or maybe use dd/MM/yyyy => f.format(date)
    .collect(Collectors.toList());

Кстати, Java-9 предложит аналогичное решение (но с исключительной границей конечной даты), см. также эту проблему улучшения.

Ответ 6

с Lamma Date:

List<Date> fridays = Dates.from(2015, 12, 1).to(2016, 1, 1).byWeek().on(DayOfWeek.FRIDAY).build();
for (Date friday: fridays) {
    System.out.println(friday);
}

Ответ 7

public static List<Date> getWeekNumberList(Date currentMonthDate) {
    List<Date> dates = new ArrayList<>(10);
    Calendar startCalendar = Calendar.getInstance();
    startCalendar.setTime(currentMonthDate);
    startCalendar.set(Calendar.DAY_OF_MONTH,
            startCalendar.getActualMinimum(Calendar.DAY_OF_MONTH));
    Calendar endCalendar = Calendar.getInstance();
    endCalendar.setTime(currentMonthDate);
    endCalendar.set(Calendar.DAY_OF_MONTH,
            endCalendar.getActualMaximum(Calendar.DAY_OF_MONTH));

    Date enddate = endCalendar.getTime();
    while (startCalendar.getTime().before(enddate)) {
        if (startCalendar.get(Calendar.DAY_OF_WEEK) == Calendar.MONDAY) {
            Date result = startCalendar.getTime();
            dates.add(result);
            startCalendar.add(Calendar.WEEK_OF_MONTH, 1);
        } else {

            startCalendar.add(Calendar.DAY_OF_MONTH, 1);
        }
    }
    return dates;
}