Как я могу сгенерировать квартальную последовательность дат в Java?
Я пытаюсь создать функцию в Java, которая генерирует квартальную последовательность дат с учетом даты начала и окончания.
В R, например, я могу сделать это следующим образом:
generateQuarterlySequence = function(startDate, endDate)
{
require(zoo)
# Generate date sequence
dateSequence = seq.Date(from = as.Date(startDate),
to = as.Date(endDate),
by = "quarter")
# Convert to quarters
dateSequence = as.yearqtr(dateSequence, format = "%Y-%m-%d")
# Get rid of extra white space
dateSequence = gsub(" ", "", dateSequence)
return(dateSequence)
}
generateQuarterlySequence(startDate = "2017-06-30", endDate = "2017-12-31")
[1] "2017Q2" "2017Q3" "2017Q4"
Любые рок-звезды хотят показать, как это делается на Java? Вы сделаете этого новичка Java очень счастливым!
Ура, Джо
3 ответа
Я знаю, что в комментарии я предложил ThreeTen-Extra. Тем не менее, вот решение с использованием чистого java.time
как встроенный в Java 8 и выше и доступный в Java 6 и 7 через ThreeTen Backport.
public static List<String> generateQuarterlySequence(LocalDate startDate, LocalDate endDate) {
// first truncate startDate to first day of quarter
int startMonth = startDate.getMonthValue();
startMonth-= (startMonth - 1) % 3;
startDate = startDate.withMonth(startMonth).withDayOfMonth(1);
DateTimeFormatter quarterFormatter
= DateTimeFormatter.ofPattern("uuuuQQQ", Locale.ENGLISH);
List<String> quarterSequence = new ArrayList<>();
// iterate thorough quarters
LocalDate currentQuarterStart = startDate;
while (! currentQuarterStart.isAfter(endDate)) {
quarterSequence.add(currentQuarterStart.format(quarterFormatter));
currentQuarterStart = currentQuarterStart.plusMonths(3);
}
return quarterSequence;
}
Попробуем это с вашими примерами аргументов:
System.out.println(generateQuarterlySequence(LocalDate.of(2017, Month.JUNE, 30),
LocalDate.of(2017, Month.DECEMBER, 31)));
печать
[2017Q2, 2017Q3, 2017Q4]
ТЛ; др
YearQuarter.from( LocalDate.parse( "2017-06-30" ) )
.plusQuarters( 1 )
org.threeten.extra.YearQuarter
Другой Ответ Ole VV, использующий java.time, хорош. Кроме того, вот код, использующий YearQuarter
класс из проекта ThreeTen-Extra, расширяющий java.time дополнительными функциями. Если вы много работаете с четвертями, то стоит добавить библиотеку ThreeTen-Extra в ваш проект.
LocalDate
класс представляет значение только для даты без времени суток и без часового пояса.
LocalDate start = LocalDate.parse( "2017-06-30" );
LocalDate stop = LocalDate.parse( "2017-12-31" );
Исходя из этого, определите квартал года в календарной системе ISO 8601, то есть Q1 - с января по март, Q2 - с апреля по июнь, Q3 - с июля по сентябрь, а Q4 - с октября по декабрь.
YearQuarter yqStart = YearQuarter.from( start );
YearQuarter yqStop = YearQuarter.from( stop );
Соберите серию кварталов как List
,
int initialCapacity = ( int ) ( ChronoUnit.YEARS.between( start , stop ) + 1 ) * 4;
List < YearQuarter > quarters = new ArrayList <>( initialCapacity );
Зацикливайтесь каждый квартал, увеличивая, вызывая plusQuarters
и собрать в список.
YearQuarter yq = yqStart;
while ( ! yq.isAfter( yqStop ) )
{
quarters.add( yq );
// Setup next loop.
yq = yq.plusQuarters( 1 );
}
Дамп на консоль.
System.out.println( start + "/" + stop + " = " + quarters );
2017-06-30 / 2017-12-31 = [2017-Q2, 2017-Q3, 2017-Q4]
О java.time
Инфраструктура java.time встроена в Java 8 и более поздние версии. Эти классы вытесняют проблемные старые классы даты и времени, такие как java.util.Date
, Calendar
& SimpleDateFormat
,
Проект Joda-Time, находящийся сейчас в режиме обслуживания, рекомендует перейти на классы java.time.
Чтобы узнать больше, смотрите Oracle Tutorial. И поиск переполнения стека для многих примеров и объяснений. Спецификация JSR 310.
Где взять классы 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
- Проект ThreeTenABP адаптирует ThreeTen-Backport (упомянутый выше) специально для Android.
- Смотрите Как использовать ThreeTenABP….
Проект ThreeTen-Extra расширяет java.time дополнительными классами. Этот проект является полигоном для возможных будущих дополнений к java.time. Вы можете найти некоторые полезные классы здесь, такие как Interval
, YearWeek
, YearQuarter
и многое другое.
Моя библиотека Time4J позволяет следующее очень простое решение с использованием потокового API Java-8:
DateInterval range =
DateInterval.between(
PlainDate.of(2017, 6, 30),
PlainDate.of(2017, 12, 31)
);
range.stream(Duration.of(1, CalendarUnit.QUARTERS))
.map(CalendarQuarter::from)
.forEach(System.out::println);
// 2017-Q2
// 2017-Q3
// 2017-Q4
Код сначала строит интервал дат, который можно обработать. Вы можете определить поток, многократно добавляя четверть года к начальной дате интервала. Операция map отображает сгенерированные (григорианские) даты в потоке в желаемый квартал года, называемый CalendarQuarter в Time4J, и, наконец, вызывает его метод toString() для получения выходных данных.
Если вы предпочитаете исключать дефис в выходных данных, вы можете применить простой метод string-replace-метод в другом map-методе или использовать подходящий форматировщик (который лучше всего хранить в статической константе, поскольку она неизменна):
ChronoFormatter<CalendarQuarter> f =
ChronoFormatter.ofPattern(
"uuuuQQQ", PatternType.CLDR, Locale.ROOT, CalendarQuarter.chronology());
range.stream(Duration.of(1, CalendarUnit.QUARTERS))
.map(CalendarQuarter::from)
.map(cq -> f.format(cq))
.forEach(System.out::println);
// 2017Q2
// 2017Q3
// 2017Q4
Последний фрагмент кода можно даже упростить, исключив промежуточный тип CalendarQuarter
потому что григорианская дата (здесь: PlainDate
) также можно форматировать как квартальную дату аналогичным образом (операция на одну карту меньше).
Вот решение с использованием простых потоков Java 8 и java.time api:
public static void main(String[] args) {
LocalDate startDate = LocalDate.of(2000, 12, 25);
LocalDate endDate = LocalDate.of(2002, 4, 1);
Stream<LocalDate> quarterBounds = Stream.iterate(
startDate.with(IsoFields.DAY_OF_QUARTER, 1), date -> date.plus(3, MONTHS));
DateTimeFormatter quarterFormatter =
DateTimeFormatter.ofPattern("uuuuQQQ", Locale.ENGLISH);
quarterBounds
.filter(d -> !endDate.isBefore(d))
.peek(System.out::println)
.map(quarterFormatter::format)
.forEach(System.out::println);
}
Пример вывода:
2000-10-01
2000Q4
2001-01-01
2001Q1
2001-04-01
2001Q2
2001-07-01
2001Q3
2001-10-01
2001Q4
2002-01-01
2002Q1
2002-04-01
2002Q2
Через Как получить первую и последнюю дату текущего квартала в java.util.Date.