All Downloads are FREE. Search and download functionalities are using the official Maven repository.

edu.stanford.nlp.time.JodaTimeUtils Maven / Gradle / Ivy

Go to download

Stanford CoreNLP provides a set of natural language analysis tools which can take raw English language text input and give the base forms of words, their parts of speech, whether they are names of companies, people, etc., normalize dates, times, and numeric quantities, mark up the structure of sentences in terms of phrases and word dependencies, and indicate which noun phrases refer to the same entities. It provides the foundational building blocks for higher level text understanding applications.

There is a newer version: 4.5.7
Show newest version
package edu.stanford.nlp.time;

import org.joda.time.*;
import org.joda.time.chrono.ISOChronology;
import org.joda.time.field.DividedDateTimeField;
import org.joda.time.field.OffsetDateTimeField;
import org.joda.time.field.RemainderDateTimeField;
import org.joda.time.field.ScaledDurationField;

import java.util.Set;

import static org.joda.time.DateTimeFieldType.*;
import static org.joda.time.DurationFieldType.*;

import edu.stanford.nlp.util.Generics;

/**
 * Extensions to Joda time.
 *
 * @author Angel Chang
 * @author Gabor Angeli
 */
public class JodaTimeUtils {

  private JodaTimeUtils() {} // static methods only

  // Standard ISO fields
  protected static final Chronology isoUTCChronology = ISOChronology.getInstanceUTC();
  protected static final DateTimeFieldType[] standardISOFields = {
          DateTimeFieldType.year(),
          DateTimeFieldType.monthOfYear(),
          DateTimeFieldType.dayOfMonth(),
          DateTimeFieldType.hourOfDay(),
          DateTimeFieldType.minuteOfHour(),
          DateTimeFieldType.secondOfMinute(),
          DateTimeFieldType.millisOfSecond()
  };
  protected static final DateTimeFieldType[] standardISOWeekFields = {
          DateTimeFieldType.year(),  // should this be weekyear()?
          DateTimeFieldType.weekOfWeekyear(),
          DateTimeFieldType.dayOfWeek(),
          DateTimeFieldType.hourOfDay(),
          DateTimeFieldType.minuteOfHour(),
          DateTimeFieldType.secondOfMinute(),
          DateTimeFieldType.millisOfSecond()
  };
  protected static final DateTimeFieldType[] standardISODateFields = {
          DateTimeFieldType.year(),
          DateTimeFieldType.monthOfYear(),
          DateTimeFieldType.dayOfMonth(),
  };
  protected static final DateTimeFieldType[] standardISOTimeFields = {
          DateTimeFieldType.hourOfDay(),
          DateTimeFieldType.minuteOfHour(),
          DateTimeFieldType.secondOfMinute(),
          DateTimeFieldType.millisOfSecond()
  };
  public static final Partial EMPTY_ISO_PARTIAL = new Partial(standardISOFields, new int[]{0,1,1,0,0,0,0});
  public static final Partial EMPTY_ISO_WEEK_PARTIAL = new Partial(standardISOWeekFields, new int[]{0,1,1,0,0,0,0});
  public static final Partial EMPTY_ISO_DATE_PARTIAL = new Partial(standardISODateFields, new int[]{0,1,1});
  public static final Partial EMPTY_ISO_TIME_PARTIAL = new Partial(standardISOTimeFields, new int[]{0,0,0,0});
  public static final Instant INSTANT_ZERO = new Instant(0);


  // Extensions to Joda time fields
  // Duration Fields
  public static final DurationFieldType Quarters = new DurationFieldType("quarters") {
    private static final long serialVersionUID = -8167713675442491871L;

    public DurationField getField(Chronology chronology) {
      return new ScaledDurationField(chronology.months(), Quarters, 3);
    }
  };

  public static final DurationFieldType HalfYears = new DurationFieldType("halfyear") {
    private static final long serialVersionUID = -8167713675442491872L;

    public DurationField getField(Chronology chronology) {
      return new ScaledDurationField(chronology.months(), HalfYears, 6);
    }
  };

  public static final DurationFieldType Decades = new DurationFieldType("decades") {
    private static final long serialVersionUID = -4594189766036833410L;

    public DurationField getField(Chronology chronology) {
      return new ScaledDurationField(chronology.years(), Decades, 10);
    }
  };

  public static final DurationFieldType Centuries = new DurationFieldType("centuries") {
    private static final long serialVersionUID = -7268694266711862790L;

    public DurationField getField(Chronology chronology) {
      return new ScaledDurationField(chronology.years(), Centuries, 100);
    }
  };

  // DateTimeFields
  public static final DateTimeFieldType QuarterOfYear = new DateTimeFieldType("quarterOfYear") {
    private static final long serialVersionUID = -5677872459807379123L;

    public DurationFieldType getDurationType() {
      return Quarters;
    }

    public DurationFieldType getRangeDurationType() {
      return DurationFieldType.years();
    }

    public DateTimeField getField(Chronology chronology) {
      return new OffsetDateTimeField(new DividedDateTimeField(new OffsetDateTimeField(chronology.monthOfYear(), -1), QuarterOfYear, 3), 1);
    }
  };

  public static final DateTimeFieldType HalfYearOfYear = new DateTimeFieldType("halfYearOfYear") {
    private static final long serialVersionUID = -5677872459807379123L;

    public DurationFieldType getDurationType() {
      return HalfYears;
    }

    public DurationFieldType getRangeDurationType() {
      return DurationFieldType.years();
    }

    public DateTimeField getField(Chronology chronology) {
      return new OffsetDateTimeField(new DividedDateTimeField(new OffsetDateTimeField(chronology.monthOfYear(), -1), HalfYearOfYear, 6), 1);
    }
  };

  public static final DateTimeFieldType MonthOfQuarter = new DateTimeFieldType("monthOfQuarter") {
    private static final long serialVersionUID = -5677872459807379123L;

    public DurationFieldType getDurationType() {
      return DurationFieldType.months();
    }

    public DurationFieldType getRangeDurationType() {
      return Quarters;
    }

    public DateTimeField getField(Chronology chronology) {
      return new OffsetDateTimeField(new RemainderDateTimeField(new OffsetDateTimeField(chronology.monthOfYear(), -1), MonthOfQuarter, 3), 1);
    }
  };

  public static final DateTimeFieldType MonthOfHalfYear = new DateTimeFieldType("monthOfHalfYear") {
    private static final long serialVersionUID = -5677872459807379123L;

    public DurationFieldType getDurationType() {
      return DurationFieldType.months();
    }

    public DurationFieldType getRangeDurationType() {
      return HalfYears;
    }

    public DateTimeField getField(Chronology chronology) {
      return new OffsetDateTimeField(new RemainderDateTimeField(new OffsetDateTimeField(chronology.monthOfYear(), -1), MonthOfHalfYear, 6), 1);
    }
  };

  public static final DateTimeFieldType WeekOfMonth = new DateTimeFieldType("weekOfMonth") {
    private static final long serialVersionUID = 8676056306203579438L;

    public DurationFieldType getDurationType() {
      return DurationFieldType.weeks();
    }

    public DurationFieldType getRangeDurationType() {
      return DurationFieldType.months();
    }

    public DateTimeField getField(Chronology chronology) {
      return new OffsetDateTimeField(new RemainderDateTimeField(new OffsetDateTimeField(chronology.weekOfWeekyear(), -1), WeekOfMonth, 4), 1);
    }
  };

  public static final DateTimeFieldType DecadeOfCentury = new DateTimeFieldType("decadeOfCentury") {
    private static final long serialVersionUID = 4301444712229535664L;

    public DurationFieldType getDurationType() {
      return Decades;
    }

    public DurationFieldType getRangeDurationType() {
      return DurationFieldType.centuries();
    }

    public DateTimeField getField(Chronology chronology) {
      return new DividedDateTimeField(chronology.yearOfCentury(), DecadeOfCentury, 10);
    }
  };

  public static final DateTimeFieldType YearOfDecade = new DateTimeFieldType("yearOfDecade") {
    private static final long serialVersionUID = 4301444712229535664L;

    public DurationFieldType getDurationType() {
      return DurationFieldType.years();
    }

    public DurationFieldType getRangeDurationType() {
      return Decades;
    }

    public DateTimeField getField(Chronology chronology) {
      return new DividedDateTimeField(chronology.yearOfCentury(), YearOfDecade, 10);
    }
  };

  // Helper functions for working with joda time type
  protected static boolean hasField(ReadablePartial base, DateTimeFieldType field)
  {
    if (base == null) {
      return false;
    } else {
      return base.isSupported(field);
    }
  }

  protected static boolean hasYYYYMMDD(ReadablePartial base)
  {
    if (base == null) {
      return false;
    } else {
      return base.isSupported(DateTimeFieldType.year()) &&
             base.isSupported(DateTimeFieldType.monthOfYear()) &&
             base.isSupported(DateTimeFieldType.dayOfMonth());
    }
  }

  protected static boolean hasYYMMDD(ReadablePartial base)
  {
    if (base == null) {
      return false;
    } else {
      return base.isSupported(DateTimeFieldType.yearOfCentury()) &&
             base.isSupported(DateTimeFieldType.monthOfYear()) &&
             base.isSupported(DateTimeFieldType.dayOfMonth());
    }
  }

  protected static boolean hasField(ReadablePeriod base, DurationFieldType field)
  {
    if (base == null) {
      return false;
    } else {
      return base.isSupported(field);
    }
  }

  protected static Partial setField(Partial base, DateTimeFieldType field, int value) {
    if (base == null) {
      return new Partial(field, value);
    } else {
      return base.with(field, value);
    }
  }

  public static Set getSupportedDurationFields(Partial p)
  {
    Set supportedDurations = Generics.newHashSet();
    for (int i = 0; i < p.size(); i++) {
      supportedDurations.add(p.getFieldType(i).getDurationType());
    }
    return supportedDurations;
  }
  public static Period getUnsupportedDurationPeriod(Partial p, Period offset)
  {
    if (offset == null) { return null; }
    Set supported = getSupportedDurationFields(p);
    Period res = null;
    for (int i = 0; i < offset.size(); i++) {
      if (!supported.contains(offset.getFieldType(i))) {
        if (offset.getValue(i) != 0) {
          if (res == null) { res = new Period(); }
          res = res.withField(offset.getFieldType(i), offset.getValue(i));
        }
      }
    }
    return res;
  }
  public static Partial combine(Partial p1, Partial p2) {
    if (p1 == null) return p2;
    if (p2 == null) return p1;
    Partial p = p1;
    for (int i = 0; i < p2.size(); i++) {
      DateTimeFieldType fieldType = p2.getFieldType(i);
      if (fieldType == DateTimeFieldType.year()) {
        if (p.isSupported(DateTimeFieldType.yearOfCentury())) {
          if (!p.isSupported(DateTimeFieldType.centuryOfEra())) {
            int yoc = p.get(DateTimeFieldType.yearOfCentury());
            int refYear = p2.getValue(i);
            int century = refYear / 100;
            int y2 = yoc + century*100;
            // TODO: Figure out which way to go
            if (refYear < y2) {
              y2 -= 100;
            }
            p = p.without(DateTimeFieldType.yearOfCentury());
            p = p.with(DateTimeFieldType.year(), y2);
          }
          continue;
        } else if (p.isSupported(DateTimeFieldType.centuryOfEra())) {
          continue;
        }
      } else if (fieldType == DateTimeFieldType.yearOfCentury()) {
        if (p.isSupported(DateTimeFieldType.year())) {
          continue;
        }
      } else if (fieldType == DateTimeFieldType.centuryOfEra()) {
        if (p.isSupported(DateTimeFieldType.year())) {
          continue;
        }
      }
      if (!p.isSupported(fieldType)) {
        p = p.with(fieldType, p2.getValue(i));
      }
    }
    if (!p.isSupported(DateTimeFieldType.year())) {
      if (p.isSupported(DateTimeFieldType.yearOfCentury()) && p.isSupported(DateTimeFieldType.centuryOfEra())) {
        int year = p.get(DateTimeFieldType.yearOfCentury()) + p.get(DateTimeFieldType.centuryOfEra())*100;
        p = p.with(DateTimeFieldType.year(), year);
        p = p.without(DateTimeFieldType.yearOfCentury());
        p = p.without(DateTimeFieldType.centuryOfEra());
      }
    }
    if (p.isSupported(DateTimeFieldType.halfdayOfDay())) {
      int hour = -1;
      if (p.isSupported(DateTimeFieldType.hourOfHalfday())) {
        hour = p.get(DateTimeFieldType.hourOfHalfday());
        p = p.without(DateTimeFieldType.hourOfHalfday());
      } else if (p.isSupported(DateTimeFieldType.clockhourOfHalfday())) {
        hour = p.get(DateTimeFieldType.clockhourOfHalfday())-1;
        p = p.without(DateTimeFieldType.clockhourOfHalfday());
      } else if (p.isSupported(DateTimeFieldType.clockhourOfDay())) {
        hour = p.get(DateTimeFieldType.clockhourOfDay())-1;
        p = p.without(DateTimeFieldType.clockhourOfDay());
      } else if (p.isSupported(DateTimeFieldType.hourOfDay())) {
        hour = p.get(DateTimeFieldType.hourOfDay());
        p = p.without(DateTimeFieldType.hourOfDay());
      }
      if (hour >= 0) {
        if (p.get(DateTimeFieldType.halfdayOfDay()) == SUTime.HALFDAY_PM) {
          if (hour < 12) {
            hour = hour+12;
          }
        } else if (hour == 12) {
          hour = 0;
        }
        if (hour < 24) {
          p = p.with(DateTimeFieldType.hourOfDay(), hour);
        } else {
          p = p.with(DateTimeFieldType.clockhourOfDay(), hour);
        }
      }
    }
    return p;
  }
  protected static DateTimeFieldType getMostGeneral(Partial p)
  {
    if (p.size() > 0) { return p.getFieldType(0); }
    return null;
  }
  protected static DateTimeFieldType getMostSpecific(Partial p)
  {
    if (p.size() > 0) { return p.getFieldType(p.size()-1); }
    return null;
  }
  protected static DurationFieldType getMostGeneral(Period p)
  {
    for (int i = 0; i < p.size(); i++) {
      if (p.getValue(i) != 0) {
        return p.getFieldType(i);
      }
    }
    return null;
  }
  protected static DurationFieldType getMostSpecific(Period p)
  {
    for (int i = p.size()-1; i >= 0; i--) {
      if (p.getValue(i) != 0) {
        return p.getFieldType(i);
      }
    }
    return null;
  }
  protected static Period getJodaTimePeriod(Partial p)
  {
    if (p.size() > 0) {
      DateTimeFieldType dtType = p.getFieldType(p.size()-1);
      DurationFieldType dType = dtType.getDurationType();
      Period period = new Period();
      if (period.isSupported(dType)) {
       return period.withField(dType, 1);
      } else {
        DurationField df = dType.getField(p.getChronology());
        if (df instanceof ScaledDurationField) {
          ScaledDurationField sdf = (ScaledDurationField) df;
          return period.withField(sdf.getWrappedField().getType(), sdf.getScalar());
        }
       // PeriodType.forFields(new DurationFieldType[]{dType});
       // return new Period(df.getUnitMillis(), PeriodType.forFields(new DurationFieldType[]{dType}));

      }
    }
    return null;
  }
  public static Partial combineMoreGeneralFields(Partial p1, Partial p2) {
    return combineMoreGeneralFields(p1, p2, null);
  }

  // Combines more general fields from p2 to p1
  public static Partial combineMoreGeneralFields(Partial p1, Partial p2, DateTimeFieldType mgf) {
    Partial p = p1;
    Chronology c1 = p1.getChronology();
    Chronology c2 = p2.getChronology();
    if (!c1.equals(c2)) {
      throw new RuntimeException("Different chronology: c1=" + c1 + ", c2=" + c2);
    }
    DateTimeFieldType p1MostGeneralField = null;
    if (p1.size() > 0) {
      p1MostGeneralField = p1.getFieldType(0);    // Assume fields ordered from most general to least....
    }
    if (mgf == null || (p1MostGeneralField != null && isMoreGeneral(p1MostGeneralField, mgf, c1))) {
      mgf = p1MostGeneralField;
    }
    for (int i = 0; i < p2.size(); i++) {
      DateTimeFieldType fieldType = p2.getFieldType(i);
      if (fieldType == DateTimeFieldType.year()) {
        if (p.isSupported(DateTimeFieldType.yearOfCentury())) {
          if (!p.isSupported(DateTimeFieldType.centuryOfEra())) {
            int yoc = p.get(DateTimeFieldType.yearOfCentury());
            int refYear = p2.getValue(i);
            int century = refYear / 100;
            int y2 = yoc + century*100;
            // TODO: Figure out which way to go
            if (refYear < y2) {
              y2 -= 100;
            }
            p = p.without(DateTimeFieldType.yearOfCentury());
            p = p.with(DateTimeFieldType.year(), y2);
          }
          continue;
        } else if (p.isSupported(JodaTimeUtils.DecadeOfCentury)) {
          if (!p.isSupported(DateTimeFieldType.centuryOfEra())) {
            int decade = p.get(JodaTimeUtils.DecadeOfCentury);
            int refYear = p2.getValue(i);
            int century = refYear / 100;
            int y2 = decade*10 + century*100;
            // TODO: Figure out which way to go
            if (refYear < y2) {
              century--;
            }
            p = p.with(DateTimeFieldType.centuryOfEra(), century);
          }
          continue;
        }
      }
      if (mgf == null || isMoreGeneral(fieldType, mgf, c1)) {
        if (!p.isSupported(fieldType)) {
          p = p.with(fieldType, p2.getValue(i));
        }
      } else {
        break;
      }
    }
    if (!p.isSupported(DateTimeFieldType.year())) {
      if (p.isSupported(DateTimeFieldType.yearOfCentury()) && p.isSupported(DateTimeFieldType.centuryOfEra())) {
        int year = p.get(DateTimeFieldType.yearOfCentury()) + p.get(DateTimeFieldType.centuryOfEra())*100;
        p = p.with(DateTimeFieldType.year(), year);
        p = p.without(DateTimeFieldType.yearOfCentury());
        p = p.without(DateTimeFieldType.centuryOfEra());
      }
    }
    return p;
  }

  public static Partial discardMoreSpecificFields(Partial p, DateTimeFieldType d)
  {
    Partial res = new Partial();
    for (int i = 0; i < p.size(); i++) {
      DateTimeFieldType fieldType = p.getFieldType(i);
      if (fieldType.equals(d) || isMoreGeneral(fieldType, d, p.getChronology())) {
        res = res.with(fieldType, p.getValue(i));
      }
    }
    if (res.isSupported(JodaTimeUtils.DecadeOfCentury) && !res.isSupported(DateTimeFieldType.centuryOfEra())) {
      if (p.isSupported(DateTimeFieldType.year())) {
        res = res.with(DateTimeFieldType.centuryOfEra(), p.get(DateTimeFieldType.year()) / 100);
      }
    }
    return res;
  }

  public static Partial discardMoreSpecificFields(Partial p, DurationFieldType dft)
  {
    DurationField df = dft.getField(p.getChronology());
    Partial res = new Partial();
    for (int i = 0; i < p.size(); i++) {
      DateTimeFieldType fieldType = p.getFieldType(i);
      DurationField f = fieldType.getDurationType().getField(p.getChronology());
      int cmp = df.compareTo(f);
      if (cmp <= 0) {
        res = res.with(fieldType, p.getValue(i));
      }
    }
    return res;
  }

  public static Period discardMoreSpecificFields(Period p, DurationFieldType dft, Chronology chronology)
  {
    DurationField df = dft.getField(chronology);
    Period res = new Period();
    for (int i = 0; i < p.size(); i++) {
      DurationFieldType fieldType = p.getFieldType(i);
      DurationField f = fieldType.getField(chronology);
      int cmp = df.compareTo(f);
      if (cmp <= 0) {
        res = res.withField(fieldType, p.getValue(i));
      }
    }
    return res;
  }

  public static Partial padMoreSpecificFields(Partial p, Period granularity)
  {
    DateTimeFieldType msf = getMostSpecific(p);
    if (isMoreGeneral(msf, DateTimeFieldType.year(), p.getChronology()) ||
            isMoreGeneral(msf, DateTimeFieldType.yearOfCentury(), p.getChronology())) {
      if (p.isSupported(DateTimeFieldType.yearOfCentury())) {
        // OKAY
      } else {
        if (p.isSupported(JodaTimeUtils.DecadeOfCentury)) {
          if (p.isSupported(DateTimeFieldType.centuryOfEra())) {
            int year = p.get(DateTimeFieldType.centuryOfEra()) * 100 + p.get(JodaTimeUtils.DecadeOfCentury)*10;
            p = p.without(JodaTimeUtils.DecadeOfCentury);
            p = p.without(DateTimeFieldType.centuryOfEra());
            p = p.with(DateTimeFieldType.year(), year);
          } else {
            int year = p.get(JodaTimeUtils.DecadeOfCentury)*10;
            p = p.without(JodaTimeUtils.DecadeOfCentury);
            p = p.with(DateTimeFieldType.yearOfCentury(), year);
          }
        } else {
          if (p.isSupported(DateTimeFieldType.centuryOfEra())) {
            int year = p.get(DateTimeFieldType.centuryOfEra()) * 100;
            p = p.without(DateTimeFieldType.centuryOfEra());
            p = p.with(DateTimeFieldType.year(), year);
          }
        }
      }
    }
    boolean useWeek = false;
    if (p.isSupported(DateTimeFieldType.weekOfWeekyear())) {
      if (!p.isSupported(DateTimeFieldType.dayOfMonth()) && !p.isSupported(DateTimeFieldType.dayOfWeek())) {
        p = p.with(DateTimeFieldType.dayOfWeek(), 1);
        if (p.isSupported(DateTimeFieldType.monthOfYear())) {
          p = p.without(DateTimeFieldType.monthOfYear());
        }
      }
      useWeek = true;
    }
    Partial p2 = useWeek? EMPTY_ISO_WEEK_PARTIAL:EMPTY_ISO_PARTIAL;
    for (int i = 0; i < p2.size(); i++) {
      DateTimeFieldType fieldType = p2.getFieldType(i);
      if (msf == null || isMoreSpecific(fieldType, msf, p.getChronology())) {
        if (!p.isSupported(fieldType)) {
          if (fieldType == DateTimeFieldType.monthOfYear()) {
            if (p.isSupported(QuarterOfYear)) {
              p = p.with(DateTimeFieldType.monthOfYear(), (p.get(QuarterOfYear)-1)*3+1);
              continue;
            } else if (p.isSupported(HalfYearOfYear)) {
              p = p.with(DateTimeFieldType.monthOfYear(), (p.get(HalfYearOfYear)-1)*6+1);
              continue;
            }
          }
          p = p.with(fieldType, p2.getValue(i));
        }
      }
    }
    if (granularity != null) {
      DurationFieldType mostSpecific = getMostSpecific(granularity);
      p = discardMoreSpecificFields(p, mostSpecific);
    }
    return p;
  }

  public static boolean isCompatible(Partial p1, Partial p2) {
    if (p1 == null) return true;
    if (p2 == null) return true;
    for (int i = 0; i < p1.size(); i++) {
      DateTimeFieldType type = p1.getFieldType(i);
      int v = p1.getValue(i);
      if (JodaTimeUtils.hasField(p2,type)) {
        if (v != p2.get(type)) {
          return false;
        }
      }
    }
    return true;
  }
  // Uses p2 to resolve dow for p1
  public static Partial resolveDowToDay(Partial p1, Partial p2)
  {
    // Discard anything that's more specific than dayOfMonth for p2
    p2 = JodaTimeUtils.discardMoreSpecificFields(p2, DateTimeFieldType.dayOfMonth());
    if (isCompatible(p1,p2)) {
      if (p1.isSupported(DateTimeFieldType.dayOfWeek())) {
        if (!p1.isSupported(DateTimeFieldType.dayOfMonth())) {
          if (p2.isSupported(DateTimeFieldType.dayOfMonth()) && p2.isSupported(DateTimeFieldType.monthOfYear()) && p2.isSupported(DateTimeFieldType.year())) {
            Instant t2 = getInstant(p2);
            DateTime t1 = p1.toDateTime(t2);
            return getPartial(t1.toInstant(), p1.with(DateTimeFieldType.dayOfMonth(), 1)/*.with(DateTimeFieldType.weekOfWeekyear(), 1) */);
          }
        }
      }
    }
    return p1;
  }

  public static Partial withWeekYear(Partial p)
  {
    Partial res = new Partial();
    for (int i = 0; i < p.size(); i++) {
      DateTimeFieldType fieldType = p.getFieldType(i);
      if (fieldType == DateTimeFieldType.year()) {
        res = res.with(DateTimeFieldType.weekyear(), p.getValue(i));
      } else {
        res = res.with(fieldType, p.getValue(i));
      }
    }
    return res;
  }

  // Resolve dow for p1
  public static Partial resolveDowToDay(Partial p)
  {
    if (p.isSupported(DateTimeFieldType.dayOfWeek())) {
      if (!p.isSupported(DateTimeFieldType.dayOfMonth())) {
        if (p.isSupported(DateTimeFieldType.weekOfWeekyear()) && (p.isSupported(DateTimeFieldType.year()))) {
          // Convert from year to weekyear (to avoid weirdness when the weekyear and year don't match at the beginning of the year)
          Partial pwy = withWeekYear(p);
          Instant t2 = getInstant(pwy);
          DateTime t1 = pwy.toDateTime(t2);
          Partial res = getPartial(t1.toInstant(), EMPTY_ISO_PARTIAL);
          DateTimeFieldType mostSpecific = getMostSpecific(p);
          res = discardMoreSpecificFields(res, mostSpecific.getDurationType());
          return res;
        }
      }
    }
    return p;
  }

  // Uses p2 to resolve week for p1
  public static Partial resolveWeek(Partial p1, Partial p2)
  {
    if (isCompatible(p1,p2)) {
        if (!p1.isSupported(DateTimeFieldType.dayOfMonth())) {
          if (p2.isSupported(DateTimeFieldType.dayOfMonth()) && p2.isSupported(DateTimeFieldType.monthOfYear()) && p2.isSupported(DateTimeFieldType.year())) {
            Instant t2 = getInstant(p2);
            DateTime t1 = p1.toDateTime(t2);
            return getPartial(t1.toInstant(), p1.without(DateTimeFieldType.dayOfMonth()).without(DateTimeFieldType.monthOfYear()).with(DateTimeFieldType.weekOfWeekyear(), 1));
          }
      }
    }
    return p1;
  }
  public static Partial resolveWeek(Partial p)
  {
    // Figure out week
    if (p.isSupported(DateTimeFieldType.dayOfMonth()) && p.isSupported(DateTimeFieldType.monthOfYear()) && p.isSupported(DateTimeFieldType.year())) {
      Instant t = getInstant(p);
//      return getPartial(t.toInstant(), p.without(DateTimeFieldType.dayOfMonth()).without(DateTimeFieldType.monthOfYear()).with(DateTimeFieldType.weekOfWeekyear(), 1));
      return getPartial(t.toInstant(), p.with(DateTimeFieldType.weekOfWeekyear(), 1));
    } else return p;
  }

  public static Instant getInstant(Partial p)
  {
    if (p == null) return null;
    int year = p.isSupported(DateTimeFieldType.year())? p.get(DateTimeFieldType.year()):0;
    if (!p.isSupported(DateTimeFieldType.year())) {
      if (p.isSupported(DateTimeFieldType.centuryOfEra())) {
        year += 100*p.get(DateTimeFieldType.centuryOfEra());
      }
      if (p.isSupported(DateTimeFieldType.yearOfCentury())) {
        year += p.get(DateTimeFieldType.yearOfCentury());
      } else if (p.isSupported(DecadeOfCentury)) {
        year += 10*p.get(DecadeOfCentury);
      }
    }
    int moy = p.isSupported(DateTimeFieldType.monthOfYear())? p.get(DateTimeFieldType.monthOfYear()):1;
    if (!p.isSupported(DateTimeFieldType.monthOfYear())) {
      if (p.isSupported(QuarterOfYear)) {
        moy += 3*(p.get(QuarterOfYear)-1);
      }
    }
    int dom = p.isSupported(DateTimeFieldType.dayOfMonth())? p.get(DateTimeFieldType.dayOfMonth()):1;
    int hod = p.isSupported(DateTimeFieldType.hourOfDay())? p.get(DateTimeFieldType.hourOfDay()):0;
    int moh = p.isSupported(DateTimeFieldType.minuteOfHour())? p.get(DateTimeFieldType.minuteOfHour()):0;
    int som = p.isSupported(DateTimeFieldType.secondOfMinute())? p.get(DateTimeFieldType.secondOfMinute()):0;
    int msos = p.isSupported(DateTimeFieldType.millisOfSecond())? p.get(DateTimeFieldType.millisOfSecond()):0;
    return new DateTime(year, moy, dom, hod, moh, som, msos, isoUTCChronology).toInstant();
  }

  public static Partial getPartial(Instant t, Partial p)
  {
    Partial res = new Partial(p);
    for (int i = 0; i < p.size(); i++) {
      res = res.withField(p.getFieldType(i), t.get(p.getFieldType(i)));
    }
    return res;
  }

  // Add duration to partial
  public static Partial addForce(Partial p, Period d, int scalar)
  {
    Instant t = getInstant(p);
    t = t.withDurationAdded(d.toDurationFrom(INSTANT_ZERO), scalar);
    return getPartial(t, p);
  }

  // Returns if df1 is more general than df2
  public static boolean isMoreGeneral(DateTimeFieldType df1, DateTimeFieldType df2, Chronology chronology)
  {
    DurationFieldType df1DurationFieldType = df1.getDurationType();
    DurationFieldType df2DurationFieldType = df2.getDurationType();
    if (!df2DurationFieldType.equals(df1DurationFieldType)) {
      DurationField df1Unit = df1DurationFieldType.getField(chronology);
      DurationFieldType p = df2.getRangeDurationType();
      if (p != null) {
        DurationField df2Unit = df2DurationFieldType.getField(chronology);
        int cmp = df1Unit.compareTo(df2Unit);
        if (cmp > 0) {
          return true;
        }
      }
    }
    return false;
  }

  // Returns if df1 is more specific than df2
  public static boolean isMoreSpecific(DateTimeFieldType df1, DateTimeFieldType df2, Chronology chronology)
  {
    DurationFieldType df1DurationFieldType = df1.getDurationType();
    DurationFieldType df2DurationFieldType = df2.getDurationType();
    if (!df2DurationFieldType.equals(df1DurationFieldType)) {
      DurationField df2Unit = df2DurationFieldType.getField(chronology);
      DurationFieldType p = df1.getRangeDurationType();
      if (p != null) {
        DurationField df1Unit = df1DurationFieldType.getField(chronology);
        int cmp = df1Unit.compareTo(df2Unit);
        if (cmp < 0) {
          return true;
        }
      }
    }
    return false;
  }

  private static String zeroPad(int value, int padding){
    StringBuilder b = new StringBuilder();
    b.append(value);
    while(b.length() < padding){
      b.insert(0,"0");
    }
    return b.toString();
  }

  private static boolean noFurtherFields(DateTimeFieldType smallestFieldSet, ReadableDateTime begin, ReadableDateTime end){
    //--Get Indices
    //(standard fields)
    int indexInStandard = -1;
    for(int i=0; i= 0){
      index = indexInStandard;
      toCheck = standardISOFields;
    } else if(indexInWeek >= 0){
        index = indexInWeek;
        toCheck = standardISOWeekFields;
    } else {
      throw new IllegalArgumentException("Field is not in my list of fields: " + smallestFieldSet);
    }
    //--Perform Check
    for(int i=index; i 100000){
      return "FUTURE_REF";
    } else if(begin.equals(end)){
      return timexTimeValue(begin);
    }
    StringBuilder value = new StringBuilder();
    boolean shouldBeDone = false;
    //--Differences
    int monthDiff = (end.getMonthOfYear() - begin.getMonthOfYear()) + (end.getYear()-begin.getYear())*12;
    int weekDiff = end.getWeekOfWeekyear()-begin.getWeekOfWeekyear() + (end.getYear()-begin.getYear())*maximumValue(weekOfWeekyear(),begin);
    int dayDiff = end.getDayOfMonth()-begin.getDayOfMonth() + monthDiff*maximumValue(dayOfMonth(),begin);
    int hrDiff = end.getHourOfDay()-begin.getHourOfDay() + dayDiff*24;
    int minDiff = end.getMinuteOfHour()-begin.getMinuteOfHour() + hrDiff*60;
    int secDiff = end.getSecondOfMinute()-begin.getSecondOfMinute() + minDiff*60;
    //--Years
    if(noFurtherFields(year(), begin, end)){
      int diff = end.getYear()-begin.getYear();
      if(diff == 100 && (opts.forceDate || begin.getYear() % 100 == 0)){
        //(case: century)
        value.append(( begin.getYear() / 100)).append("XX");
      } else if(diff == 10 && (opts.forceDate || begin.getYear() % 10 == 0)){
        //(case: decade)
        value.append(( begin.getYear() / 10));
      } else if(diff == 1 || opts.forceDate){
        //(case: year)
        value.append(begin.getYear());
      } else {
        //(case: duration)
        return timexDurationValue(begin,end);
      }
      return value.toString();
    } else if(monthDiff < 12 || opts.forceDate) {
      //(case: year and more)
      value.append(begin.getYear());
    } else {
      //(case: treat as duration)
      return timexDurationValue(begin, end);
    }
    //--Week/Month/Quarters
    value.append("-");
    if(noFurtherFields(monthOfYear(), begin, end) || noFurtherFields(weekOfWeekyear(), begin, end)){
      boolean monthTerminal = noFurtherFields(monthOfYear(), begin, end);
      boolean weekTerminal = noFurtherFields(weekOfWeekyear(), begin, end);
      //(Month/Quarter)
      if(monthTerminal && monthDiff == 6 && (begin.getMonthOfYear()-1) % 6 == 0){
        //(case: half of year)
        value.append("H").append(( begin.getMonthOfYear()-1) / 6 + 1);
      } else if(monthTerminal && monthDiff == 3 && (begin.getMonthOfYear()-1) % 3 == 0){
        //(case: quarter of year)
        value.append("Q").append(( begin.getMonthOfYear()-1) / 3 + 1);
      } else if(monthTerminal && monthDiff == 3 && begin.getMonthOfYear() % 3 == 0){
        //(case: season)
        switch( begin.getMonthOfYear() ){
          case 12:
            value.append("WI");
            break;
          case 3:
            value.append("SP");
            break;
          case 6:
            value.append("SU");
            break;
          case 9:
            value.append("FA");
            break;
          default:
            throw new IllegalStateException("Season start month is unknown");
        }
      } else if(weekTerminal && weekDiff == 1) {
        //(case: a week)
        value.append("W").append(zeroPad(begin.getWeekOfWeekyear(), 2));
      } else if(monthTerminal && monthDiff == 1 && weekDiff != 1 || opts.forceDate) {
        //(case: a month)
        value.append( zeroPad(begin.getMonthOfYear(),2) );
      } else {
        //(case: treat as duration)
        return timexDurationValue(begin, end);
      }
      return value.toString();
    } else if(noFurtherFields(dayOfWeek(), begin, end) && dayDiff == 2 && begin.getDayOfWeek() == 6){
      //(case: a weekend)
      value.append("W").append(zeroPad(begin.getWeekOfWeekyear(),2)).append("-WE");
      return value.toString();
    } else if(dayDiff < maximumValue(dayOfMonth(),begin) || opts.forceDate) {
      //(case: month and more)
      value.append(zeroPad(begin.getMonthOfYear(),2));
    } else {
      //(case: treat as duration)
      return timexDurationValue(begin, end);
    }
    //--Weekday/Day
    value.append("-");
    if(noFurtherFields(dayOfMonth(), begin, end)){
      if(dayDiff == 1 || opts.forceDate){
        //(case: a day)
        value.append(zeroPad(begin.getDayOfMonth(),2));
      } else {
        //(case: treat as duration)
        return timexDurationValue(begin, end);
      }
      return value.toString();
    } else if(hrDiff < 24 || opts.forceDate){
      //(case: day and more)
      value.append(zeroPad(begin.getDayOfMonth(),2));
    } else {
      //(case: treat as duration)
      return timexDurationValue(begin, end);
    }
    //--Hour/TimeOfDay
    value.append("T");
    if(noFurtherFields(hourOfDay(),begin,end)){
      //((case: half day)
      if(hrDiff == 12 && begin.getHourOfDay() == 0){
        value.append("H1");
      } else if(hrDiff == 12 && begin.getHourOfDay() == 12){
        value.append("H2");
      //(case: time of day)
      }else if(hrDiff == 4 && begin.getHourOfDay() == 8){
        value.append("MO");
      }else if(hrDiff == 4 && begin.getHourOfDay() == 12){
        value.append("AF");
      }else if(hrDiff == 4 && begin.getHourOfDay() == 16){
        value.append("EV");
      }else if(hrDiff == 4 && begin.getHourOfDay() == 20){
        value.append("NI");
      } else if(hrDiff == 1 || opts.forceDate){
        //(case: an hour)
        value.append(zeroPad(begin.getHourOfDay()+1,2));
      } else {
        //(case: treat as duration)
        return timexDurationValue(begin,end);
      }
      return value.toString();
    } else if(minDiff <= 60 || opts.forceDate){
      //(case: hour and more)
      value.append(zeroPad(begin.getHourOfDay(),2));
    } else {
      //(case: treat as duration)
      return timexDurationValue(begin, end);
    }
    //--Minute/Second
    value.append(":");
    value.append(zeroPad(begin.getMinuteOfHour(),2));
    return value.toString();
  }

  private static boolean consistentWithForced(String cand, String[] forcedList){
    //--Check If Forced
    for(String forced : forcedList){
      if(forced.equals(cand)){ return true; }
    }
    //--Get Ordering
    String[] ordering = {"L","C","E","Y","Q","M","W","D","H","m","S"};
    int candIndex = -1;
    for(int i=0; i= 0;
    //--Check If Lower Priority Forced
    for(int candI=candIndex+1; candI < ordering.length; candI++){
      for(String forced : forcedList){
        if(ordering[candI].equals(forced)){
          return false;
        }
      }
    }
    //--OK
    return true;
  }

  /**
   * Return the TIMEX string for the duration represented by the given period; approximately if
   * approximate is set to true.
   * @param duration The JodaTime period representing this duration
   * @param opts Options for the conversion (e.g., mark duration as approximates)
   * @return The string representation of a DURATION type Timex3 expression
   */
  public static String timexDurationValue(ReadablePeriod duration, ConversionOptions opts){
    StringBuilder b = new StringBuilder().append("P");
    boolean seenTime = false;
    int years = duration.get(years());
    //(millenia)
    if(years >= 1000 && consistentWithForced("L",opts.forceUnits)){
      b.append(opts.approximate ? "X" : years / 1000).append("L");
      years = years % 1000;
    }
    //(centuries)
    if(years >= 100 && consistentWithForced("C", opts.forceUnits)){
      b.append(opts.approximate ? "X" : years / 100).append("C");
      years = years % 100;
    }
    //(decades)
    if(years >= 10 && consistentWithForced("E", opts.forceUnits)){
      b.append(opts.approximate ? "X" : years / 10).append("E");
      years = years % 10;
    }
    //(years)
    if(years != 0 && consistentWithForced("Y", opts.forceUnits)){
      b.append(opts.approximate ? "X" : years).append("Y");
    }
    //(months)
    int months = duration.get(months());
    if(months != 0){
      if(months % 3 == 0 && consistentWithForced("Q", opts.forceUnits)){
        b.append(opts.approximate ? "X" : months / 3).append("Q");
        months = months % 3;
      } else {
        b.append(opts.approximate ? "X" : months).append("M");
      }
    }
    //(weeks)
    if(duration.get(weeks()) != 0){
      b.append(opts.approximate ? "X" : duration.get(weeks())).append("W");
    }
    //(days)
    if(duration.get(days()) != 0){
      b.append(opts.approximate ? "X" : duration.get(days())).append("D");
    }
    //(hours)
    if(duration.get(hours()) != 0){
      if(!seenTime){ b.append("T"); seenTime = true; }
      b.append(opts.approximate ? "X" : duration.get(hours())).append("H");
    }
    //(minutes)
    if(duration.get(minutes()) != 0){
      if(!seenTime){ b.append("T"); seenTime = true; }
      b.append(opts.approximate ? "X" : duration.get(minutes())).append("M");
    }
    //(seconds)
    if(duration.get(seconds()) != 0){
      if(!seenTime){ b.append("T"); seenTime = true; }
      b.append(opts.approximate ? "X" : duration.get(seconds())).append("S");
    }
    return b.toString();
  }
  public static String timexDurationValue(ReadablePeriod duration){ return timexDurationValue(duration, new ConversionOptions()); }

  /**
   * Return the TIMEX string for the difference between two dates
   * TODO not really sure if this works...
   */
  public static String timexDurationValue(ReadableDateTime begin, ReadableDateTime end){
    return timexDurationValue( new Period(end.getMillis()-begin.getMillis()) );
  }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy