ItEr14S08ModeladoTempoItEr13S09: PartialDate, IntervalsOfPartialDates and TimeQuantities.

This commit is contained in:
Óscar González Fernández 2009-06-25 12:49:09 +02:00 committed by Javier Moran Rua
parent dfe98d469a
commit 56925dd424
8 changed files with 1060 additions and 380 deletions

View file

@ -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();
}
}

View file

@ -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 <ogonzalez@igalia.com>
*/
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<Integer> 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<Integer> 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<Integer> asIntegersList(int[] values) {
List<Integer> result = new ArrayList<Integer>();
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<Integer> getValues(Partial partial,
Granularity granularity) {
List<Integer> values = new ArrayList<Integer>();
for (DateTimeFieldType fieldType : granularity.getDateTimeTypes()) {
values.add(partial.get(fieldType));
}
return values;
}
private static Instant toNormalizedInstant(Granularity granularity,
List<Integer> values) {
return new Partial(granularity.getDateTimeTypes(), asIntArray(values),
ISOChronology.getInstance(DateTimeZone.getDefault()))
.toDateTime(new DateTime(0, DateTimeZone.getDefault()))
.toInstant();
}
private static int[] asIntArray(List<Integer> 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<Integer> 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<Integer> iterator = this.values.iterator();
Iterator<Integer> 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<Integer> bigger, List<Integer> other) {
assert bigger.size() == other.size();
int[] result = new int[bigger.size()];
Iterator<Integer> iterator = bigger.iterator();
Iterator<Integer> 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());
}
}

View file

@ -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 <br />
* @author Óscar González Fernández <ogonzalez@igalia.com>
*/
public class TimeQuantity {
private static EnumMap<Granularity, Integer> createSumOf(
EnumMap<Granularity, Integer>... values) {
EnumMap<Granularity, Integer> result = new EnumMap<Granularity, Integer>(
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<Granularity, Integer>... maps) {
Integer result = null;
for (EnumMap<Granularity, Integer> enumMap : maps) {
if (enumMap.containsKey(granularity)) {
Integer valueToAdd = enumMap.get(granularity);
result = result == null ? enumMap.get(granularity) : result
+ valueToAdd;
}
}
return result;
}
private static EnumMap<Granularity, Integer> copyAndAdd(
EnumMap<Granularity, Integer> existent, int quantity,
Granularity granularity) {
EnumMap<Granularity, Integer> result = new EnumMap<Granularity, Integer>(
existent);
int newQuantity = quantity;
if (result.containsKey(granularity)) {
newQuantity += result.get(granularity);
}
result.put(granularity, newQuantity);
return result;
}
private final EnumMap<Granularity, Integer> values;
public static TimeQuantity empty() {
return new TimeQuantity();
}
public TimeQuantity() {
this(new EnumMap<Granularity, Integer>(Granularity.class));
}
private TimeQuantity(EnumMap<Granularity, Integer> 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;
}
}

View file

@ -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));
}
}
}

View file

@ -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));
}
}

View file

@ -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));
}
}

View file

@ -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)));
}
}

View file

@ -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);
}
}