Skip to content
This repository has been archived by the owner on Mar 20, 2018. It is now read-only.

Commit

Permalink
initial implementation for a DateTimeBuilder
Browse files Browse the repository at this point in the history
  • Loading branch information
RichardWarburton committed Mar 23, 2012
1 parent 6a802b4 commit 953c79a
Show file tree
Hide file tree
Showing 4 changed files with 228 additions and 15 deletions.
2 changes: 1 addition & 1 deletion src/main/java/javax/time/builder/CopticChrono.java
Expand Up @@ -91,7 +91,7 @@ public enum CopticChrono implements Chrono, DateTimeRules, PeriodRules {
/**
* The maximum permitted epoch-day.
*/
private static final long MAX_EPOCH_DAY = 0;
private static final long MAX_EPOCH_DAY = (long) (MAX_YEAR * 365.25);

private static final long EPOCH_DAYS_OFFSET = 615558;

Expand Down
152 changes: 140 additions & 12 deletions src/main/java/javax/time/builder/DateTimeBuilder.java
Expand Up @@ -31,31 +31,89 @@
*/
package javax.time.builder;

import static javax.time.MathUtils.safeToInt;
import static javax.time.MonthOfYear.JANUARY;
import static javax.time.builder.StandardDateTimeField.DAY_OF_MONTH;
import static javax.time.builder.StandardDateTimeField.DAY_OF_YEAR;
import static javax.time.builder.StandardDateTimeField.EPOCH_DAY;
import static javax.time.builder.StandardDateTimeField.HOUR_OF_DAY;
import static javax.time.builder.StandardDateTimeField.MINUTE_OF_HOUR;
import static javax.time.builder.StandardDateTimeField.MONTH_OF_YEAR;
import static javax.time.builder.StandardDateTimeField.NANO_OF_DAY;
import static javax.time.builder.StandardDateTimeField.NANO_OF_SECOND;
import static javax.time.builder.StandardDateTimeField.SECOND_OF_MINUTE;
import static javax.time.builder.StandardDateTimeField.YEAR;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.EnumMap;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.time.CalendricalException;
import javax.time.Duration;
import javax.time.LocalDate;
import javax.time.LocalDateTime;
import javax.time.LocalTime;
import javax.time.calendrical.DateTimeRule;

/**
* Builder that can combine date and time fields into date and time objects.
* <p>
* This is a mutable class.
*
* @author Richard Warburton
* @author Stephen Colebourne
*/
public class DateTimeBuilder {
public final class DateTimeBuilder {

public boolean containsValue(DateTimeRule rule) {
return false;
private final EnumMap<StandardDateTimeField, Long> values;
private Map<DateTimeField, Long> nonStandardValues;

public static DateTimeBuilder of() {
return new DateTimeBuilder();
}

public long getValue(DateTimeRule rule) {
return 0;
private DateTimeBuilder() {
values = new EnumMap<StandardDateTimeField, Long>(StandardDateTimeField.class);
nonStandardValues = null;
}

public boolean containsValue(DateTimeField field) {
return values.containsKey(field) || nonStandardValues != null && nonStandardValues.containsKey(field);
}

public long getValue(DateTimeField field) {
Long val = values.get(field);
if (val != null) {
return val;
}

if (nonStandardValues != null) {
return nonStandardValues.get(field);
}

throw new CalendricalException("Unable to find a value for that field"); // TODO
}

public DateTimeBuilder add(DateTimeRule rule, long value) {
public DateTimeBuilder add(DateTimeField field, long value) {
if (field instanceof StandardDateTimeField) {
StandardDateTimeField standardField = (StandardDateTimeField) field;
values.put(standardField, value);
} else {
if (nonStandardValues == null) {
nonStandardValues = new HashMap<DateTimeField, Long>();
}
nonStandardValues.put(field, value);
}
return this;
}

public DateTimeBuilder remove(DateTimeRule rule) {
public DateTimeBuilder remove(DateTimeField field) {
if (values.remove(field) == null && nonStandardValues != null) {
nonStandardValues.remove(field);
}
return this;
}

Expand All @@ -64,19 +122,89 @@ public long build() {
}

public LocalDate buildLocalDate() {
return null;
if (hasAllFields(YEAR, MONTH_OF_YEAR, DAY_OF_MONTH)) {
return LocalDate.of(getInt(YEAR), getInt(MONTH_OF_YEAR), getInt(DAY_OF_MONTH));
} else if (hasAllFields(EPOCH_DAY)) {
return LocalDate.ofEpochDay(values.get(EPOCH_DAY));
} else if (hasAllFields(YEAR, DAY_OF_YEAR)) {
return LocalDate.ofYearDay(getInt(YEAR), getInt(DAY_OF_YEAR));
}
throw new CalendricalException("Unable to build Date due to missing fields"); // TODO
}

public LocalTime buildLocalTime() {
return null;
boolean normalFields = hasAllFields(HOUR_OF_DAY, MINUTE_OF_HOUR);
boolean uptoSecond = normalFields && values.containsKey(SECOND_OF_MINUTE);
boolean hasNano = values.containsKey(NANO_OF_SECOND);
if (uptoSecond && hasNano) {
return LocalTime.of(getInt(HOUR_OF_DAY), getInt(MINUTE_OF_HOUR), getInt(SECOND_OF_MINUTE), getInt(NANO_OF_SECOND));
} else if (uptoSecond) {
return LocalTime.of(getInt(HOUR_OF_DAY), getInt(MINUTE_OF_HOUR), getInt(SECOND_OF_MINUTE));
} else if (normalFields) {
return LocalTime.of(getInt(HOUR_OF_DAY), getInt(MINUTE_OF_HOUR));
} else if (values.containsKey(NANO_OF_DAY)) {
return LocalTime.ofNanoOfDay(values.get(NANO_OF_DAY));
}
throw new CalendricalException("Unable to build Time due to missing fields"); // TODO
}

public LocalDateTime buildLocalDateTime() {
return null;
return LocalDateTime.of(buildLocalDate(), buildLocalTime());
}

private int getInt(StandardDateTimeField field) {
return safeToInt(values.get(field));
}

private boolean hasAllFields(StandardDateTimeField ... field) {
for (StandardDateTimeField standardField : field) {
if (!values.containsKey(standardField)) {
return false;
}
}
return true;
}

public LocalDate buildLocalDate(final Chrono chrono) {
LocalDate date = LocalDate.now();
boolean someNonStandard = nonStandardValues != null;
int size = values.size() + (someNonStandard ? nonStandardValues.size() : 0);
List<DateTimeField> fields = new ArrayList<DateTimeField>(size);
fields.addAll(values.keySet());
if (someNonStandard) {
fields.addAll(nonStandardValues.keySet());
}
// Make sure that you set Years before months to account for leap years
// and different length months etc.

Collections.sort(fields, new Comparator<DateTimeField>() {
@Override
public int compare(DateTimeField o1, DateTimeField o2) {
if (o1 instanceof StandardDateTimeField && o2 instanceof StandardDateTimeField) {
StandardDateTimeField s1 = (StandardDateTimeField) o1;
StandardDateTimeField s2 = (StandardDateTimeField) o2;
return -1 * s1.compareTo(s2);
} else {
Duration estimated1 = chrono.getEstimatedDuration(o1.getBaseUnit());
Duration estimated2 = chrono.getEstimatedDuration(o2.getBaseUnit());
return -1 * estimated1.compareTo(estimated2);
}
}
});
for (DateTimeField field : fields) {
Long value = values.get(field);
date = chrono.setDate(date, field, value != null ? value : nonStandardValues.get(field));
}
return date;
}

public <T extends Chrono> DateChronoView<T> buildChronoDateView(T chrono) {
return DateChronoView.of(buildLocalDate(chrono), chrono);
}

protected long resolve() {
return 0;
}

}

5 changes: 3 additions & 2 deletions src/main/java/javax/time/builder/ISOChrono.java
Expand Up @@ -69,11 +69,11 @@ public class ISOChrono implements Chrono, DateTimeRules, PeriodRules {
/**
* The minimum permitted epoch-day.
*/
private static final long MIN_EPOCH_DAY = 0;
private static final long MIN_EPOCH_DAY = (long) (MIN_YEAR * 365.25);
/**
* The maximum permitted epoch-day.
*/
private static final long MAX_EPOCH_DAY = 0;
private static final long MAX_EPOCH_DAY = (long) (MAX_YEAR * 365.25);

@Override
public String getName() {
Expand Down Expand Up @@ -196,6 +196,7 @@ public LocalDate setDate(LocalDate date, DateTimeField field, long newValue) {
case DAY_OF_MONTH: return date.withDayOfMonth((int) newValue);
case DAY_OF_YEAR: return date.withDayOfYear((int) newValue);
case DAY_OF_WEEK: return date.plusDays(newValue - date.getDayOfWeek().getValue());
case EPOCH_DAY: return LocalDate.ofEpochDay(newValue);
}
throw new CalendricalException("Unsupported field on LocalDate: " + field);
}
Expand Down
84 changes: 84 additions & 0 deletions src/test/java/javax/time/builder/TestDateTimeBuilder.java
@@ -0,0 +1,84 @@
package javax.time.builder;

import static javax.time.MonthOfYear.APRIL;
import static javax.time.MonthOfYear.DECEMBER;
import static javax.time.MonthOfYear.JANUARY;
import static javax.time.MonthOfYear.MARCH;
import static javax.time.builder.StandardDateTimeField.DAY_OF_MONTH;
import static javax.time.builder.StandardDateTimeField.DAY_OF_YEAR;
import static javax.time.builder.StandardDateTimeField.EPOCH_DAY;
import static javax.time.builder.StandardDateTimeField.MONTH_OF_YEAR;
import static javax.time.builder.StandardDateTimeField.YEAR;
import static org.testng.Assert.assertEquals;

import javax.time.LocalDate;

import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;

public class TestDateTimeBuilder {

ISOChrono iso = ISOChrono.INSTANCE;
CopticChrono coptic = CopticChrono.INSTANCE;

@DataProvider(name="isodates")
Object[][] dateEquivalencesProvider() {
return new Object[][]{
{ 2012, 3, 15, LocalDate.of(2012, MARCH, 15),},
{ 2011, 12, 28, LocalDate.of(2011, DECEMBER, 28),},
{ 1, 1, 1, LocalDate.of(1, JANUARY, 1),},
};
}

@Test(dataProvider="isodates", groups = "tck")
public void buildsTo(int year, int month, int day, LocalDate result) {
DateTimeBuilder builder = DateTimeBuilder.of();
builder.add(YEAR, year);
builder.add(MONTH_OF_YEAR, month);
builder.add(DAY_OF_MONTH, day);
assertEquals(builder.buildLocalDate(), result);
}

@Test(dataProvider="isodates", groups = "tck")
public void reverseBuildsTo(int year, int month, int day, LocalDate result) {
DateTimeBuilder builder = DateTimeBuilder.of();
builder.add(DAY_OF_MONTH, day);
builder.add(MONTH_OF_YEAR, month);
builder.add(YEAR, year);
assertEquals(builder.buildLocalDate(), result);
}

@Test(dataProvider="isodates", groups = "tck")
public void buildsFromEpochDay(int year, int month, int day, LocalDate result) {
DateTimeBuilder builder = DateTimeBuilder.of();
builder.add(EPOCH_DAY, result.toEpochDay());
assertEquals(builder.buildLocalDate(), result);
}

@Test(dataProvider="isodates", groups = "tck")
public void buildsFromDayOfYear(int year, int month, int day, LocalDate result) {
DateTimeBuilder builder = DateTimeBuilder.of();
long dayOfYear = iso.getDateValue(result, DAY_OF_YEAR);
builder.add(YEAR, year);
builder.add(DAY_OF_YEAR, dayOfYear);
assertEquals(builder.buildLocalDate(), result);
}

@DataProvider(name="chronoDates")
Object[][] chronologyDateProvider() {
return new Object[][]{
{ 1728, 8, 6, LocalDate.of(2012, APRIL, 14), coptic },
{ 1729, 4, 19, LocalDate.of(2012, DECEMBER, 28), coptic },
};
}

@Test(dataProvider="chronoDates", groups = "tck")
public void buildsFromChrono(int year, int month, int day, LocalDate result, Chrono chrono) {
DateTimeBuilder builder = DateTimeBuilder.of();
builder.add(DAY_OF_MONTH, day);
builder.add(MONTH_OF_YEAR, month);
builder.add(YEAR, year);
assertEquals(builder.buildLocalDate(chrono), result);
}

}

0 comments on commit 953c79a

Please sign in to comment.