2015-12-16 1 views
3

У меня есть два объекта календарьУзнайте, если ряд дат охватывающего интервал

Calendar startCalendar = new GregorianCalendar(2013,0,31); 

Calendar endCalendar = new GregorianCalendar(); 

Я хочу знать, если интервал между двумя датами, перечисленных выше охватываются п других объектов парных календарей без отверстий между интервалы

example1:

Calendar startCalendar1(2013,0,31); 
Calendar endCalendar1(2014,0,31); 
Calendar startCalendar2(2013,5,31); 
Calendar endCalendar2(); 

Is ХОРОШО

Example2:

Calendar startCalendar1(2013,0,31); 
Calendar endCalendar1(2014,0,31); 
Calendar startCalendar2(2014,2,31); 
Calendar endCalendar2(); 

НЕ ХОРОШО

Я использую Java 6 Благодаря

+1

В чем проблема, с которой вы столкнулись? –

+0

Возможно, что-то вроде [этого] (http://stackoverflow.com/questions/25735407/validate-item-fall-within-start-date-and-end-date/25735601#25735601) или [это] (http: /stackoverflow.com/questions/20677541/date-range-in-date-range/20678485#20678485) – MadProgrammer

+0

Можете ли вы использовать Java 8 или время Joda? – assylias

ответ

3

1 Rude но простой метод

Используйте Set < Long>

Set<Long> all_times_in_milli=new HashSet<Long>(); 
// Put every interval 

// interval 1 
for (long time_in_millis=startCalendar1.getTimeInMillis(); 
     time_in_millis<= endCalendar1.getTimeInMillis(); 
     time_in_millis+=86400000) 
     all_times_in_milli.add(time_in_millis); 

// interval 2 
for (long time_in_millis=startCalendar2.getTimeInMillis(); 
     time_in_millis<= endCalendar2.getTimeInMillis(); 
     time_in_millis+=86400000) 
     all_times_in_milli.add(time_in_millis); 

// ETC 
// AND TEST ! 
boolean failed=false; 
for (long time_in_millis=startCalendar.getTimeInMillis(); 
     time_in_millis<= endCalendar.getTimeInMillis(); 
     time_in_millis+=86400000) 
     { 

     if (all_times_in_milli.contains(time_in_millis)) 
      { 
      failed=true; break; 
      } 
     } 

if (failed) System.out.println("Your are done !"); 

2 SMARTER МЕТОД Вал является [длинный - длинный] интервал

  • собрать ваши интервалы, чтобы получить непрерывные интервалы (множества перекрывающихся интервалов) =>, то вы получите B1-E1, B2-E2, B3-E3 distincts Интервал замены
  • чек если вы первый интервал внутри них: B1 = < < начать = конец < = E1, или B2 < = начало < = конец < = E2, ...

интересно, только если у вас есть много данных

2

Вы используете старые классы времени, которые были вытеснены java.time framework в Java 8 и более поздних версиях. Те старые классы оказались неуклюжими, запутанными и ошибочными.

java.time

Новые java.time классы навеяны весьма успешная Joda‑Time библиотекой, предназначенный в качестве своего преемника, аналогичного по концепции, но повторно спроектирован. Определено JSR 310. Продлен проект ThreeTen‑Extra. См. Tutorial.

Новые классы включают в себя LocalDate только для даты и времени без времени суток. Для вашей цели используйте это вместо Calendar.

Обратите внимание, что цифры месяца разумно начинаются с одного, в отличие от Calendar.

LocalDate start = LocalDate.of(2013 , 1 , 31); 

Обратите внимание, что для определения даты часовой пояс имеет решающее значение. Дата не одинакова во всем мире. Например, в Париже новый день предстает раньше, чем в Монреале.

ZoneId zoneId = ZoneId.of ("America/Montreal"); 
LocalDate today = LocalDate.now (zoneId); 

Оттуда вы можете назвать любую комбинацию isAfter, isBefore или isEqual сделать вашу логику. Ваш вопрос не совсем ясен в отношении этой логики, поэтому я не могу это решить.

Проект ThreeTen-Extra, расширяющий java.time, включает в себя класс Interval, который поможет вам. К сожалению, этот класс работает только с объектами Instant (дата-время в UTC), а не LocalDate. В частности, методы сравнения интервалов помогут, abuts, encloses и overlaps.

Вы бы взломали свой собственный класс IntervalLD для объектов LocalDate. Обычно я не рекомендую переводить свои собственные классы обработки даты, потому что работа с датами на удивление сложна. Но в этом случае с LocalDate логика может простой. Вот моя краткая черновик совершенно непроверенный пример, чтобы вы начали.

package com.example.javatimestuffmaven; 

import java.time.LocalDate; 

/** 
* Similar to the 'Interval'class in the ThreeTen-Extra project, but for LocalDate objects. 
* 
* @author Basil Bourque 
*/ 
public class IntervalLD { 

    private LocalDate start, end; 

    // Constructor 
    public IntervalLD (LocalDate startArg , LocalDate endArg) { 
     this.start = startArg; 
     this.end = endArg; 
    } 

    public Boolean isBefore (IntervalLD interval) { 
     // True if this one's end is before that one's start. 
     boolean before = this.getEnd().isBefore (interval.getStart()); 
     return before; 
    } 

    public Boolean isAfter (IntervalLD interval) { 
     // True if this one's start is after that one's end. 
     boolean after = this.getStart().isAfter (interval.getStart()); 
     return after; 
    } 

    public Boolean abuts (IntervalLD interval) { 
     // True if the intervals are next to each other on the time line but do not share a date. (exclusive of each other, not half-open) 
     // True if either one's end is a day ahead of the other's start or vice versa, either's start is day after the other's end. 
     if (this.isBefore (interval)) { 
      if (this.getEnd().plusDays (1).equals (interval.getStart())) { 
       return Boolean.TRUE; 
      } else { 
       return Boolean.FALSE; 
      } 
     } else if (this.isAfter (interval)) { 
      if (this.getStart().minusDays (1).equals (interval.getEnd())) { 
       return Boolean.TRUE; 
      } else { 
       return Boolean.FALSE; 
      } 
     } else if (this.isEqual (interval)) { 
      return Boolean.FALSE; 
     } 

     // Impossible. Should never reach this point. 
     // TODO: Handle this error condition. 
     return Boolean.FALSE; 
    } 

    public Boolean encloses (IntervalLD interval) { 
     //This checks if the specified interval is fully enclosed by this interval. 
     // The result is true if the start of the specified interval is contained in this interval, and 
     // the end is contained or equal to the end of this interval. 
     boolean thatOneStartsOnOrAfterThisOne = ! interval.getStart().isBefore (this.getStart()); 
     boolean thatOneEndsOnOrAfterThisOne = ! interval.getEnd().isAfter (this.getEnd()); 
     boolean doesEnclose = (thatOneStartsOnOrAfterThisOne && thatOneEndsOnOrAfterThisOne); 
     return doesEnclose; 
    } 

    public Boolean overlaps (IntervalLD interval) { 
     // True if the two intervals share some part of the timeline. 
     // True if this interval does NOT start after that one ends OR this interval does NOT end before that one starts. 
     boolean startsTooLate = this.getStart().isAfter (interval.getEnd()); 
     boolean endsTooEarly = this.getEnd().isAfter (interval.getEnd()); 
     boolean doesOverlap = ( ! startsTooLate && ! endsTooEarly); 
     return (doesOverlap); 
    } 

    public Boolean isEqual (IntervalLD interval) { 
     boolean sameStart = this.getStart().isEqual (interval.getStart()); 
     boolean sameEnd = this.getEnd().isEqual (interval.getEnd()); 
     return (sameStart && sameEnd); 
    } 

    @Override 
    public String toString() { 
     String output = this.getStart() + "/" + this.getEnd(); 
     return output; 
    } 

    // Getters. Read-only (immutable) so no Setters. 
    /** 
    * @return the start 
    */ 
    public LocalDate getStart() { 
     return this.start; 
    } 

    /** 
    * @return the end 
    */ 
    public LocalDate getEnd() { 
     return this.end; 
    } 
} 
3

Первый подход: только с помощью Java 6

Когда я вижу ваши примеры дат, как 2015-01-31 тогда я получаю сильное подозрение, что вы говорите о замкнутых интервалов дат иначе выбирая конец месяца может показаться немного странным. Это широко распространенный и разумный подход. К сожалению, выбор типа данных, такого как java.util.Calendar, представляющий собой мгновенный (также комбинированный с датой и зоной времени), не согласуется с закрытыми интервалами. Подобные типа типа работают лучше с полуоткрытыми интервалами. Следствием этого является:

Если вы решили использовать только Java-6-типа, то вы можете попытаться преобразовать все Calendar -Объекты в длинных значений, представляющих истекшее миллисекунды с момента Unix эпохи, как было предложено @guillaume Жиро-vitouchkina (имеет я получил в качестве примера пример, как это сделать без какой-либо внешней библиотеки). Но вы должны добавить дополнительный день к каждому Calendar -объекту (если изображать конечную границу) заранее, чтобы добиться эффекта закрытых интервалов дат.

И, конечно же, вы все равно должны сделать некоторые домашние арифметические интервалы, как показано в этом ответе отрывочным способом. Если вы внимательно изучите другие предложения и свои собственные требования, вы обнаружите, что для окончательного решения требуется даже больше, чем просто новый класс интервалов или базовые сравнения интервалов. Вам также потребуется более высокий уровень абстракции, а именно определенные операции между несколькими интервалами. Это может привести к некоторой головной боли. С другой стороны: реализация арифметики с длинным интервалом может сэкономить некоторые издержки производительности как типичные для дополнительной библиотеки интервалов, если у вас хорошие навыки программирования.

Второй подход: Использование выделенного интервала библиотеки

Я знаю только четыре библиотеки, которые обещают справиться с интервалами. Threeten-Extra, упомянутый @Basil Bourque, не может быть использован, поскольку для него требуется Java-8. У его интервала класса есть недостаток, чтобы обрабатывать только моменты, но не даты календаря. Также почти нет поддержки для обработки коллекций интервалов. То же самое можно сказать и о Joda-Time (который, по крайней мере, работает на Java-6, а также предлагает специальный тип календарной даты, а именно LocalDate, но без интервалов времени).

Интересный вариант использует Guava и его класс RangeSet, особенно если вы решите продолжить использование Calendar -Объектов и Longs. Этот класс имеет некоторую поддержку для обработки операций между интервалами - для меня это гораздо более привлекательно, чем использование простого класса интервалов Joda-Time.

Наконец, у вас также есть возможность использовать мою библиотеку Time4J, у которой есть range-package. Я покажу вам полное решение вашей проблемы:

// our test interval 
PlainDate start = PlainDate.of(2013, Month.JANUARY, 31); 
PlainDate end = SystemClock.inLocalView().today(); 
DateInterval test = DateInterval.between(start, end); 
IntervalCollection<PlainDate> icTest = IntervalCollection.onDateAxis().plus(test); 

// two intervals for your GOOD case 
PlainDate s1 = PlainDate.of(2013, Month.JANUARY, 31); 
PlainDate e1 = PlainDate.of(2014, Month.JANUARY, 31); 
DateInterval i1 = DateInterval.between(s1, e1); 

PlainDate s2 = PlainDate.of(2013, Month.MAY, 31); 
PlainDate e2 = end; // today 
DateInterval i2 = DateInterval.between(s2, e2); 

IntervalCollection<PlainDate> goodCase = 
    IntervalCollection.onDateAxis().plus(i1).plus(i2); 

boolean covered = icTest.minus(goodCase).isEmpty(); 
System.out.println("Good case: " + covered); // true 

// two intervals for your BAD case 
PlainDate s3 = PlainDate.of(2013, Month.JANUARY, 31); 
PlainDate e3 = PlainDate.of(2014, Month.JANUARY, 31); 
DateInterval i3 = DateInterval.between(s3, e3); 

PlainDate s4 = PlainDate.of(2014, Month.MARCH, 31); 
PlainDate e4 = end; // today 
DateInterval i4 = DateInterval.between(s4, e4); 

IntervalCollection<PlainDate> badCase = 
    IntervalCollection.onDateAxis().plus(i3).plus(i4); 

covered = icTest.minus(badCase).isEmpty(); 
System.out.println("Bad case: " + covered); // false 

Самая большая часть кода - это просто интервал. Реальный интервал сама арифметика выполняется этим удивительно небольшой фрагмент кода:

boolean covered = 
    IntervalCollection.onDateAxis().plus(test).minus(
    IntervalCollection.onDateAxis().plus(i1).plus(i2) 
).isEmpty(); 

Объяснение: Интервал испытаний покрывается интервалами i1 и i2, если остаток от вычитания i1 и i2 из теста пуст.

К слову: интервалы времени в Time4J по умолчанию являются закрытыми интервалами. Вы можете изменить эти интервалы на половину открытых интервалов, но если вы действительно хотите (просто позвонив withOpenEnd() с заданным интервалом дат).

И если вы планируете перейти на Java-8 позже, вы можете просто обновить версию Time4J до версии 4.x (версия v3.x для Java-6) и получить очень легкие преобразования в Java-8 типа java.time.LocalDate (например: PlainDate.from(localDate) или LocalDate ld = plainDate.toTemporalAccessor()), поэтому вы можете продолжать использовать Time4J для дополнительных функций, не охватываемых стандартной Java даже в будущем.