Package edu.stanford.nlp.time

Source Code of edu.stanford.nlp.time.JodaTimeUtils$ConversionOptions

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
  private static final Chronology isoUTCChronology = ISOChronology.getInstanceUTC();
  private static final DateTimeFieldType[] standardISOFields = {
          DateTimeFieldType.year(),
          DateTimeFieldType.monthOfYear(),
          DateTimeFieldType.dayOfMonth(),
          DateTimeFieldType.hourOfDay(),
          DateTimeFieldType.minuteOfHour(),
          DateTimeFieldType.secondOfMinute(),
          DateTimeFieldType.millisOfSecond()
  };
  private static final DateTimeFieldType[] standardISOWeekFields = {
          DateTimeFieldType.year(),
          DateTimeFieldType.weekOfWeekyear(),
          DateTimeFieldType.dayOfWeek(),
          DateTimeFieldType.hourOfDay(),
          DateTimeFieldType.minuteOfHour(),
          DateTimeFieldType.secondOfMinute(),
          DateTimeFieldType.millisOfSecond()
  };
  private static final DateTimeFieldType[] standardISODateFields = {
          DateTimeFieldType.year(),
          DateTimeFieldType.monthOfYear(),
          DateTimeFieldType.dayOfMonth(),
  };
  private 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<DurationFieldType> getSupportedDurationFields(Partial p)
  {
    Set<DurationFieldType> 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<DurationFieldType> 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)) {
          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;
  }
  // 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())) {
          Instant t2 = getInstant(p);
          DateTime t1 = p.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<standardISOFields.length; i++){
      if(standardISOFields[i] == smallestFieldSet){
        indexInStandard = i+1;
      }
    }
    //(week-based fields)
    int indexInWeek = -1;
    for(int i=0; i<standardISOWeekFields.length; i++){
      if(standardISOWeekFields[i] == smallestFieldSet){
        indexInWeek = i+1;
      }
    }
    //(special fields)
    if(smallestFieldSet == QuarterOfYear){
      for(int i=0; i<standardISOFields.length; i++){
        if(standardISOFields[i] == monthOfYear()){
          indexInStandard = i;
        }
      }
    }
    //(get data)
    int index = -1;
    DateTimeFieldType[] toCheck = null;
    if(indexInStandard >= 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<toCheck.length; i++){
      int minValue = minimumValue(toCheck[i], begin) ;
      if(begin.get(toCheck[i]) != minValue || end.get(toCheck[i]) != minValue){
        return false;
      }
    }
    return true;
  }

  /**
   * Return the minimum value of a field, closest to the reference time
   */
  public static int minimumValue(DateTimeFieldType type, ReadableDateTime reference){
    return reference.toDateTime().property(type).getMinimumValue();
  }
  /**
   * Return the maximum value of a field, closest to the reference time
   */
  public static int maximumValue(DateTimeFieldType type, ReadableDateTime reference){
    return reference.toDateTime().property(type).getMaximumValue();
  }

  /**
   * Return the TIMEX string for the time given
   */
  public static String timexTimeValue(ReadableDateTime time){
    return String.valueOf(time.getYear()) + '-' + zeroPad(time.getMonthOfYear(), 2) + '-' + zeroPad(time.getDayOfMonth(), 2) + 'T' + zeroPad(time.getHourOfDay(), 2) + ':' + zeroPad(time.getMinuteOfHour(), 2);
  }

  public static class ConversionOptions{
    /**
     * If true, give a "best guess" of the right date; if false, backoff to giving a duration
     * for malformed dates.
     */
    public boolean forceDate = false;
    /**
     * Force particular units -- e.g., force 20Y to be 20Y (20 years) rather than 2E (2 decades)
     */
    public String[] forceUnits = new String[0];
    /**
     * Treat durations as approximate
     */
    public boolean approximate = false;
  }

  public static String timexDateValue(ReadableDateTime begin, ReadableDateTime end){
    return timexDateValue(begin,end,new ConversionOptions());
  }

  /**
   * Return the TIMEX string for the range of dates given.
   * For example, (2011-12-3:00:00,000 to 2011-12-4:00:00.000) would give 2011-12-3.
   * @param begin The begin time for the timex
   * @param end The end time for the timex
   * @param opts Tweaks in the heuristic conversion
   * @return The string representation of a DATE type Timex3 expression
   */
  public static String timexDateValue(ReadableDateTime begin, ReadableDateTime end, ConversionOptions opts){
    //--Special Cases
    if( begin.getYear() < -100000){
      return "PAST_REF";
    } else if(end.getYear() > 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<ordering.length; i++){
      if(ordering[i].equals(cand)){
        candIndex = i;
        break;
      }
    }
    assert candIndex >= 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()) );
  }

}
TOP

Related Classes of edu.stanford.nlp.time.JodaTimeUtils$ConversionOptions

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.