Как я могу сгенерировать квартальную последовательность дат в 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

Проект 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.

Другие вопросы по тегам