Подсчет количества дней определенного месяца при заданной продолжительности

Дана продолжительность. Пример: 15 января — 15 марта.

Я хочу подсчитать количество дней, принадлежащих каждому месяцу, в заданной продолжительности. В этом примере количество дней января в этой продолжительности; 15 число дней февраля в этой продолжительности; 28 количество дней марта в этой продолжительности; 15

Я ищу другое решение, которое проходит через каждую дату продолжительности и проверяет, Date.getMonth() = "Month I want to check against"

Есть ли более простой способ сделать это, используя методы Java Date или Java SQL Date или используя любой другой тип даты?


person direndd    schedule 28.12.2019    source источник
comment
SQL кажется неуместным в этом контексте   -  person Strawberry    schedule 28.12.2019
comment
это строка, которая дана, и вам нужно подсчитать количество дней? Вам нужно количество дней в каждом месяце? Года нет в строке?   -  person dassum    schedule 28.12.2019
comment
Дата начала включена? Как насчет даты окончания? В примере вы хотите считать 14 или 15 дней матча? А 28 или 29 февраля? Можем ли мы назвать год, чтобы мы могли определить, является ли он високосным?   -  person Ole V.V.    schedule 28.12.2019
comment
Да, возможно, но вопрос кажется очень широким и малоизученным. Используйте LocalDate и ChronoUnit.DAYS.between(). Не используйте java.util.Date. Этот класс плохо разработан и давно устарел, и, несмотря на имя, не представляет собой дату.   -  person Ole V.V.    schedule 28.12.2019
comment
(1-) Какое отношение это имеет к Swing?   -  person camickr    schedule 28.12.2019


Ответы (3)


Map < YearMonth , Long > с лямбда-синтаксисом

Вот решение, использующее немного краткого кода с использованием потоков и лямбда-выражений. Хотя это решение действительно проходит каждую дату временного диапазона, простота и ясность кода могут перевесить эту неэффективность.

Используйте LocalDate для даты начала и окончания. Используйте YearMonth отслеживать каждый месяц.

LocalDate start = LocalDate.of( 2019 , 1 , 15 );
LocalDate stop = LocalDate.of( 2019 , 3 , 16 );

Создайте Map< /a>, чтобы сохранить количество дней в каждом месяце.

Map < YearMonth, Long > map =
        start
                .datesUntil( stop )
                .collect(
                        Collectors.groupingBy(
                                ( LocalDate localDate ) -> YearMonth.from( localDate ) ,
                                TreeMap::new ,
                                Collectors.counting()
                        )
                );

Дамп на консоль.

{2019-01=17, 2019-02=28, 2019-03=15}

System.out.println( map );
  1. Учитывая начальную дату, LocalDate::datesUntil предоставляет Stream из LocalDate с увеличением на days.

  2. Затем просто сгруппируйте в SortedMap (TreeMap), чтобы хранить месяцы в хронологическом порядке, классифицированном по YearMonth и подсчет дней в этом месяце в диапазоне.

Если вам нужно общее количество дней, вы можете просто сделать

long totalDays = d.datesUntil(LocalDate.of(2019, 3, 16)).count();
person WJS    schedule 28.12.2019
comment
Впечатляющий. Это переход через каждую дату продолжительности, чего задавший вопрос хотел избежать, но, учитывая краткость, в данном случае оно того стоило. - person Ole V.V.; 29.12.2019
comment
Умное решение. Я изменил ваш код, чтобы использовать YearMonth, а не Month, на случай, если диапазон дат превысит 12 месяцев. - person Basil Bourque; 29.12.2019
comment
Хорошая идея. У меня действительно не было времени подумать об этом, пока я не опубликовал ответ, посмотрев localDate. Обратите внимание, что я довольно неуклюже обработал exclusive часть datesUntil, которую также следует доработать. Возможно, добавив день с помощью plus() - person WJS; 29.12.2019
comment
Как вариант, вторым аргументом groupingBy может быть LinkedHashMap::new, чтобы даты отображались в хронологическом порядке. - person WJS; 29.12.2019
comment
@WJS Если вы имеете в виду datesUntil до ограничения 16 марта, но не включая его, это функция, а не ошибка. Подход Half-Open обычно используется для обработки даты и времени и, как правило, является самым разумным способом. - person Basil Bourque; 29.12.2019
comment
Я знаю, что это функция, и я завишу от нее во всех случаях диапазона. Я имел в виду, что я плохо справлялся с этим. Должен быть предоставлен метод для расчета этого, когда к дате окончания добавляется один день, прозрачный для пользователя (если это то, что хочет OP). - person WJS; 29.12.2019
comment
Ах, ДревоКарта. Гораздо лучший выбор. - person WJS; 29.12.2019
comment
Создание экземпляра SortedMap во втором аргументе сработало. Я выбрал TreeMap вместо LinkedHashMap, реализовав интерфейс SortedMap/NavigableMap, а не завися от исходного порядка вставки. - person Basil Bourque; 29.12.2019

Это всего лишь простой пример, который я привел вместе с некоторыми фундаментальными исследованиями.

LocalDate from = LocalDate.of(2019, Month.JANUARY, 15);
LocalDate to = LocalDate.of(2019, Month.MARCH, 15);

DateTimeFormatter monthFormatter = DateTimeFormatter.ofPattern("MMM");

LocalDate date = from;
while (date.isBefore(to)) {
    LocalDate endOfMonth = date.withDayOfMonth(date.lengthOfMonth());
    if (endOfMonth.isAfter(to)) { 
        endOfMonth = to;
    }

    // Inclusive to exclusive comparison
    long days = ChronoUnit.DAYS.between(date, endOfMonth.plusDays(1));
    System.out.println(days + " days in " + date.format(monthFormatter));

    date = date.plusMonths(1).withDayOfMonth(1);
}

Это выведет

17 days in Jan.
28 days in Feb.
15 days in Mar.

Вероятно, есть лучшие способы добиться того же результата, но, как я уже сказал, я просто собрал его вместе с небольшим количеством поиска в Google, методом проб и ошибок.

Как уже было сказано, вам следует избегать использования старых, устаревших и фактически устаревших классов Date, Calendar и связанных с ними классов.

person MadProgrammer    schedule 28.12.2019
comment
И когда вопрос задан на 15 дней в январе, это должно быть ошибка в вопросе, а не в ответе. С 15 по 31 января включительно 17 дней. - person Ole V.V.; 29.12.2019
comment
@ОлеВ.В. Честно говоря, я не мог понять эту часть. - person MadProgrammer; 29.12.2019
comment
Их не было. :-) - person Ole V.V.; 29.12.2019

Попробуй это. Может быть что-то вроде этого вы хотите. Таким образом, он устанавливает дату начала и дату окончания, затем выполняет цикл для каждого месяца до даты окончания и вычисляет количество дней. Я не проверял это полностью, но должно быть близко к вашей концепции.

public static void main(String[] args) throws ParseException {
    String startDateS = "01/15/2019";
    String endDateS = "03/15/2019";

    SimpleDateFormat dateFormat = new SimpleDateFormat("MM/dd/yyyy");
    Date startDate = dateFormat.parse(startDateS);
    Date endDate = dateFormat.parse(endDateS);

    while (endDate.compareTo(startDate) > 0) {
        Calendar c = Calendar.getInstance();
        c.setTime(startDate);
        c.set(Calendar.DAY_OF_MONTH, c.getActualMaximum(Calendar.DAY_OF_MONTH));
        Date endOfMonth = c.getTime();
        if( endDate.compareTo(endOfMonth) > 0 )
            System.out.println("Count Month " + getMonthForInt(c.get(Calendar.MONTH)) + " " + getDifferenceDays(startDate, endOfMonth));
        else
            System.out.println("Count Month " + getMonthForInt(c.get(Calendar.MONTH)) + " " + getDifferenceDays(startDate, endDate));
        c.add(Calendar.DAY_OF_MONTH, 1);
        startDate = c.getTime();
    }
}

static String getMonthForInt(int num) {
    String month = "wrong";
    DateFormatSymbols dfs = new DateFormatSymbols();
    String[] months = dfs.getMonths();
    if (num >= 0 && num <= 11) {
        month = months[num];
    }
    return month;
}

public static int getDifferenceDays(Date d1, Date d2) {
    int daysdiff = 0;
    long diff = d2.getTime() - d1.getTime();
    long diffDays = diff / (24 * 60 * 60 * 1000) + 1;
    daysdiff = (int) diffDays;
    return daysdiff;
}

Вы можете сделать то же самое, используя Java.time в Java 8.

public static void main(String[] args) throws ParseException {
        String startDateS = "01/15/2019";
        String endDateS = "03/15/2019";

        DateTimeFormatter format1 = DateTimeFormatter.ofPattern("MM/dd/yyyy");
        LocalDate startDate = LocalDate.parse(startDateS, format1);
        LocalDate endDate = LocalDate.parse(endDateS, format1);

        while (endDate.compareTo(startDate) > 0) {
            LocalDate endOfMonth = startDate.minusDays(startDate.getDayOfMonth()).plusMonths(1);
            if( endDate.compareTo(endOfMonth) > 0 )
                System.out.println("Count Month " + getMonthForInt(startDate) + " " + getDifferenceDays(startDate, endOfMonth));
            else
                System.out.println("Count Month " + getMonthForInt(startDate) + " " + getDifferenceDays(startDate, endDate));
            startDate = endOfMonth.plusDays(1);
        }
    }

    static String getMonthForInt(LocalDate startDate) {
        return startDate.getMonth().getDisplayName(
                TextStyle.FULL , 
                Locale.US 
            );
    }

    public static long getDifferenceDays(LocalDate d1, LocalDate d2) {
       // return Duration.between(d2.atStartOfDay(), d1.atStartOfDay()).toDays();
        return ChronoUnit.DAYS.between(d1, d2) + 1;
    }
person A Paul    schedule 28.12.2019
comment
Пожалуйста, не учите молодежь пользоваться давно устаревшим и заведомо проблемным классом SimpleDateFormat. По крайней мере, не в качестве первого варианта. И не без оговорок. Сегодня у нас намного лучше java.time, современный API даты и времени Java, и его DateTimeFormatter. - person Ole V.V.; 28.12.2019