Ответ Ole VV является правильным и разумно использует современные java.time классы.
LocalDateRange
КакВ качестве бонуса я упомяну добавление отличной библиотеки ThreeTen-Extra в ваш проект для доступа к классу LocalDateRange
.Этот класс представляет промежуток времени в виде пары LocalDate
объектов.Кажется, это соответствует вашей бизнес-проблеме.
LocalDate start = … ;
LocalDate stop = … ;
LocalDateRange range = LocalDateRange.of( start , stop ) ;
Из этого объекта LocalDateRange
вы можете получить Period
объекты, используемые в этом Answer by Ole VV
Period period = range.toPeriod() ;
Обратите внимание, что LocalDateRange
имеет удобные методы для сравнения, такие как abuts
, contains
, overlaps
и т. Д.
Half-Open
Вы можетебыть запутанным о том, как обращаться с концом месяца / первым месяцем.
Обычно при обработке даты и времени лучше всего представлять промежуток времени, используя подход Half-Open.В этом подходе начало включительно , а окончание исключительно .
Таким образом, месяц начинается с первого числа месяца и продолжается до, но не включает первое число следующего месяца.
LocalDateRange rangeOfMonthOfNovember2019 =
LocalDateRange.of(
LocalDate.of( 2019 , Month.NOVEMBER , 1 ) ,
LocalDate.of( 2019 , Month.DECEMBER , 1
)
;
Вы можете попросить каждого LocalDateRange
за прошедшие дни: LocalDateRange::lengthInDays
.
DateTimeFormatter f = DateTimeFormatter.ofPattern( "dd/MM/uuuu" );
List < LocalDateRange > ranges = new ArrayList <>( 4 );
ranges.add(
LocalDateRange.of(
LocalDate.parse( "31/03/2017" , f ) ,
LocalDate.parse( "30/09/2017" , f )
)
);
ranges.add(
LocalDateRange.of(
LocalDate.parse( "01/10/2017" , f ) ,
LocalDate.parse( "31/03/2018" , f )
)
);
ranges.add(
LocalDateRange.of(
LocalDate.parse( "01/04/2018" , f ) ,
LocalDate.parse( "30/09/2018" , f )
)
);
ranges.add(
LocalDateRange.of(
LocalDate.parse( "01/10/2018" , f ) ,
LocalDate.parse( "31/12/2019" , f )
)
);
// Sum the periods, one from each range.
Period period = Period.ZERO;
int days = 0;
for ( LocalDateRange range : ranges )
{
days = ( days + range.lengthInDays() );
period = period.plus( range.toPeriod() ).normalized();
}
Дамп на консоль.
System.out.println( "ranges: " + ranges );
System.out.println( "days: " + days + " | pseudo-months: " + ( days / 30 ) + " and days: " + ( days % 30 ) );
System.out.println( "period: " + period );
диапазоны: [2017-03-31 / 2017-09-30, 2017-10-01 / 2018-03-31, 2018-04-01 / 2018-09-30, 2018-10-01 / 2019-12-31]
дней: 1002 |псевдо-месяцы: 33 и дни: 12
период: P2Y5M119D
Если вы настаиваете на том, чтобы быть полностью закрытым, а не полуоткрытым, LocalDateRange
все равно может помочь вам с его ofClosed
метод.Но я настоятельно рекомендую вместо этого принять полуоткрытое, поскольку, как я полагаю, вы обнаружите, что вам будет проще использовать один согласованный подход ко всему коду.И обучайте своих пользователей.Я видел много путаницы среди офисного персонала, делающего неправильные предположения о инклюзивных / эксклюзивных датах.Ваша практика отслеживания конца месяца и конца месяца, вероятно, вызовет еще большую путаницу.
YearMonth
Еще один класс, который может оказаться полезным, встроен в Java: YearMonth
.Этот класс представляет отдельный месяц как единое целое.
YearMonth yearMonth = YearMonth.of( 2019 , Month.NOVEMBER ) ;
Обратите внимание на такие методы, как atDay
для получения LocalDate
из YearMonth
.
ISO 8601
Совет. При сериализации значений даты и времени в виде текста используйте привычку использовать только стандартные форматы ISO 8601, а не локализованные форматы.
Поэтому для даты используйте YYYY-MM-DD.
Классы java.time по умолчанию используют эти форматы при разборе / генерации текста.Поэтому не нужно указывать шаблон форматирования.
LocalDate.parse( "2019-01-23" )
Стандартный формат ISO 8601 для всего месяца - ГГГГ-ММ, например 2019-11
.
YearMonth yearMonth = YearMonth.parse( "2019-11" ) ; // Entire month of November 2019.
О java.time
Среда java.time встроена в Java 8и позже.Эти классы вытесняют проблемные старые устаревшие классы даты и времени, такие как java.util.Date
, Calendar
, & SimpleDateFormat
.
Чтобы узнать больше, см. Oracle Tutorial .И поиск переполнения стека для многих примеров и объяснений.Спецификация: JSR 310 .
Проект Joda-Time , теперь в режиме обслуживания , рекомендует перейти на java.time классы.
Вы можете обмениваться java.time объектами напрямую с вашей базой данных.Используйте драйвер JDBC , совместимый с JDBC 4.2 или более поздней версии.Нет необходимости в строках, нет необходимости в java.sql.*
классах.
Где получить классы java.time?
ThreeTen-Extra Проект расширяет java.time дополнительными классами.Этот проект является полигоном для возможных будущих дополнений к java.time.Здесь вы можете найти некоторые полезные классы, такие как Interval
, YearWeek
, YearQuarter
и more .