From 56925dd424dd61986c484fab09ff09b2f28da24f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=93scar=20Gonz=C3=A1lez=20Fern=C3=A1ndez?= Date: Thu, 25 Jun 2009 12:49:09 +0200 Subject: [PATCH] ItEr14S08ModeladoTempoItEr13S09: PartialDate, IntervalsOfPartialDates and TimeQuantities. --- .../partialtime/IntervalOfPartialDates.java | 52 +++ .../business/partialtime/PartialDate.java | 392 ++++++++++++++++++ .../business/partialtime/TimeQuantity.java | 90 ++++ .../IntervalOfPartialDatesTest.java | 99 +++++ .../test/partialtime/PartialDateTest.java | 320 ++++++++++++++ .../test/partialtime/TimeQuantityTest.java | 107 +++++ .../business/test/time/PartialDateTest.java | 182 -------- .../business/time/PartialDate.java | 198 --------- 8 files changed, 1060 insertions(+), 380 deletions(-) create mode 100644 navalplanner-business/src/main/java/org/navalplanner/business/partialtime/IntervalOfPartialDates.java create mode 100644 navalplanner-business/src/main/java/org/navalplanner/business/partialtime/PartialDate.java create mode 100644 navalplanner-business/src/main/java/org/navalplanner/business/partialtime/TimeQuantity.java create mode 100644 navalplanner-business/src/test/java/org/navalplanner/business/test/partialtime/IntervalOfPartialDatesTest.java create mode 100644 navalplanner-business/src/test/java/org/navalplanner/business/test/partialtime/PartialDateTest.java create mode 100644 navalplanner-business/src/test/java/org/navalplanner/business/test/partialtime/TimeQuantityTest.java delete mode 100644 navalplanner-business/src/test/java/org/navalplanner/business/test/time/PartialDateTest.java delete mode 100644 navalplanner-business/src/test/java/org/navalplanner/business/time/PartialDate.java diff --git a/navalplanner-business/src/main/java/org/navalplanner/business/partialtime/IntervalOfPartialDates.java b/navalplanner-business/src/main/java/org/navalplanner/business/partialtime/IntervalOfPartialDates.java new file mode 100644 index 000000000..44086ea82 --- /dev/null +++ b/navalplanner-business/src/main/java/org/navalplanner/business/partialtime/IntervalOfPartialDates.java @@ -0,0 +1,52 @@ +package org.navalplanner.business.partialtime; + +import org.apache.commons.lang.builder.EqualsBuilder; +import org.apache.commons.lang.builder.HashCodeBuilder; + +public class IntervalOfPartialDates { + + private final PartialDate start; + + private final PartialDate end; + + public IntervalOfPartialDates(PartialDate start, PartialDate end) { + if (!start.getGranularity().equals(end.getGranularity())) + throw new IllegalArgumentException( + "the from and the to must have the same granularity"); + if (!start.before(end)) { + throw new IllegalArgumentException( + "the start must be before the end"); + } + this.start = start; + this.end = end; + } + + public PartialDate getStart() { + return this.start; + } + + public PartialDate getEnd() { + return this.end; + } + + public TimeQuantity getDuration() { + return end.quantityFrom(start); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj instanceof IntervalOfPartialDates) { + IntervalOfPartialDates other = (IntervalOfPartialDates) obj; + return new EqualsBuilder().append(this.start, other.start).append( + this.end, other.end).isEquals(); + } + return false; + } + + @Override + public int hashCode() { + return new HashCodeBuilder().append(start).append(end).toHashCode(); + } +} \ No newline at end of file diff --git a/navalplanner-business/src/main/java/org/navalplanner/business/partialtime/PartialDate.java b/navalplanner-business/src/main/java/org/navalplanner/business/partialtime/PartialDate.java new file mode 100644 index 000000000..f6b4099ab --- /dev/null +++ b/navalplanner-business/src/main/java/org/navalplanner/business/partialtime/PartialDate.java @@ -0,0 +1,392 @@ +package org.navalplanner.business.partialtime; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Date; +import java.util.Iterator; +import java.util.List; + +import org.apache.commons.lang.Validate; +import org.apache.commons.lang.builder.HashCodeBuilder; +import org.joda.time.Chronology; +import org.joda.time.DateTime; +import org.joda.time.DateTimeField; +import org.joda.time.DateTimeFieldType; +import org.joda.time.DateTimeZone; +import org.joda.time.Instant; +import org.joda.time.LocalDate; +import org.joda.time.Partial; +import org.joda.time.ReadableInstant; +import org.joda.time.ReadablePartial; +import org.joda.time.chrono.ISOChronology; + +/** + * @author Óscar González Fernández + */ +public class PartialDate implements ReadablePartial { + private static final DateTimeFieldType year = DateTimeFieldType.year(); + + private static final DateTimeFieldType monthOfYear = DateTimeFieldType + .monthOfYear(); + + private static final DateTimeFieldType weekyear = DateTimeFieldType + .weekyear(); + + private static final DateTimeFieldType weekOfYear = DateTimeFieldType + .weekOfWeekyear(); + + private static final DateTimeFieldType dayOfMonth = DateTimeFieldType + .dayOfMonth(); + + private static final DateTimeFieldType hourOfDay = DateTimeFieldType + .hourOfDay(); + + private static final DateTimeFieldType minuteOfHour = DateTimeFieldType + .minuteOfHour(); + + private static final DateTimeFieldType secondOfMinute = DateTimeFieldType + .secondOfMinute(); + + private static final DateTimeFieldType millisOfSecond = DateTimeFieldType + .millisOfSecond(); + + public enum Granularity { + + YEAR(year), MONTH(year, monthOfYear), WEEK(weekyear, weekOfYear) { + TimeQuantity asQuantity(TimeQuantity acc, List values) { + return acc.plus(values.get(0), Granularity.YEAR).plus( + values.get(1), Granularity.WEEK); + } + }, + DAY(year, monthOfYear, dayOfMonth), HOUR(year, monthOfYear, dayOfMonth, + hourOfDay), MINUTE(year, monthOfYear, dayOfMonth, hourOfDay, + minuteOfHour), SECOND(year, monthOfYear, dayOfMonth, hourOfDay, + minuteOfHour, secondOfMinute), MILLISECONDS(year, monthOfYear, + dayOfMonth, hourOfDay, minuteOfHour, secondOfMinute, + millisOfSecond); + + private DateTimeFieldType[] types; + + private Granularity(DateTimeFieldType... types) { + this.types = types; + } + + public DateTimeFieldType[] getDateTimeTypes() { + return types.clone(); + } + + public TimeQuantity asQuantity(int[] values) { + return asQuantity(TimeQuantity.empty(), asIntegersList(values)); + } + + TimeQuantity asQuantity(TimeQuantity acc, List values) { + Integer current = values.remove(values.size() - 1); + acc = acc.plus(current, this); + if (hasPrevious()) { + acc = getPrevious().asQuantity(acc, values); + } + return acc; + } + + private boolean hasPrevious() { + return ordinal() > 0; + } + + private Granularity getPrevious() { + Granularity granularity = Granularity.values()[ordinal() - 1]; + // we bypass week, as it isn't part of the rest of hierarchy + if (granularity == WEEK) { + return WEEK.getPrevious(); + } + return granularity; + } + + private List asIntegersList(int[] values) { + List result = new ArrayList(); + for (int value : values) { + result.add(value); + } + return result; + } + + boolean isMoreCoarseGrainedThan(Granularity other) { + return this.ordinal() < other.ordinal(); + } + + public Granularity[] thisAndMoreFineGrained() { + Granularity[] result = new Granularity[values().length + - this.ordinal()]; + for (int i = 0; i < result.length; i++) { + result[i] = values()[i + this.ordinal()]; + } + return result; + } + + public Granularity[] moreCoarseGrained() { + Granularity[] result = new Granularity[this.ordinal()]; + for (int i = 0; i < result.length; i++) { + result[i] = values()[i]; + } + return result; + } + + private static Granularity forType(DateTimeFieldType fieldType) { + for (Granularity granularity : Granularity.values()) { + if (granularity.specifies(fieldType)) { + return granularity; + } + } + throw new RuntimeException("not found granularity for " + fieldType); + } + + private boolean specifies(DateTimeFieldType fieldType) { + return getMostSpecific().equals(fieldType); + } + + private DateTimeFieldType getMostSpecific() { + return getDateTimeTypes()[getDateTimeTypes().length - 1]; + } + } + + public static PartialDate createFrom(LocalDate date) { + long millis = date.toDateTimeAtStartOfDay().getMillis(); + return createFrom(millis).with(Granularity.DAY); + } + + public static PartialDate createFrom(DateTime dateTime) { + return createFrom(dateTime.getMillis()); + } + + public static PartialDate createFrom(Date date) { + return createFrom(date.getTime()); + } + + public static PartialDate createFrom(ReadableInstant instant) { + return createFrom(instant.getMillis()); + } + + public static PartialDate createFrom(long timeMilliseconds) { + return new PartialDate(new Instant(timeMilliseconds), + Granularity.MILLISECONDS); + } + + private static List getValues(Partial partial, + Granularity granularity) { + List values = new ArrayList(); + for (DateTimeFieldType fieldType : granularity.getDateTimeTypes()) { + values.add(partial.get(fieldType)); + } + return values; + } + + private static Instant toNormalizedInstant(Granularity granularity, + List values) { + return new Partial(granularity.getDateTimeTypes(), asIntArray(values), + ISOChronology.getInstance(DateTimeZone.getDefault())) + .toDateTime(new DateTime(0, DateTimeZone.getDefault())) + .toInstant(); + } + + private static int[] asIntArray(List values) { + int[] result = new int[values.size()]; + int i = 0; + for (Integer integer : values) { + result[i++] = integer; + } + return result; + } + + private static Partial asPartial(ReadableInstant instant, Granularity unit) { + DateTime dateTime = interpretInDefaultTimeZone(instant); + DateTimeFieldType[] dateTimeTypes = unit.getDateTimeTypes(); + int[] values = new int[dateTimeTypes.length]; + for (int i = 0; i < values.length; i++) { + values[i] = dateTime.get(dateTimeTypes[i]); + } + return new Partial(dateTimeTypes, values); + } + + private final Granularity granularity; + + private final Partial partial; + + private final Instant normalizedInstant; + + private List values; + + private PartialDate(Instant instant, Granularity granularityUnit) { + this.partial = asPartial(instant, granularityUnit); + this.granularity = granularityUnit; + this.values = Collections.unmodifiableList(getValues(partial, + granularityUnit)); + this.normalizedInstant = toNormalizedInstant(granularityUnit, values); + assert this.partial.equals(asPartial(this.normalizedInstant, + granularityUnit)); + } + + private static DateTime interpretInDefaultTimeZone(ReadableInstant instant) { + return new DateTime(instant.getMillis(), DateTimeZone.getDefault()); + } + + public PartialDate with(Granularity granularity) { + if (!canBeConvertedTo(granularity)) { + throw new IllegalArgumentException("the granularity " + granularity + + " is more fine-grained than the current one(" + + this.granularity + "). Conversion is impossible"); + } + return new PartialDate(normalizedInstant, granularity); + } + + public Granularity getGranularity() { + return this.granularity; + } + + public boolean canBeConvertedToLocalDate() { + return !this.granularity.isMoreCoarseGrainedThan(Granularity.DAY); + } + + public boolean canBeConvertedTo(Granularity unit) { + Validate.notNull(unit); + return !this.granularity.isMoreCoarseGrainedThan(unit); + } + + public boolean before(PartialDate other) throws IllegalArgumentException { + if (!getGranularity().equals(other.getGranularity())) { + throw new IllegalArgumentException( + "It's required that the two PartialDates have the same granularity"); + } + assert this.values.size() == other.values.size(); + Iterator iterator = this.values.iterator(); + Iterator otherIterator = other.values.iterator(); + while (iterator.hasNext()) { + int diff = iterator.next() - otherIterator.next(); + if (diff > 0) + return false; + if (diff < 0) + return true; + } + return false; + } + + public boolean after(PartialDate other) throws IllegalArgumentException { + return !before(other) && !equals(other); + } + + public LocalDate tryToConvertToLocalDate() throws IllegalArgumentException { + if (!this.canBeConvertedToLocalDate()) + throw new IllegalArgumentException("the partialDate " + this + + " can't support be converted to local date"); + return new LocalDate(this.normalizedInstant, ISOChronology + .getInstance(DateTimeZone.getDefault())); + } + + public IntervalOfPartialDates to(PartialDate to) { + return new IntervalOfPartialDates(this, to); + } + + @Override + public int hashCode() { + return new HashCodeBuilder().append(getGranularity()).append(values) + .toHashCode(); + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof PartialDate) { + PartialDate other = (PartialDate) obj; + if (!getGranularity().equals(other.getGranularity())) { + return false; + } + return this.values.equals(other.values); + } + return false; + } + + @Override + public int get(DateTimeFieldType field) { + return partial.get(field); + } + + @Override + public Chronology getChronology() { + return partial.getChronology(); + } + + @Override + public DateTimeField getField(int index) { + return partial.getField(index); + } + + @Override + public DateTimeFieldType getFieldType(int index) { + return granularity.types[index]; + } + + @Override + public int getValue(int index) { + return partial.getValue(index); + } + + @Override + public boolean isSupported(DateTimeFieldType field) { + return partial.isSupported(field); + } + + @Override + public int size() { + return partial.size(); + } + + @Override + public DateTime toDateTime(ReadableInstant baseInstant) { + return partial.toDateTime(baseInstant); + } + + public IntervalOfPartialDates to(TimeQuantity timeQuantity) { + return to(this.plus(timeQuantity)); + } + + public PartialDate plus(TimeQuantity timeQuantity) { + if (granularity.isMoreCoarseGrainedThan(timeQuantity + .getGreatestGranularitySpecified())) { + throw new IllegalArgumentException(""); + } + DateTimeFieldType[] dateTimeTypes = granularity.getDateTimeTypes(); + long result = normalizedInstant.getMillis(); + for (int i = 0; i < dateTimeTypes.length; i++) { + DateTimeField field = dateTimeTypes[i].getField(getChronology()); + result = field.add(result, timeQuantity.valueFor(Granularity + .forType(dateTimeTypes[i]))); + } + return new PartialDate(new Instant(result), granularity); + } + + public TimeQuantity quantityFrom(PartialDate start) { + Validate.isTrue(this.granularity.equals(start.getGranularity()), + "must have the same granularity"); + Validate.isTrue(this.after(start)); + int[] values = substract(this.values, start.values); + return this.granularity.asQuantity(values); + } + + private static int[] substract(List bigger, List other) { + assert bigger.size() == other.size(); + int[] result = new int[bigger.size()]; + Iterator iterator = bigger.iterator(); + Iterator otherIterator = other.iterator(); + int i = 0; + while (iterator.hasNext()) { + result[i] = iterator.next() - otherIterator.next(); + i++; + } + return result; + } + + public Integer valueFor(Granularity granularity) { + if (this.granularity.isMoreCoarseGrainedThan(granularity)) + throw new IllegalArgumentException(granularity + + " is more specific than this instance granularity: " + + this.granularity); + return get(granularity.getMostSpecific()); + } +} diff --git a/navalplanner-business/src/main/java/org/navalplanner/business/partialtime/TimeQuantity.java b/navalplanner-business/src/main/java/org/navalplanner/business/partialtime/TimeQuantity.java new file mode 100644 index 000000000..adf948b77 --- /dev/null +++ b/navalplanner-business/src/main/java/org/navalplanner/business/partialtime/TimeQuantity.java @@ -0,0 +1,90 @@ +package org.navalplanner.business.partialtime; + +import java.util.EnumMap; + +import org.apache.commons.lang.Validate; +import org.navalplanner.business.partialtime.PartialDate.Granularity; + +/** + * Represents a quantity of time. It's composed from granularities and integers
+ * @author Óscar González Fernández + */ +public class TimeQuantity { + + private static EnumMap createSumOf( + EnumMap... values) { + EnumMap result = new EnumMap( + Granularity.class); + for (Granularity granularity : Granularity.values()) { + Integer acc = sumAll(granularity, values); + if (acc != null) { + result.put(granularity, acc); + } + } + return result; + } + + private static Integer sumAll(Granularity granularity, + EnumMap... maps) { + Integer result = null; + for (EnumMap enumMap : maps) { + if (enumMap.containsKey(granularity)) { + Integer valueToAdd = enumMap.get(granularity); + result = result == null ? enumMap.get(granularity) : result + + valueToAdd; + } + } + return result; + } + + private static EnumMap copyAndAdd( + EnumMap existent, int quantity, + Granularity granularity) { + EnumMap result = new EnumMap( + existent); + int newQuantity = quantity; + if (result.containsKey(granularity)) { + newQuantity += result.get(granularity); + } + result.put(granularity, newQuantity); + return result; + } + + private final EnumMap values; + + public static TimeQuantity empty() { + return new TimeQuantity(); + } + + public TimeQuantity() { + this(new EnumMap(Granularity.class)); + } + + private TimeQuantity(EnumMap enumMap) { + Validate.notNull(enumMap); + this.values = enumMap; + } + + public Integer valueFor(Granularity granularity) { + return values.containsKey(granularity) ? values.get(granularity) : 0; + } + + public TimeQuantity plus(int quantity, Granularity granularity) { + Validate.notNull(granularity, "granularity must be not null"); + return new TimeQuantity(copyAndAdd(values, quantity, granularity)); + } + + public TimeQuantity plus(TimeQuantity other) { + return new TimeQuantity(createSumOf(values, other.values)); + } + + public Granularity getGreatestGranularitySpecified() { + Granularity result = Granularity.YEAR; + for (Granularity granularity : values.keySet()) { + if (result == null || result.isMoreCoarseGrainedThan(granularity)) { + result = granularity; + } + } + return result; + } +} diff --git a/navalplanner-business/src/test/java/org/navalplanner/business/test/partialtime/IntervalOfPartialDatesTest.java b/navalplanner-business/src/test/java/org/navalplanner/business/test/partialtime/IntervalOfPartialDatesTest.java new file mode 100644 index 000000000..73d03ad3e --- /dev/null +++ b/navalplanner-business/src/test/java/org/navalplanner/business/test/partialtime/IntervalOfPartialDatesTest.java @@ -0,0 +1,99 @@ +package org.navalplanner.business.test.partialtime; + +import static org.hamcrest.CoreMatchers.equalTo; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertThat; + +import org.joda.time.DateTime; +import org.joda.time.LocalDate; +import org.junit.Test; +import org.navalplanner.business.partialtime.IntervalOfPartialDates; +import org.navalplanner.business.partialtime.PartialDate; +import org.navalplanner.business.partialtime.TimeQuantity; +import org.navalplanner.business.partialtime.PartialDate.Granularity; + +public class IntervalOfPartialDatesTest { + + private PartialDate start = PartialDate.createFrom(new LocalDate(2006, 10, 2)); + private PartialDate end = PartialDate.createFrom(new LocalDate(2006, 11, 5)); + private IntervalOfPartialDates interval = new IntervalOfPartialDates(start, + end); + + @Test + public void isComposedFromTwoPartialDates() { + assertThat(interval.getStart(), equalTo(start)); + assertThat(interval.getEnd(), equalTo(end)); + } + + @Test(expected = IllegalArgumentException.class) + public void thePartialDatesMustBeOfTheSameGranularity() { + new IntervalOfPartialDates(start, end.with(Granularity.MONTH)); + } + + @Test(expected = IllegalArgumentException.class) + public void theFromMustBeBeforeTo() { + new IntervalOfPartialDates(end, start); + } + + @Test(expected = IllegalArgumentException.class) + public void cannotCreateEmptyInterval() { + PartialDate.createFrom(new LocalDate(2006, 11, 2)).to( + PartialDate.createFrom(new LocalDate(2006, 11, 2))); + } + + @Test + public void canBeCreatedFromPartialDate() { + PartialDate.createFrom(new LocalDate(2006, 10, 2)).to( + PartialDate.createFrom(new LocalDate(2006, 11, 2))); + } + + @Test + public void canGetTheDurationOfTheInterval() { + TimeQuantity duration = interval.getDuration(); + assertThat(duration.valueFor(Granularity.MONTH), equalTo(1)); + assertThat(duration.valueFor(Granularity.DAY), equalTo(3)); + } + + @Test + public void twoIntervalsAreEqualsIfTheirPartialDatesAreEquals() { + PartialDate from = PartialDate.createFrom(new LocalDate(2006, 10, 2) + .toDateTimeAtStartOfDay()); + IntervalOfPartialDates oneInterval = from.to(PartialDate + .createFrom(new LocalDate(2006, 10, 3).toDateTimeAtStartOfDay())); + IntervalOfPartialDates otherInterval = PartialDate.createFrom( + new LocalDate(2006, 10, 2).toDateTimeAtStartOfDay()).to( + PartialDate.createFrom(new LocalDate(2006, 10, 3) + .toDateTimeAtStartOfDay())); + IntervalOfPartialDates differentInterval = from.to(PartialDate + .createFrom(new LocalDate(2006, 10, 4).toDateTimeAtStartOfDay())); + assertEquals(oneInterval, oneInterval); + + assertEquals(oneInterval, otherInterval); + assertEquals(otherInterval, oneInterval); + assertEquals(oneInterval.hashCode(), otherInterval.hashCode()); + + assertFalse(oneInterval.equals(differentInterval)); + assertFalse(differentInterval.equals(oneInterval)); + assertFalse(oneInterval.hashCode() == differentInterval.hashCode()); + } + + @Test + public void canGetTheDurationInMilliseconds() { + DateTime startOfDay = new LocalDate(2009, 3, 8) + .toDateTimeAtStartOfDay(); + PartialDate startOfDayPartial = PartialDate.createFrom(startOfDay); + long millis = startOfDay.getMillis(); + PartialDate after = PartialDate.createFrom(new DateTime(millis + 3)); + PartialDate before = PartialDate.createFrom(new DateTime(millis - 3)); + IntervalOfPartialDates[] intervalsWithTheSameDuration = { + before.to(startOfDayPartial), startOfDayPartial.to(after) }; + for (IntervalOfPartialDates interval : intervalsWithTheSameDuration) { + TimeQuantity duration = interval.getDuration(); + PartialDate start2 = interval.getStart(); + PartialDate endCalculated = start2.plus(duration); + PartialDate originalEnd = interval.getEnd(); + assertThat(endCalculated, equalTo(originalEnd)); + } + } +} diff --git a/navalplanner-business/src/test/java/org/navalplanner/business/test/partialtime/PartialDateTest.java b/navalplanner-business/src/test/java/org/navalplanner/business/test/partialtime/PartialDateTest.java new file mode 100644 index 000000000..46cc7b784 --- /dev/null +++ b/navalplanner-business/src/test/java/org/navalplanner/business/test/partialtime/PartialDateTest.java @@ -0,0 +1,320 @@ +package org.navalplanner.business.test.partialtime; + +import static org.hamcrest.CoreMatchers.equalTo; +import static org.hamcrest.CoreMatchers.not; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import java.util.Date; + +import org.joda.time.DateTime; +import org.joda.time.DateTimeFieldType; +import org.joda.time.Instant; +import org.joda.time.LocalDate; +import org.joda.time.ReadablePartial; +import org.junit.Test; +import org.navalplanner.business.partialtime.IntervalOfPartialDates; +import org.navalplanner.business.partialtime.PartialDate; +import org.navalplanner.business.partialtime.TimeQuantity; +import org.navalplanner.business.partialtime.PartialDate.Granularity; + +public class PartialDateTest { + + @Test + public void defaultTimeUnitIsMilliseconds() throws Exception { + Date date = new Date(); + PartialDate partialDate = PartialDate.createFrom(date); + assertThat(partialDate.getGranularity(), + equalTo(Granularity.MILLISECONDS)); + } + + @Test + public void isAReadablePartial() { + assertTrue(PartialDate.createFrom(new Date()) instanceof ReadablePartial); + } + + @Test + public void canBeCreatedFromJavaUtilDate() { + Date now = new Date(); + PartialDate partial = PartialDate.createFrom(now).with(Granularity.DAY); + Granularity unit = partial.getGranularity(); + assertThat(unit, equalTo(Granularity.DAY)); + } + + @Test + public void canBeCreatedFromJodaDateTime() { + LocalDate date = new LocalDate(2000, 10, 12); + DateTime dateTimeAtCurrentTime = date.toDateTimeAtCurrentTime(); + PartialDate partial = PartialDate.createFrom(dateTimeAtCurrentTime) + .with(Granularity.DAY); + Granularity unit = partial.getGranularity(); + assertThat(unit, equalTo(Granularity.DAY)); + assertThat(partial.get(DateTimeFieldType.dayOfMonth()), equalTo(12)); + } + + @Test + public void canBeCreatedFromLocalDateAndPutsGranularityAsDay() { + LocalDate date = new LocalDate(2000, 10, 12); + PartialDate partial = PartialDate.createFrom(date); + assertThat(partial.getGranularity(), equalTo(Granularity.DAY)); + check(partial, 2000, 10, 12); + } + + @Test + public void granularityOfDayAndMoreFineGrainedCanBeConvertedToLocalDate() { + Instant now = new Instant(); + LocalDate today = new LocalDate(); + Granularity[] supportingConversionToLocalDate = Granularity.DAY + .thisAndMoreFineGrained(); + for (Granularity g : supportingConversionToLocalDate) { + PartialDate partialDate = PartialDate.createFrom(now).with(g); + assertTrue(partialDate.canBeConvertedToLocalDate()); + LocalDate converted = partialDate.tryToConvertToLocalDate(); + assertThat(converted, equalTo(today)); + } + Granularity[] notSupportingConversionToLocalDate = Granularity.DAY + .moreCoarseGrained(); + for (Granularity g : notSupportingConversionToLocalDate) { + PartialDate partialDate = PartialDate.createFrom(now).with(g); + assertFalse(partialDate.canBeConvertedToLocalDate()); + try { + LocalDate converted = partialDate.tryToConvertToLocalDate(); + fail("the partial date cannot be converted to a LocalDate"); + } catch (IllegalArgumentException e) { + // ok + } + } + } + + @Test + public void availableFieldsDependOnTimeUnit() throws Exception { + Date now = new Date(); + for (Granularity timeUnit : Granularity.values()) { + PartialDate partial = PartialDate.createFrom(now).with(timeUnit); + DateTimeFieldType types[] = timeUnit.getDateTimeTypes(); + for (int i = 0; i < types.length; i++) { + assertThat(partial.getFieldType(i), equalTo(types[i])); + } + } + } + + @Test + public void canAccessFieldsUsingGranularity() { + LocalDate date = new LocalDate(2000, 10, 12); + PartialDate partialDate = PartialDate.createFrom(date.toDateMidnight() + .toDate()); + assertThat(partialDate.valueFor(Granularity.MINUTE), equalTo(0)); + assertThat(partialDate.valueFor(Granularity.YEAR), equalTo(2000)); + assertThat(partialDate.valueFor(Granularity.DAY), equalTo(12)); + } + + @Test + public void cannotAccessFieldsOfMoreSpecificGranularity() { + PartialDate partialDate = PartialDate.createFrom( + new LocalDate(2000, 10, 12)).with(Granularity.MONTH); + Granularity[] moreSpecific = Granularity.MONTH.thisAndMoreFineGrained(); + for (int i = 1; i < moreSpecific.length; i++) { + try { + partialDate.valueFor(moreSpecific[i]); + fail("must send IllegalArgumentException"); + } catch (IllegalArgumentException e) { + // ok + } + } + } + + @Test + public void retainsValues() throws Exception { + LocalDate date = new LocalDate(2000, 10, 12); + PartialDate partialDate = PartialDate.createFrom( + date.toDateMidnight().toDate()).with(Granularity.DAY); + check(partialDate, 2000, 10, 12); + } + + private void check(PartialDate partialDate, int year, int month, int day) { + assertThat(partialDate.get(DateTimeFieldType.year()), equalTo(year)); + assertThat(partialDate.get(DateTimeFieldType.monthOfYear()), + equalTo(month)); + assertThat(partialDate.get(DateTimeFieldType.dayOfMonth()), + equalTo(day)); + } + + @Test(expected = IllegalArgumentException.class) + public void dontSupportAllTypes() { + LocalDate date = new LocalDate(2000, 10, 12); + PartialDate partialDate = PartialDate.createFrom( + date.toDateMidnight().toDate()).with(Granularity.DAY); + partialDate.get(DateTimeFieldType.minuteOfHour()); + } + + @Test + public void canBeConvertedToMoreCoarseGrained() { + LocalDate date = new LocalDate(2000, 10, 12); + PartialDate partialDate = PartialDate.createFrom( + date.toDateMidnight().toDate()).with(Granularity.DAY); + PartialDate onlyUntilMonthSpecified = partialDate + .with(Granularity.MONTH); + assertThat( + onlyUntilMonthSpecified.get(DateTimeFieldType.monthOfYear()), + equalTo(10)); + try { + onlyUntilMonthSpecified.get(DateTimeFieldType.dayOfMonth()); + fail("must send exception since the day is lost in the conversion"); + } catch (IllegalArgumentException e) { + // ok + } + } + + @Test + public void thereIsQueryMethodToKnowIfItCanBeConverted() { + LocalDate date = new LocalDate(2000, 10, 12); + PartialDate partialDate = PartialDate.createFrom( + date.toDateMidnight().toDate()).with(Granularity.DAY); + Granularity[] moreCoarseGrained = { Granularity.YEAR, + Granularity.MONTH, Granularity.DAY }; + for (Granularity t : moreCoarseGrained) { + assertTrue(partialDate.canBeConvertedTo(t)); + } + Granularity[] moreFineGrained = { Granularity.HOUR, Granularity.MINUTE, + Granularity.SECOND, Granularity.MILLISECONDS }; + for (Granularity t : moreFineGrained) { + assertFalse(partialDate.canBeConvertedTo(t)); + } + } + + @Test(expected = IllegalArgumentException.class) + public void cannotBeConvertedToMoreFineGrained() { + LocalDate date = new LocalDate(2000, 10, 12); + PartialDate partialDate = PartialDate.createFrom( + date.toDateMidnight().toDate()).with(Granularity.DAY); + partialDate.with(Granularity.HOUR); + } + + @Test + public void onlyPartialDatesOfTheSameGranuralityCanBeEqual() { + LocalDate date = new LocalDate(2000, 10, 12); + PartialDate partialDate = PartialDate.createFrom( + date.toDateTimeAtStartOfDay()).with(Granularity.DAY); + PartialDate otherOnSameInstantWithDifferentGranularity = PartialDate + .createFrom(date.toDateTimeAtStartOfDay()).with( + Granularity.MILLISECONDS); + assertThat(partialDate, + not(equalTo(otherOnSameInstantWithDifferentGranularity))); + } + + @Test + public void equalsAndHashCodeBasedOnTheValuesForCurrentGranularity() { + LocalDate localDate = new LocalDate(2000, 10, 12); + DateTime dateTime = localDate.toDateTimeAtStartOfDay(); + PartialDate partial = PartialDate.createFrom(localDate).with( + Granularity.DAY); + PartialDate other = PartialDate.createFrom(dateTime).with( + Granularity.DAY); + int partialHashCode = partial.hashCode(); + int otherHashCode = other.hashCode(); + assertThat(partialHashCode, equalTo(otherHashCode)); + assertThat(partial, equalTo(other)); + } + + @Test + public void forTwoEqualsPartialsChangingGranularityTheSameWayKeepsEquality() { + DateTime dateTime = new LocalDate(2000, 10, 12) + .toDateTimeAtStartOfDay(); + PartialDate partial1 = PartialDate.createFrom(dateTime).with( + Granularity.MILLISECONDS); + PartialDate partial2 = PartialDate.createFrom(dateTime).with( + Granularity.MILLISECONDS); + assertEquals(partial1, partial2); + Granularity[] granularities = Granularity.values(); + for (int i = granularities.length - 1; i >= 0; i--) { + partial1 = partial1.with(granularities[i]); + partial2 = partial2.with(granularities[i]); + assertEquals(partial1, partial2); + } + } + + @Test + public void hasBeforeAndAfterMethod() { + PartialDate beforePartialDate = PartialDate.createFrom(new LocalDate( + 2000, 9, 12)); + PartialDate posteriorPartialDate = PartialDate + .createFrom(new LocalDate(2000, 10, 1)); + assertTrue(beforePartialDate.before(posteriorPartialDate)); + assertFalse(posteriorPartialDate.before(beforePartialDate)); + assertFalse(posteriorPartialDate.before(posteriorPartialDate)); + assertFalse(beforePartialDate.before(beforePartialDate)); + assertTrue(posteriorPartialDate.after(beforePartialDate)); + assertFalse(beforePartialDate.after(posteriorPartialDate)); + assertFalse(posteriorPartialDate.after(posteriorPartialDate)); + assertFalse(beforePartialDate.after(beforePartialDate)); + } + + @Test(expected = IllegalArgumentException.class) + public void cantUseBeforeWithPartialsOfDifferentGranularity() { + PartialDate p = PartialDate.createFrom(new LocalDate(2000, 9, 12)); + PartialDate withDifferentGranularity = p.with(Granularity.MONTH); + p.before(withDifferentGranularity); + } + + @Test(expected = IllegalArgumentException.class) + public void cantUseAfterWithPartialsOfDifferentGranularity() { + PartialDate p = PartialDate.createFrom(new LocalDate(2000, 9, 12)); + PartialDate withDifferentGranularity = p.with(Granularity.MONTH); + p.after(withDifferentGranularity); + } + + @Test + public void canAddATimeQuantity() { + PartialDate partialDate = PartialDate.createFrom(new LocalDate(2009, 8, + 4)); + assertThat(partialDate.plus( + TimeQuantity.empty().plus(3, Granularity.MONTH)) + .tryToConvertToLocalDate().getMonthOfYear(), equalTo(11)); + assertThat(partialDate.plus( + TimeQuantity.empty().plus(3, Granularity.DAY)) + .tryToConvertToLocalDate().getDayOfMonth(), equalTo(7)); + assertThat(partialDate.plus( + TimeQuantity.empty().plus(4, Granularity.MONTH)) + .tryToConvertToLocalDate().getMonthOfYear(), equalTo(12)); + PartialDate overflowedToYear = partialDate.plus(TimeQuantity.empty() + .plus(5, Granularity.MONTH)); + assertThat(overflowedToYear.tryToConvertToLocalDate().getMonthOfYear(), + equalTo(1)); + assertThat(overflowedToYear.tryToConvertToLocalDate().getYear(), + equalTo(2010)); + assertThat(partialDate.plus( + TimeQuantity.empty().plus(-1, Granularity.DAY)) + .tryToConvertToLocalDate().getDayOfMonth(), equalTo(3)); + } + + @Test + public void canAddTimeQuantityWithMillisecondsGranularity() { + PartialDate partialDate = PartialDate.createFrom(new LocalDate(2009, 8, + 4).toDateTimeAtStartOfDay()); + assertThat(partialDate.valueFor(Granularity.MILLISECONDS), equalTo(0)); + PartialDate sum = partialDate.plus(TimeQuantity.empty().plus(4, + Granularity.MILLISECONDS)); + assertThat(sum.valueFor(Granularity.MILLISECONDS), equalTo(4)); + } + + @Test(expected = IllegalArgumentException.class) + public void theTimeQuantityMustNotHaveGreaterGranularityThanThePartialDate() { + PartialDate partialDate = PartialDate.createFrom(new LocalDate(2009, 8, + 4)); + partialDate.plus(TimeQuantity.empty().plus(3, Granularity.HOUR)); + } + + @Test + public void canCreateIntervalsUsingQuantities() { + PartialDate start = PartialDate.createFrom(new LocalDate(2009, 8, 4)); + IntervalOfPartialDates interval = start.to(TimeQuantity.empty().plus(3, + Granularity.MONTH)); + assertThat( + interval.getEnd().tryToConvertToLocalDate().getMonthOfYear(), + equalTo(11)); + } + +} diff --git a/navalplanner-business/src/test/java/org/navalplanner/business/test/partialtime/TimeQuantityTest.java b/navalplanner-business/src/test/java/org/navalplanner/business/test/partialtime/TimeQuantityTest.java new file mode 100644 index 000000000..500660bff --- /dev/null +++ b/navalplanner-business/src/test/java/org/navalplanner/business/test/partialtime/TimeQuantityTest.java @@ -0,0 +1,107 @@ +package org.navalplanner.business.test.partialtime; + +import static org.hamcrest.CoreMatchers.equalTo; +import static org.junit.Assert.assertNotSame; +import static org.junit.Assert.assertThat; + +import org.junit.Test; +import org.navalplanner.business.partialtime.TimeQuantity; +import org.navalplanner.business.partialtime.PartialDate.Granularity; + +public class TimeQuantityTest { + + @Test + public void aEmptyTimeQuantityHasAValueOfZeroForAllGranularities() { + TimeQuantity empty = TimeQuantity.empty(); + for (Granularity granularity : Granularity.values()) { + assertThat(empty.valueFor(granularity), equalTo(0)); + } + } + + @Test + public void aTimeQuantityIsComposedOfIntengersAssociatedToSeveralGranularities() { + TimeQuantity timeQuantity = TimeQuantity.empty().plus(2, + Granularity.MONTH).plus(-1, Granularity.DAY); + assertThat(timeQuantity.valueFor(Granularity.MONTH), equalTo(2)); + assertThat(timeQuantity.valueFor(Granularity.DAY), equalTo(-1)); + } + + @Test(expected = IllegalArgumentException.class) + public void theGranularityMustBeNotNull() { + TimeQuantity.empty().plus(3, null); + } + + @Test + public void severalPlusOnTheSameGranularityAccumulateTheValue() { + TimeQuantity timeQuantity = TimeQuantity.empty().plus(2, + Granularity.MONTH).plus(4, Granularity.DAY).plus(3, + Granularity.MONTH); + assertThat(timeQuantity.valueFor(Granularity.MONTH), equalTo(5)); + } + + @Test + public void isInmutable() { + TimeQuantity initial = TimeQuantity.empty(); + TimeQuantity modified = initial.plus(3, Granularity.HOUR); + assertNotSame(initial, modified); + } + + @Test + public void theAmountCanBeZero() { + TimeQuantity.empty().plus(0, Granularity.MONTH); + } + + @Test + public void theAmountCanBeNegative() { + TimeQuantity.empty().plus(-1, Granularity.MONTH); + } + + @Test + public void timeQuantitiesCanBeAdded() { + TimeQuantity one = TimeQuantity.empty().plus(2, Granularity.MONTH) + .plus(4, Granularity.DAY); + TimeQuantity another = TimeQuantity.empty().plus(3, Granularity.YEAR) + .plus(2, Granularity.DAY); + TimeQuantity result = one.plus(another); + assertThat(result.valueFor(Granularity.MONTH), equalTo(2)); + assertThat(result.valueFor(Granularity.DAY), equalTo(6)); + assertThat(result.valueFor(Granularity.YEAR), equalTo(3)); + assertThat(result.valueFor(Granularity.MINUTE), equalTo(0)); + } + + @Test + public void canSubstractUsingNegativeIntegers() { + TimeQuantity one = TimeQuantity.empty().plus(2, Granularity.MONTH) + .plus(4, Granularity.DAY); + TimeQuantity another = TimeQuantity.empty().plus(3, Granularity.YEAR) + .plus(-5, Granularity.DAY); + TimeQuantity result = one.plus(another); + assertThat(result.valueFor(Granularity.MONTH), equalTo(2)); + assertThat(result.valueFor(Granularity.DAY), equalTo(-1)); + assertThat(result.valueFor(Granularity.YEAR), equalTo(3)); + assertThat(result.valueFor(Granularity.MINUTE), equalTo(0)); + } + + @Test + public void timeQuantitiesDontOverflowIntoLargerGranularities() { + TimeQuantity quantity = TimeQuantity.empty() + .plus(13, Granularity.MONTH); + assertThat(quantity.valueFor(Granularity.MONTH), equalTo(13)); + assertThat(quantity.valueFor(Granularity.YEAR), equalTo(0)); + } + + @Test + public void theGreatestGranularitySpecifiedCanBeKnown() { + TimeQuantity timeQuantity = TimeQuantity.empty().plus(2, + Granularity.MONTH).plus(4, Granularity.DAY); + assertThat(timeQuantity.getGreatestGranularitySpecified(), + equalTo(Granularity.DAY)); + } + + @Test + public void theGranularityOfAnEmptyTimeQuantityIsYear() { + TimeQuantity empty = TimeQuantity.empty(); + assertThat(empty.getGreatestGranularitySpecified(), + equalTo(Granularity.YEAR)); + } +} diff --git a/navalplanner-business/src/test/java/org/navalplanner/business/test/time/PartialDateTest.java b/navalplanner-business/src/test/java/org/navalplanner/business/test/time/PartialDateTest.java deleted file mode 100644 index 0cf0077dd..000000000 --- a/navalplanner-business/src/test/java/org/navalplanner/business/test/time/PartialDateTest.java +++ /dev/null @@ -1,182 +0,0 @@ -package org.navalplanner.business.test.time; - -import static org.hamcrest.CoreMatchers.equalTo; -import static org.hamcrest.CoreMatchers.not; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertThat; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; - -import java.util.Date; - -import org.joda.time.DateTime; -import org.joda.time.DateTimeFieldType; -import org.joda.time.Instant; -import org.joda.time.LocalDate; -import org.joda.time.ReadablePartial; -import org.junit.Test; -import org.navalplanner.business.time.PartialDate; -import org.navalplanner.business.time.PartialDate.Granularity; - -public class PartialDateTest { - - @Test - public void defaultTimeUnitIsMilliseconds() throws Exception { - Date date = new Date(); - PartialDate partialDate = PartialDate.from(date); - assertThat(partialDate.getGranularity(), equalTo(Granularity.MILLISECONDS)); - } - - @Test - public void isAReadablePartial() { - assertTrue(PartialDate.from(new Date()) instanceof ReadablePartial); - } - - @Test - public void canBeCreatedFromJavaUtilDate() { - Date now = new Date(); - PartialDate partial = PartialDate.from(now).with(Granularity.DAY); - Granularity unit = partial.getGranularity(); - assertThat(unit, equalTo(Granularity.DAY)); - } - - @Test - public void canBeCreatedFromJodaDateTime() { - LocalDate date = new LocalDate(2000, 10, 12); - DateTime dateTimeAtCurrentTime = date.toDateTimeAtCurrentTime(); - PartialDate partial = PartialDate.from(dateTimeAtCurrentTime).with( - Granularity.DAY); - Granularity unit = partial.getGranularity(); - assertThat(unit, equalTo(Granularity.DAY)); - assertThat(partial.get(DateTimeFieldType.dayOfMonth()), equalTo(12)); - } - - @Test - public void canBeCreatedFromLocalDateAndPutsGranularityAsDay() { - LocalDate date = new LocalDate(2000, 10, 12); - PartialDate partial = PartialDate.from(date); - assertThat(partial.getGranularity(), equalTo(Granularity.DAY)); - check(partial, 2000, 10, 12); - } - - @Test - public void granularityOfDayAndMoreFineGrainedCanBeConvertedToLocalDate() { - Instant now = new Instant(); - LocalDate today = new LocalDate(); - Granularity[] supportingConversionToLocalDate = Granularity.DAY - .thisAndMoreFineGrained(); - for (Granularity g : supportingConversionToLocalDate) { - PartialDate partialDate = PartialDate.from(now).with(g); - assertTrue(partialDate.canBeConvertedToLocalDate()); - LocalDate converted = PartialDate - .tryToConvertToLocalDate(partialDate); - assertThat(converted, equalTo(today)); - } - Granularity[] notSupportingConversionToLocalDate = Granularity.DAY - .moreCoarseGrained(); - for (Granularity g : notSupportingConversionToLocalDate) { - PartialDate partialDate = PartialDate.from(now).with(g); - assertFalse(partialDate.canBeConvertedToLocalDate()); - try { - LocalDate converted = PartialDate - .tryToConvertToLocalDate(partialDate); - fail("the partial date cannot be converted to a LocalDate"); - } catch (IllegalArgumentException e) { - // ok - } - } - } - - - @Test - public void fieldsDependOnTimeUnit() throws Exception { - Date now = new Date(); - for (Granularity timeUnit : Granularity.values()) { - PartialDate partial = PartialDate.from(now).with(timeUnit); - DateTimeFieldType types[] = timeUnit.getDateTimeTypes(); - for (int i = 0; i < types.length; i++) { - assertThat(partial.getFieldType(i), equalTo(types[i])); - } - } - } - - @Test - public void retainsValues() throws Exception { - LocalDate date = new LocalDate(2000, 10, 12); - PartialDate partialDate = PartialDate.from( - date.toDateMidnight().toDate()).with(Granularity.DAY); - check(partialDate, 2000, 10, 12); - } - - private void check(PartialDate partialDate, int year, int month, int day) { - assertThat(partialDate.get(DateTimeFieldType.year()), equalTo(year)); - assertThat(partialDate.get(DateTimeFieldType.monthOfYear()), - equalTo(month)); - assertThat(partialDate.get(DateTimeFieldType.dayOfMonth()), - equalTo(day)); - } - - @Test(expected = IllegalArgumentException.class) - public void dontSupportAllTypes() { - LocalDate date = new LocalDate(2000, 10, 12); - PartialDate partialDate = PartialDate.from( - date.toDateMidnight().toDate()).with(Granularity.DAY); - partialDate.get(DateTimeFieldType.minuteOfHour()); - } - - @Test - public void canBeConvertedToMoreCoarseGrained() { - LocalDate date = new LocalDate(2000, 10, 12); - PartialDate partialDate = PartialDate.from( - date.toDateMidnight().toDate()).with(Granularity.DAY); - PartialDate onlyUntilMonthSpecified = partialDate.with(Granularity.MONTH); - assertThat( - onlyUntilMonthSpecified.get(DateTimeFieldType.monthOfYear()), - equalTo(10)); - try { - onlyUntilMonthSpecified.get(DateTimeFieldType.dayOfMonth()); - fail("must send exception since the day is lost in the conversion"); - } catch (IllegalArgumentException e) { - // ok - } - } - - @Test - public void thereIsQueryMethodToKnowIfItCanBeConverted() { - LocalDate date = new LocalDate(2000, 10, 12); - PartialDate partialDate = PartialDate.from( - date.toDateMidnight().toDate()).with(Granularity.DAY); - Granularity[] moreCoarseGrained = { Granularity.YEAR, Granularity.MONTH, - Granularity.DAY }; - for (Granularity t : moreCoarseGrained) { - assertTrue(partialDate.canBeConvertedTo(t)); - } - Granularity[] moreFineGrained = { Granularity.HOUR, Granularity.MINUTE, - Granularity.SECOND, Granularity.MILLISECONDS }; - for (Granularity t : moreFineGrained) { - assertFalse(partialDate.canBeConvertedTo(t)); - } - } - - @Test(expected = IllegalArgumentException.class) - public void cannotBeConvertedToMoreFineGrained() { - LocalDate date = new LocalDate(2000, 10, 12); - PartialDate partialDate = PartialDate.from( - date.toDateMidnight().toDate()).with(Granularity.DAY); - partialDate.with(Granularity.HOUR); - } - - @Test - public void onlyPartialDatesOfTheSameGranuralityCanBeEqual() { - LocalDate date = new LocalDate(2000, 10, 12); - PartialDate partialDate = PartialDate.from( - date.toDateMidnight()).with( - Granularity.DAY); - PartialDate otherOnSameInstantWithDifferentGranularity = PartialDate - .from(date.toDateMidnight()).with( - Granularity.MILLISECONDS); - assertThat(partialDate, - not(equalTo(otherOnSameInstantWithDifferentGranularity))); - } - -} \ No newline at end of file diff --git a/navalplanner-business/src/test/java/org/navalplanner/business/time/PartialDate.java b/navalplanner-business/src/test/java/org/navalplanner/business/time/PartialDate.java deleted file mode 100644 index ef19196a4..000000000 --- a/navalplanner-business/src/test/java/org/navalplanner/business/time/PartialDate.java +++ /dev/null @@ -1,198 +0,0 @@ -package org.navalplanner.business.time; - -import java.util.Date; - -import org.joda.time.Chronology; -import org.joda.time.DateTime; -import org.joda.time.DateTimeField; -import org.joda.time.DateTimeFieldType; -import org.joda.time.DateTimeZone; -import org.joda.time.Instant; -import org.joda.time.LocalDate; -import org.joda.time.Partial; -import org.joda.time.ReadableInstant; -import org.joda.time.ReadablePartial; - -public class PartialDate implements ReadablePartial { - private static final DateTimeFieldType year = DateTimeFieldType.year(); - - private static final DateTimeFieldType monthOfYear = DateTimeFieldType - .monthOfYear(); - - private static final DateTimeFieldType weekOfYear = DateTimeFieldType - .weekOfWeekyear(); - - private static final DateTimeFieldType dayOfMonth = DateTimeFieldType - .dayOfMonth(); - - private static final DateTimeFieldType hourOfDay = DateTimeFieldType - .hourOfDay(); - - private static final DateTimeFieldType minuteOfHour = DateTimeFieldType - .minuteOfHour(); - - private static final DateTimeFieldType secondOfMinute = DateTimeFieldType - .secondOfMinute(); - - private static final DateTimeFieldType millisOfSecond = DateTimeFieldType - .millisOfSecond(); - - public enum Granularity { - - YEAR(year), MONTH(year, monthOfYear), WEEK(year, weekOfYear), DAY(year, - monthOfYear, dayOfMonth), HOUR(year, monthOfYear, dayOfMonth, - hourOfDay), MINUTE(year, monthOfYear, dayOfMonth, hourOfDay, - minuteOfHour), SECOND(year, monthOfYear, dayOfMonth, hourOfDay, - minuteOfHour, secondOfMinute), MILLISECONDS(year, monthOfYear, - dayOfMonth, hourOfDay, minuteOfHour, secondOfMinute, - millisOfSecond); - - private DateTimeFieldType[] types; - - private Granularity(DateTimeFieldType... types) { - this.types = types; - } - - public DateTimeFieldType[] getDateTimeTypes() { - return types.clone(); - } - - boolean isMoreCoarseGrainedThan(Granularity other) { - return this.ordinal() < other.ordinal(); - } - - public Granularity[] thisAndMoreFineGrained() { - Granularity[] result = new Granularity[values().length - - this.ordinal()]; - for (int i = 0; i < result.length; i++) { - result[i] = values()[i + this.ordinal()]; - } - return result; - } - - public Granularity[] moreCoarseGrained() { - Granularity[] result = new Granularity[this.ordinal() - 1]; - for (int i = 0; i < result.length; i++) { - result[i] = values()[i]; - } - return result; - } - } - - public static LocalDate tryToConvertToLocalDate(PartialDate partialDate) { - if (!partialDate.canBeConvertedToLocalDate()) - throw new IllegalArgumentException("the partialDate " + partialDate - + " doesn't support be converted to local date"); - return new LocalDate(partialDate.instant); - } - - public static PartialDate from(LocalDate date) { - long millis = date.toDateMidnight().getMillis(); - return from(millis).with(Granularity.DAY); - } - - public static PartialDate from(DateTime dateTime) { - return from(dateTime.getMillis()); - } - - public static PartialDate from(Date date) { - return from(date.getTime()); - } - - public static PartialDate from(ReadableInstant instant) { - return from(instant.getMillis()); - } - - public static PartialDate from(long timeMilliseconds) { - return new PartialDate(new Instant(timeMilliseconds), - Granularity.MILLISECONDS); - } - - private final Granularity granularity; - - private final Partial partial; - - private final Instant instant; - - private PartialDate(Instant instant, Granularity unit) { - this.partial = asPartial(instant, unit); - this.granularity = unit; - this.instant = instant; - } - - private static Partial asPartial(ReadableInstant instant, Granularity unit) { - DateTime dateTime = interpretInDefaultTimeZone(instant); - DateTimeFieldType[] dateTimeTypes = unit.getDateTimeTypes(); - int[] values = new int[dateTimeTypes.length]; - for (int i = 0; i < values.length; i++) { - values[i] = dateTime.get(dateTimeTypes[i]); - } - return new Partial(dateTimeTypes, values); - } - - private static DateTime interpretInDefaultTimeZone(ReadableInstant instant) { - return new DateTime(instant.getMillis(), DateTimeZone.getDefault()); - } - - public PartialDate with(Granularity granularity) { - if (!canBeConvertedTo(granularity)) { - throw new IllegalArgumentException("the granularity " + granularity - + " is more fine-grained than the current one(" - + this.granularity + "). Conversion is impossible"); - } - return new PartialDate(instant, granularity); - } - - public Granularity getGranularity() { - return this.granularity; - } - - public boolean canBeConvertedToLocalDate() { - return !this.granularity.isMoreCoarseGrainedThan(Granularity.DAY); - } - - public boolean canBeConvertedTo(Granularity unit) { - return !this.granularity.isMoreCoarseGrainedThan(unit); - } - - @Override - public int get(DateTimeFieldType field) { - return partial.get(field); - } - - @Override - public Chronology getChronology() { - return partial.getChronology(); - } - - @Override - public DateTimeField getField(int index) { - return partial.getField(index); - } - - @Override - public DateTimeFieldType getFieldType(int index) { - return granularity.types[index]; - } - - @Override - public int getValue(int index) { - return partial.getValue(index); - } - - @Override - public boolean isSupported(DateTimeFieldType field) { - return partial.isSupported(field); - } - - @Override - public int size() { - return partial.size(); - } - - @Override - public DateTime toDateTime(ReadableInstant baseInstant) { - return partial.toDateTime(baseInstant); - } - -}