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

edu.stanford.nlp.time.SUTime 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 edu.stanford.nlp.ling.tokensregex.types.Expressions;
import edu.stanford.nlp.util.CollectionUtils;
import edu.stanford.nlp.util.FuzzyInterval;
import edu.stanford.nlp.util.Generics;
import edu.stanford.nlp.util.HasInterval;
import edu.stanford.nlp.util.HashIndex;
import edu.stanford.nlp.util.Index;
import edu.stanford.nlp.util.Interval;
import edu.stanford.nlp.util.Pair;
import edu.stanford.nlp.util.StringUtils;
// import edu.stanford.nlp.util.logging.Redwood;

import org.joda.time.*;
import org.joda.time.format.DateTimeFormat;
import org.joda.time.format.DateTimeFormatter;
import org.joda.time.format.DateTimeFormatterBuilder;

import java.io.Serializable;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * SUTime is a collection of data structures to represent various temporal
 * concepts and operations between them.
 *
 * Different types of time expressions:
 * 
    *
  • Time - A time point on a time scale In most cases, we only know partial information * (with a certain granularity) about a point in time (8:00pm)
  • *
  • Duration - A length of time (3 days)
  • *
  • Interval - A range of time with start and end points
  • *
  • Set - A set of time: Can be periodic (Friday every week) or union (Thursday or Friday)
  • *
* *

* Use {@link TimeAnnotator} to annotate documents within an Annotation pipeline such as CoreNLP. * Use {@link SUTimeMain} for standalone testing. * * @author Angel Chang */ public class SUTime { /** A logger for this class */ // private static Redwood.RedwoodChannels log = Redwood.channels(SUTime.class); // TODO: // 1. Decrease dependency on JodaTime... // 2. Number parsing // - Improve Number detection/normalization // - Handle four-years, one thousand two hundred and sixty years // - Currently custom word to number combo - integrate with Number classifier, // QuantifiableEntityNormalizer // - Stop repeated conversions of word to numbers // 3. Durations // - Underspecified durations // 4. Date Time // - Patterns // -- 1st/last week(end) of blah blah // -- Don't treat all 3 to 5 as times // - Holidays // - Too many classes - reduce number of classes // 5. Nest time expressions // - Before annotating: Can remove nested time expressions // - After annotating: types to combine time expressions // 6. Set of times (Timex3 standard is weird, timex2 makes more sense?) // - freq, quant // 7. Ground with respect to reference time - figure out what is reference // time to use for what // - news... things happen in the past, so favor resolving to past? // - Use heuristics from GUTime to figure out direction to resolve to // - tids for anchor times...., valueFromFunctions for resolved relative times // (option to keep some nested times)? // 8. Composite time patterns // - Composite time operators // 9. Ranges // - comparing times (before, after, ... // - intersect, mid, resolving // - specify clear start/end for range (sonal) // 10. Clean up formatting // 11. ISO/Timex3/Custom // 12. Keep modifiers // 13. Handle mid- (token not separated) // 14. future, plurals // 15. Resolve to future.... with year specified.... // 16. Check recursive calls // 17. Add TimeWithFields (that doesn't use jodatime and is only field based? private SUTime() { } public enum TimexType { DATE, TIME, DURATION, SET } public enum TimexMod { BEFORE("<"), AFTER(">"), ON_OR_BEFORE("<="), ON_OR_AFTER("<="), LESS_THAN("<"), MORE_THAN(">"), EQUAL_OR_LESS("<="), EQUAL_OR_MORE(">="), START, MID, END, APPROX("~"), EARLY /* GUTIME */, LATE; /* GUTIME */ private String symbol; TimexMod() { } TimexMod(String symbol) { this.symbol = symbol; } public String getSymbol() { return symbol; } } public enum TimexDocFunc { CREATION_TIME, EXPIRATION_TIME, MODIFICATION_TIME, PUBLICATION_TIME, RELEASE_TIME, RECEPTION_TIME, NONE } public enum TimexAttr { type, value, tid, beginPoint, endPoint, quant, freq, mod, anchorTimeID, comment, valueFromFunction, temporalFunction, functionInDocument } public static final String PAD_FIELD_UNKNOWN = "X"; public static final String PAD_FIELD_UNKNOWN2 = "XX"; public static final String PAD_FIELD_UNKNOWN4 = "XXXX"; // Flags for how to resolve a time expression public static final int RESOLVE_NOW = 0x01; public static final int RESOLVE_TO_THIS = 0x20; public static final int RESOLVE_TO_PAST = 0x40; // Resolve to a past time public static final int RESOLVE_TO_FUTURE = 0x80; // Resolve to a future time public static final int RESOLVE_TO_CLOSEST = 0x200; // Resolve to closest time public static final int DUR_RESOLVE_TO_AS_REF = 0x1000; public static final int DUR_RESOLVE_FROM_AS_REF = 0x2000; public static final int RANGE_RESOLVE_TIME_REF = 0x100000; public static final int RELATIVE_OFFSET_INEXACT = 0x0100; public static final int RANGE_OFFSET_BEGIN = 0x0001; public static final int RANGE_OFFSET_END = 0x0002; public static final int RANGE_EXPAND_FIX_BEGIN = 0x0010; public static final int RANGE_EXPAND_FIX_END = 0x0020; /** Flags for how to pad when converting times into ranges */ public static final int RANGE_FLAGS_PAD_MASK = 0x000f; // Pad type /** Simple range (without padding) */ public static final int RANGE_FLAGS_PAD_NONE = 0x0001; /** Automatic range (whatever padding we think is most appropriate, default) */ public static final int RANGE_FLAGS_PAD_AUTO = 0x0002; /** Pad to most specific (whatever that is) */ public static final int RANGE_FLAGS_PAD_FINEST = 0x0003; /** Pad to specified granularity */ public static final int RANGE_FLAGS_PAD_SPECIFIED = 0x0004; public static final int FORMAT_ISO = 0x01; public static final int FORMAT_TIMEX3_VALUE = 0x02; public static final int FORMAT_FULL = 0x04; public static final int FORMAT_PAD_UNKNOWN = 0x1000; protected static final int timexVersion = 3; public static SUTime.Time getCurrentTime() { return new GroundedTime(new DateTime()); } // Index of time id to temporal object public static class TimeIndex { Index temporalExprIndex = new HashIndex<>(); Index temporalIndex = new HashIndex<>(); Index temporalFuncIndex = new HashIndex<>(); SUTime.Time docDate; public TimeIndex() { addTemporal(SUTime.TIME_REF); } public void clear() { temporalExprIndex.clear(); temporalIndex.clear(); temporalFuncIndex.clear(); // t0 is the document date (reserve) temporalExprIndex.add(null); addTemporal(SUTime.TIME_REF); } public int getNumberOfTemporals() { return temporalIndex.size(); } public int getNumberOfTemporalExprs() { return temporalExprIndex.size(); } public int getNumberOfTemporalFuncs() { return temporalFuncIndex.size(); } private static final Pattern ID_PATTERN = Pattern.compile("([a-zA-Z]*)(\\d+)"); public TimeExpression getTemporalExpr(String s) { Matcher m = ID_PATTERN.matcher(s); if (m.matches()) { String prefix = m.group(1); int id = Integer.parseInt(m.group(2)); if ("t".equals(prefix) || prefix.isEmpty()) { return temporalExprIndex.get(id); } } return null; } public Temporal getTemporal(String s) { Matcher m = ID_PATTERN.matcher(s); if (m.matches()) { String prefix = m.group(1); int id = Integer.parseInt(m.group(2)); if ("t".equals(prefix)) { TimeExpression te = temporalExprIndex.get(id); return (te != null)? te.getTemporal(): null; } else if (prefix.isEmpty()) { return temporalIndex.get(id); } } return null; } public TimeExpression getTemporalExpr(int i) { return temporalExprIndex.get(i); } public Temporal getTemporal(int i) { return temporalIndex.get(i); } public Temporal getTemporalFunc(int i) { return temporalFuncIndex.get(i); } public boolean addTemporalExpr(TimeExpression t) { Temporal temp = t.getTemporal(); if (temp != null) { addTemporal(temp); } return temporalExprIndex.add(t); } public boolean addTemporal(Temporal t) { return temporalIndex.add(t); } public boolean addTemporalFunc(Temporal t) { return temporalFuncIndex.add(t); } public int addToIndexTemporalExpr(TimeExpression t) { return temporalExprIndex.addToIndex(t); } public int addToIndexTemporal(Temporal t) { return temporalIndex.addToIndex(t); } public int addToIndexTemporalFunc(Temporal t) { return temporalFuncIndex.addToIndex(t); } } /** * Basic temporal object. * *

* There are 4 main types of temporal objects *

    *
  1. Time - Conceptually a point in time *
    NOTE: Due to limitation in precision, it is * difficult to get an exact point in time *
  2. *
  3. Duration - Amount of time in a time interval *
    • DurationWithMillis - Duration specified in milliseconds * (wrapper around JodaTime Duration)
    • *
    • DurationWithFields - Duration specified with * fields like day, year, etc (wrapper around JodaTime Period)
    • *
    • DurationRange - A duration that falls in a particular range (with min to max)
    • *
    *
  4. *
  5. Range - Time Interval with a start time, end time, and duration
  6. *
  7. TemporalSet - A set of temporal objects *
    • ExplicitTemporalSet - Explicit set of temporals (not used) *
      Ex: Tuesday 1-2pm, Wednesday night
    • *
    • PeriodicTemporalSet - Reoccurring times *
      Ex: Every Tuesday
    • *
    *
  8. *
*/ public abstract static class Temporal implements Cloneable, Serializable { public String mod; public boolean approx; StandardTemporalType standardTemporalType; public String timeLabel; // Duration after which the time is uncertain (what is there is an estimate) public Duration uncertaintyGranularity; public Temporal() { } public Temporal(Temporal t) { this.mod = t.mod; this.approx = t.approx; this.uncertaintyGranularity = t.uncertaintyGranularity; // this.standardTimeType = t.standardTimeType; // this.timeLabel = t.timeLabel; } public abstract boolean isGrounded(); // Returns time representation for Temporal (if available) public abstract Time getTime(); // Returns duration (estimate of how long the temporal expression is for) public abstract Duration getDuration(); // Returns range (start/end points of temporal, automatic granularity) public Range getRange() { return getRange(RANGE_FLAGS_PAD_AUTO); } // Returns range (start/end points of temporal) public Range getRange(int flags) { return getRange(flags, null); } // Returns range (start/end points of temporal), using specified flags public abstract Range getRange(int flags, Duration granularity); // Returns how often this time would repeat // Ex: friday repeat weekly, hour repeat hourly, hour in a day repeat daily public Duration getPeriod() { /* TimeLabel tl = getTimeLabel(); if (tl != null) { return tl.getPeriod(); } */ StandardTemporalType tlt = getStandardTemporalType(); if (tlt != null) { return tlt.getPeriod(); } return null; } // Returns the granularity to which this time or duration is specified // Typically the most specific time unit public Duration getGranularity() { StandardTemporalType tlt = getStandardTemporalType(); if (tlt != null) { return tlt.getGranularity(); } return null; } public Duration getUncertaintyGranularity() { if (uncertaintyGranularity != null) return uncertaintyGranularity; return getGranularity(); } // Resolves this temporal expression with respect to the specified reference // time using flags public Temporal resolve(Time refTime) { return resolve(refTime, 0); } public abstract Temporal resolve(Time refTime, int flags); public StandardTemporalType getStandardTemporalType() { return standardTemporalType; } // Returns if the current temporal expression is an reference public boolean isRef() { return false; } // Return sif the current temporal expression is approximate public boolean isApprox() { return approx; } // TIMEX related functions public int getTid(TimeIndex timeIndex) { return timeIndex.addToIndexTemporal(this); } public String getTidString(TimeIndex timeIndex) { return "t" + getTid(timeIndex); } public int getTfid(TimeIndex timeIndex) { return timeIndex.addToIndexTemporalFunc(this); } public String getTfidString(TimeIndex timeIndex) { return "tf" + getTfid(timeIndex); } // Returns attributes to convert this temporal expression into timex object public boolean includeTimexAltValue() { return false; } public Map getTimexAttributes(TimeIndex timeIndex) { Map map = new LinkedHashMap<>(); map.put(TimexAttr.tid.name(), getTidString(timeIndex)); // NOTE: GUTime used "VAL" instead of TIMEX3 standard "value" // NOTE: attributes are case sensitive, GUTIME used mostly upper case // attributes.... String val = getTimexValue(); if (val != null) { map.put(TimexAttr.value.name(), val); } if (val == null || includeTimexAltValue()) { String str = toFormattedString(FORMAT_FULL); if (str != null) { map.put("alt_value", str); } } /* Range r = getRange(); if (r != null) map.put("range", r.toString()); */ /* map.put("str", toString()); */ map.put(TimexAttr.type.name(), getTimexType().name()); if (mod != null) { map.put(TimexAttr.mod.name(), mod); } return map; } // Returns the timex type public TimexType getTimexType() { if (getStandardTemporalType() != null) { return getStandardTemporalType().getTimexType(); } else { return null; } } // Returns timex value (by default it is the ISO string representation of // this object) public String getTimexValue() { return toFormattedString(FORMAT_TIMEX3_VALUE); } public String toISOString() { return toFormattedString(FORMAT_ISO); } public String toString() { // TODO: Full string representation return toFormattedString(FORMAT_FULL); } public String getTimeLabel() { return timeLabel; } public String toFormattedString(int flags) { return getTimeLabel(); } // Temporal operations... public static Temporal setTimeZone(Temporal t, DateTimeZone tz) { if (t == null) return null; return t.setTimeZone(tz); } public Temporal setTimeZone(DateTimeZone tz) { return this; } public Temporal setTimeZone(int offsetHours) { return setTimeZone(DateTimeZone.forOffsetHours(offsetHours)); } // public abstract Temporal add(Duration offset); public Temporal next() { Duration per = getPeriod(); if (per != null) { if (this instanceof Duration) { return new RelativeTime(new RelativeTime(TemporalOp.THIS, this, DUR_RESOLVE_TO_AS_REF), TemporalOp.OFFSET, per); } else { // return new RelativeTime(new RelativeTime(TemporalOp.THIS, this), // TemporalOp.OFFSET, per); return TemporalOp.OFFSET.apply(this, per); } } return null; } public Temporal prev() { Duration per = getPeriod(); if (per != null) { if (this instanceof Duration) { return new RelativeTime(new RelativeTime(TemporalOp.THIS, this, DUR_RESOLVE_FROM_AS_REF), TemporalOp.OFFSET, per.multiplyBy(-1)); } else { // return new RelativeTime(new RelativeTime(TemporalOp.THIS, this), // TemporalOp.OFFSET, per.multiplyBy(-1)); return TemporalOp.OFFSET.apply(this, per.multiplyBy(-1)); } } return null; } public/* abstract*/Temporal intersect(Temporal t) { return null; } public String getMod() { return mod; } /* public void setMod(String mod) { this.mod = mod; } */ public Temporal addMod(String mod) { try { Temporal t = (Temporal) this.clone(); t.mod = mod; return t; } catch (CloneNotSupportedException ex) { throw new RuntimeException(ex); } } public Temporal addModApprox(String mod, boolean approx) { try { Temporal t = (Temporal) this.clone(); t.mod = mod; t.approx = approx; return t; } catch (CloneNotSupportedException ex) { throw new RuntimeException(ex); } } private static final long serialVersionUID = 1; } public static T createTemporal(StandardTemporalType timeType, T temporal) { temporal.standardTemporalType = timeType; return temporal; } public static T createTemporal(StandardTemporalType timeType, String label, T temporal) { temporal.standardTemporalType = timeType; temporal.timeLabel = label; return temporal; } public static T createTemporal(StandardTemporalType timeType, String label, String mod, T temporal) { temporal.standardTemporalType = timeType; temporal.timeLabel = label; temporal.mod = mod; return temporal; } // Basic time units (durations) public static final Duration YEAR = new DurationWithFields(Period.years(1)) { @Override public DateTimeFieldType[] getDateTimeFields() { return new DateTimeFieldType[] { DateTimeFieldType.year(), DateTimeFieldType.yearOfCentury(), DateTimeFieldType.yearOfEra() }; } private static final long serialVersionUID = 1; }; public static final Duration DAY = new DurationWithFields(Period.days(1)) { @Override public DateTimeFieldType[] getDateTimeFields() { return new DateTimeFieldType[] { DateTimeFieldType.dayOfMonth(), DateTimeFieldType.dayOfWeek(), DateTimeFieldType.dayOfYear() }; } private static final long serialVersionUID = 1; }; public static final Duration WEEK = new DurationWithFields(Period.weeks(1)) { @Override public DateTimeFieldType[] getDateTimeFields() { return new DateTimeFieldType[] { DateTimeFieldType.weekOfWeekyear() }; } private static final long serialVersionUID = 1; }; public static final Duration FORTNIGHT = new DurationWithFields(Period.weeks(2)); public static final Duration MONTH = new DurationWithFields(Period.months(1)) { @Override public DateTimeFieldType[] getDateTimeFields() { return new DateTimeFieldType[] { DateTimeFieldType.monthOfYear() }; } private static final long serialVersionUID = 1; }; // public static final Duration QUARTER = new DurationWithFields(new // Period(JodaTimeUtils.Quarters)) { public static final Duration QUARTER = new DurationWithFields(Period.months(3)) { @Override public DateTimeFieldType[] getDateTimeFields() { return new DateTimeFieldType[] { JodaTimeUtils.QuarterOfYear }; } private static final long serialVersionUID = 1; }; public static final Duration HALFYEAR = new DurationWithFields(Period.months(6)) { @Override public DateTimeFieldType[] getDateTimeFields() { return new DateTimeFieldType[] { JodaTimeUtils.HalfYearOfYear }; } private static final long serialVersionUID = 1; }; public static final Duration MILLIS = new DurationWithFields(Period.millis(1)) { @Override public DateTimeFieldType[] getDateTimeFields() { return new DateTimeFieldType[] { DateTimeFieldType.millisOfSecond(), DateTimeFieldType.millisOfDay() }; } private static final long serialVersionUID = 1; }; public static final Duration SECOND = new DurationWithFields(Period.seconds(1)) { @Override public DateTimeFieldType[] getDateTimeFields() { return new DateTimeFieldType[] { DateTimeFieldType.secondOfMinute(), DateTimeFieldType.secondOfDay() }; } private static final long serialVersionUID = 1; }; public static final Duration MINUTE = new DurationWithFields(Period.minutes(1)) { @Override public DateTimeFieldType[] getDateTimeFields() { return new DateTimeFieldType[] { DateTimeFieldType.minuteOfHour(), DateTimeFieldType.minuteOfDay() }; } private static final long serialVersionUID = 1; }; public static final Duration HOUR = new DurationWithFields(Period.hours(1)) { @Override public DateTimeFieldType[] getDateTimeFields() { return new DateTimeFieldType[] { DateTimeFieldType.hourOfDay(), DateTimeFieldType.hourOfHalfday() }; } private static final long serialVersionUID = 1; }; public static final Duration HALFHOUR = new DurationWithFields(Period.minutes(30)); public static final Duration QUARTERHOUR = new DurationWithFields(Period.minutes(15)); public static final Duration DECADE = new DurationWithFields(Period.years(10)) { @Override public DateTimeFieldType[] getDateTimeFields() { return new DateTimeFieldType[] { JodaTimeUtils.DecadeOfCentury }; } private static final long serialVersionUID = 1; }; public static final Duration CENTURY = new DurationWithFields(Period.years(100)) { @Override public DateTimeFieldType[] getDateTimeFields() { return new DateTimeFieldType[] { DateTimeFieldType.centuryOfEra() }; } private static final long serialVersionUID = 1; }; public static final Duration MILLENNIUM = new DurationWithFields(Period.years(1000)); public static final Time TIME_REF = new RefTime("REF") { private static final long serialVersionUID = 1; }; public static final Time TIME_REF_UNKNOWN = new RefTime("UNKNOWN"); public static final Time TIME_UNKNOWN = new SimpleTime("UNKNOWN"); public static final Time TIME_NONE = null; // No time public static final Time TIME_NONE_OK = new SimpleTime("NOTIME"); // The special time of now public static final Time TIME_NOW = new RefTime(StandardTemporalType.REFTIME, "PRESENT_REF", "NOW"); public static final Time TIME_PRESENT = createTemporal(StandardTemporalType.REFDATE, "PRESENT_REF", new InexactTime(new Range(TIME_NOW, TIME_NOW))); public static final Time TIME_PAST = createTemporal(StandardTemporalType.REFDATE, "PAST_REF",new InexactTime(new Range(TIME_UNKNOWN, TIME_NOW))); public static final Time TIME_FUTURE = createTemporal(StandardTemporalType.REFDATE, "FUTURE_REF", new InexactTime(new Range(TIME_NOW, TIME_UNKNOWN))); public static final Duration DURATION_UNKNOWN = new DurationWithFields(); public static final Duration DURATION_NONE = new DurationWithFields(Period.ZERO); // Basic dates/times // Day of week // Use constructors rather than calls to // StandardTemporalType.createTemporal because sometimes the class // loader seems to load objects in an incorrect order, resulting in // an exception. This is especially evident when deserializing public static final Time MONDAY = new PartialTime(StandardTemporalType.DAY_OF_WEEK, new Partial(DateTimeFieldType.dayOfWeek(), 1)); public static final Time TUESDAY = new PartialTime(StandardTemporalType.DAY_OF_WEEK, new Partial(DateTimeFieldType.dayOfWeek(), 2)); public static final Time WEDNESDAY = new PartialTime(StandardTemporalType.DAY_OF_WEEK, new Partial(DateTimeFieldType.dayOfWeek(), 3)); public static final Time THURSDAY = new PartialTime(StandardTemporalType.DAY_OF_WEEK, new Partial(DateTimeFieldType.dayOfWeek(), 4)); public static final Time FRIDAY = new PartialTime(StandardTemporalType.DAY_OF_WEEK, new Partial(DateTimeFieldType.dayOfWeek(), 5)); public static final Time SATURDAY = new PartialTime(StandardTemporalType.DAY_OF_WEEK, new Partial(DateTimeFieldType.dayOfWeek(), 6)); public static final Time SUNDAY = new PartialTime(StandardTemporalType.DAY_OF_WEEK, new Partial(DateTimeFieldType.dayOfWeek(), 7)); public static final Time WEEKDAY = createTemporal(StandardTemporalType.DAYS_OF_WEEK, "WD", new InexactTime(null, SUTime.DAY, new SUTime.Range(SUTime.MONDAY, SUTime.FRIDAY)) { @Override public Duration getDuration() { return SUTime.DAY; } private static final long serialVersionUID = 1; }); public static final Time WEEKEND = createTemporal(StandardTemporalType.DAYS_OF_WEEK, "WE", new TimeWithRange(new SUTime.Range(SUTime.SATURDAY, SUTime.SUNDAY, SUTime.DAY.multiplyBy(2)))); // Months // Use constructors rather than calls to // StandardTemporalType.createTemporal because sometimes the class // loader seems to load objects in an incorrect order, resulting in // an exception. This is especially evident when deserializing public static final Time JANUARY = new IsoDate(StandardTemporalType.MONTH_OF_YEAR, -1, 1, -1); public static final Time FEBRUARY = new IsoDate(StandardTemporalType.MONTH_OF_YEAR, -1, 2, -1); public static final Time MARCH = new IsoDate(StandardTemporalType.MONTH_OF_YEAR, -1, 3, -1); public static final Time APRIL = new IsoDate(StandardTemporalType.MONTH_OF_YEAR, -1, 4, -1); public static final Time MAY = new IsoDate(StandardTemporalType.MONTH_OF_YEAR, -1, 5, -1); public static final Time JUNE = new IsoDate(StandardTemporalType.MONTH_OF_YEAR, -1, 6, -1); public static final Time JULY = new IsoDate(StandardTemporalType.MONTH_OF_YEAR, -1, 7, -1); public static final Time AUGUST = new IsoDate(StandardTemporalType.MONTH_OF_YEAR, -1, 8, -1); public static final Time SEPTEMBER = new IsoDate(StandardTemporalType.MONTH_OF_YEAR, -1, 9, -1); public static final Time OCTOBER = new IsoDate(StandardTemporalType.MONTH_OF_YEAR, -1, 10, -1); public static final Time NOVEMBER = new IsoDate(StandardTemporalType.MONTH_OF_YEAR, -1, 11, -1); public static final Time DECEMBER = new IsoDate(StandardTemporalType.MONTH_OF_YEAR, -1, 12, -1); // Dates are rough with respect to northern hemisphere (actual // solstice/equinox days depend on the year) public static final Time SPRING_EQUINOX = createTemporal(StandardTemporalType.DAY_OF_YEAR, "SP", new SUTime.InexactTime(new SUTime.Range(new IsoDate(-1, 3, 20), new IsoDate(-1, 3, 21)))); public static final Time SUMMER_SOLSTICE = createTemporal(StandardTemporalType.DAY_OF_YEAR, "SU", new SUTime.InexactTime(new SUTime.Range(new IsoDate(-1, 6, 20), new IsoDate(-1, 6, 21)))); public static final Time WINTER_SOLSTICE = createTemporal(StandardTemporalType.DAY_OF_YEAR, "WI", new SUTime.InexactTime(new SUTime.Range(new IsoDate(-1, 12, 21), new IsoDate(-1, 12, 22)))); public static final Time FALL_EQUINOX = createTemporal(StandardTemporalType.DAY_OF_YEAR, "FA", new SUTime.InexactTime(new SUTime.Range(new IsoDate(-1, 9, 22), new IsoDate(-1, 9, 23)))); // Dates for seasons are rough with respect to northern hemisphere public static final Time SPRING = createTemporal(StandardTemporalType.SEASON_OF_YEAR, "SP", new SUTime.InexactTime(SPRING_EQUINOX, QUARTER, new SUTime.Range(SUTime.MARCH, SUTime.JUNE, SUTime.QUARTER))); public static final Time SUMMER = createTemporal(StandardTemporalType.SEASON_OF_YEAR, "SU", new SUTime.InexactTime(SUMMER_SOLSTICE, QUARTER, new SUTime.Range(SUTime.JUNE, SUTime.SEPTEMBER, SUTime.QUARTER))); public static final Time FALL = createTemporal(StandardTemporalType.SEASON_OF_YEAR, "FA", new SUTime.InexactTime(FALL_EQUINOX, QUARTER, new SUTime.Range(SUTime.SEPTEMBER, SUTime.DECEMBER, SUTime.QUARTER))); public static final Time WINTER = createTemporal(StandardTemporalType.SEASON_OF_YEAR, "WI", new SUTime.InexactTime(WINTER_SOLSTICE, QUARTER, new SUTime.Range(SUTime.DECEMBER, SUTime.MARCH, SUTime.QUARTER))); // Time of day public static final PartialTime NOON = createTemporal(StandardTemporalType.TIME_OF_DAY, "MI", new IsoTime(12, 0, -1)); public static final PartialTime MIDNIGHT = createTemporal(StandardTemporalType.TIME_OF_DAY, new IsoTime(0, 0, -1)); public static final Time MORNING = createTemporal(StandardTemporalType.TIME_OF_DAY, "MO", new InexactTime(new Range(new InexactTime(new Partial(DateTimeFieldType.hourOfDay(), 6)), NOON))); public static final Time AFTERNOON = createTemporal(StandardTemporalType.TIME_OF_DAY, "AF", new InexactTime(new Range(NOON, new InexactTime(new Partial(DateTimeFieldType.hourOfDay(), 18))))); public static final Time EVENING = createTemporal(StandardTemporalType.TIME_OF_DAY, "EV", new InexactTime(new Range(new InexactTime(new Partial(DateTimeFieldType.hourOfDay(), 18)), new InexactTime(new Partial(DateTimeFieldType .hourOfDay(), 20))))); public static final Time NIGHT = createTemporal(StandardTemporalType.TIME_OF_DAY, "NI", new InexactTime(MIDNIGHT, new Range(new InexactTime(new Partial(DateTimeFieldType.hourOfDay(), 14)), HOUR.multiplyBy(10)))); public static final Time SUNRISE = createTemporal(StandardTemporalType.TIME_OF_DAY, "MO", TimexMod.EARLY.name(), new PartialTime()); public static final Time SUNSET = createTemporal(StandardTemporalType.TIME_OF_DAY, "EV", TimexMod.EARLY.name(), new PartialTime()); public static final Time DAWN = createTemporal(StandardTemporalType.TIME_OF_DAY, "MO", TimexMod.EARLY.name(), new PartialTime()); public static final Time DUSK = createTemporal(StandardTemporalType.TIME_OF_DAY, "EV", new PartialTime()); public static final Time DAYTIME = createTemporal(StandardTemporalType.TIME_OF_DAY, "DT", new InexactTime(new Range(SUNRISE, SUNSET))); public static final Time LUNCHTIME = createTemporal(StandardTemporalType.TIME_OF_DAY, "MI", new InexactTime(new Range(new InexactTime(new Partial(DateTimeFieldType.hourOfDay(), 12)), new InexactTime(new Partial(DateTimeFieldType .hourOfDay(), 14))))); public static final Time TEATIME = createTemporal(StandardTemporalType.TIME_OF_DAY, "AF", new InexactTime(new Range(new InexactTime(new Partial(DateTimeFieldType.hourOfDay(), 15)), new InexactTime(new Partial(DateTimeFieldType .hourOfDay(), 17))))); public static final Time DINNERTIME = createTemporal(StandardTemporalType.TIME_OF_DAY, "EV", new InexactTime(new Range(new InexactTime(new Partial(DateTimeFieldType.hourOfDay(), 18)), new InexactTime(new Partial(DateTimeFieldType .hourOfDay(), 20))))); public static final Time MORNING_TWILIGHT = createTemporal(StandardTemporalType.TIME_OF_DAY, "MO", new InexactTime(new Range(DAWN, SUNRISE))); public static final Time EVENING_TWILIGHT = createTemporal(StandardTemporalType.TIME_OF_DAY, "EV", new InexactTime(new Range(SUNSET, DUSK))); public static final TemporalSet TWILIGHT = createTemporal(StandardTemporalType.TIME_OF_DAY, "NI", new ExplicitTemporalSet(EVENING_TWILIGHT, MORNING_TWILIGHT)); // Relative days public static final RelativeTime YESTERDAY = new RelativeTime(DAY.multiplyBy(-1)); public static final RelativeTime TOMORROW = new RelativeTime(DAY.multiplyBy(+1)); public static final RelativeTime TODAY = new RelativeTime(TemporalOp.THIS, SUTime.DAY); public static final RelativeTime TONIGHT = new RelativeTime(TemporalOp.THIS, SUTime.NIGHT); public enum TimeUnit { // Basic time units MILLIS(SUTime.MILLIS), SECOND(SUTime.SECOND), MINUTE(SUTime.MINUTE), HOUR(SUTime.HOUR), DAY(SUTime.DAY), WEEK(SUTime.WEEK), MONTH(SUTime.MONTH), QUARTER(SUTime.QUARTER), HALFYEAR(SUTime.HALFYEAR), YEAR(SUTime.YEAR), DECADE(SUTime.DECADE), CENTURY(SUTime.CENTURY), MILLENNIUM(SUTime.MILLENNIUM), UNKNOWN(SUTime.DURATION_UNKNOWN); protected Duration duration; TimeUnit(Duration d) { this.duration = d; } public Duration getDuration() { return duration; } // How long does this time last? public Duration getPeriod() { return duration; } // How often does this type of time occur? public Duration getGranularity() { return duration; } // What is the granularity of this time? public Temporal createTemporal(int n) { return duration.multiplyBy(n); } } public enum StandardTemporalType { REFDATE(TimexType.DATE), REFTIME(TimexType.TIME), /* MILLIS(TimexType.TIME, TimeUnit.MILLIS), SECOND(TimexType.TIME, TimeUnit.SECOND), MINUTE(TimexType.TIME, TimeUnit.MINUTE), HOUR(TimexType.TIME, TimeUnit.HOUR), DAY(TimexType.TIME, TimeUnit.DAY), WEEK(TimexType.TIME, TimeUnit.WEEK), MONTH(TimexType.TIME, TimeUnit.MONTH), QUARTER(TimexType.TIME, TimeUnit.QUARTER), YEAR(TimexType.TIME, TimeUnit.YEAR), */ TIME_OF_DAY(TimexType.TIME, TimeUnit.HOUR, SUTime.DAY) { @Override public Duration getDuration() { return SUTime.HOUR.makeInexact(); } }, DAY_OF_YEAR(TimexType.DATE, TimeUnit.DAY, SUTime.YEAR) { @Override protected Time _createTemporal(int n) { return new PartialTime(new Partial(DateTimeFieldType.dayOfYear(), n)); } }, DAY_OF_WEEK(TimexType.DATE, TimeUnit.DAY, SUTime.WEEK) { @Override protected Time _createTemporal(int n) { return new PartialTime(new Partial(DateTimeFieldType.dayOfWeek(), n)); } }, DAYS_OF_WEEK(TimexType.DATE, TimeUnit.DAY, SUTime.WEEK) { @Override public Duration getDuration() { return SUTime.DAY.makeInexact(); } }, WEEK_OF_YEAR(TimexType.DATE, TimeUnit.WEEK, SUTime.YEAR) { @Override protected Time _createTemporal(int n) { return new PartialTime(new Partial(DateTimeFieldType.weekOfWeekyear(), n)); } }, MONTH_OF_YEAR(TimexType.DATE, TimeUnit.MONTH, SUTime.YEAR) { @Override protected Time _createTemporal(int n) { //return new PartialTime(new Partial(DateTimeFieldType.monthOfYear(), n)); return new IsoDate(-1, n, -1); } }, PART_OF_YEAR(TimexType.DATE, TimeUnit.DAY, SUTime.YEAR) { @Override public Duration getDuration() { return SUTime.DAY.makeInexact(); } }, SEASON_OF_YEAR(TimexType.DATE, TimeUnit.QUARTER, SUTime.YEAR), QUARTER_OF_YEAR(TimexType.DATE, TimeUnit.QUARTER, SUTime.YEAR) { @Override protected Time _createTemporal(int n) { return new PartialTime(new Partial(JodaTimeUtils.QuarterOfYear, n)); } }, HALF_OF_YEAR(TimexType.DATE, TimeUnit.HALFYEAR, SUTime.YEAR) { @Override protected Time _createTemporal(int n) { return new PartialTime(new Partial(JodaTimeUtils.HalfYearOfYear, n)); } }; final TimexType timexType; TimeUnit unit = TimeUnit.UNKNOWN; Duration period = SUTime.DURATION_NONE; StandardTemporalType(TimexType timexType) { this.timexType = timexType; } StandardTemporalType(TimexType timexType, TimeUnit unit) { this.timexType = timexType; this.unit = unit; this.period = unit.getPeriod(); } StandardTemporalType(TimexType timexType, TimeUnit unit, Duration period) { this.timexType = timexType; this.unit = unit; this.period = period; } public TimexType getTimexType() { return timexType; } public Duration getDuration() { return unit.getDuration(); } // How long does this time last? public Duration getPeriod() { return period; } // How often does this type of time occur? public Duration getGranularity() { return unit.getGranularity(); } // What is the granularity of this time? protected Temporal _createTemporal(int n) { return null; } public Temporal createTemporal(int n) { Temporal t = _createTemporal(n); if (t != null) { t.standardTemporalType = this; } return t; } public static Temporal create(Expressions.CompositeValue compositeValue) { StandardTemporalType temporalType = compositeValue.get("type"); String label = compositeValue.get("label"); String modifier = compositeValue.get("modifier"); Temporal temporal = compositeValue.get("value"); if (temporal == null) { temporal = new PartialTime(); } return SUTime.createTemporal(temporalType, label, modifier, temporal); } } // Temporal operators (currently operates on two temporals and returns another // temporal) // Can add operators for: // lookup of temporal from string // creating durations, dates // public interface TemporalOp extends Function(); public enum TemporalOp { // For durations: possible interpretation of next/prev: // next month, next week // NEXT: on Thursday, next week = week starting on next monday // ??? on Thursday, next week = one week starting from now // prev month, prev week // PREV: on Thursday, last week = week starting on the monday one week // before this monday // ??? on Thursday, last week = one week going back starting from now // NEXT: on June 19, next month = July 1 to July 31 // ???: on June 19, next month = July 19 to August 19 // // // For partial dates: two kind of next // next tuesday, next winter, next january // NEXT (PARENT UNIT, FAVOR): Example: on monday, next tuesday = tuesday of // the week after this // NEXT IMMEDIATE (NOT FAVORED): Example: on monday, next saturday = // saturday of this week // last saturday, last winter, last january // PREV (PARENT UNIT, FAVOR): Example: on wednesday, last tuesday = tuesday // of the week before this // PREV IMMEDIATE (NOT FAVORED): Example: on saturday, last tuesday = // tuesday of this week // (successor) Next week/day/... NEXT { @Override public Temporal apply(Temporal arg1, Temporal arg2, int flags) { if (arg2 == null) { return arg1; } Temporal arg2Next = arg2.next(); if (arg1 == null || arg2Next == null) { return arg2Next; } if (arg1 instanceof Time) { // TODO: flags? Temporal resolved = arg2Next.resolve((Time) arg1, 0 /* RESOLVE_TO_FUTURE */); return resolved; } else { throw new UnsupportedOperationException("NEXT not implemented for arg1=" + arg1.getClass() + ", arg2=" + arg2.getClass()); } } }, // This coming week/friday NEXT_IMMEDIATE { @Override public Temporal apply(Temporal arg1, Temporal arg2, int flags) { if (arg1 == null) { return new RelativeTime(NEXT_IMMEDIATE, arg2); } if (arg2 == null) { return arg1; } // Temporal arg2Next = arg2.next(); // if (arg1 == null || arg2Next == null) { return arg2Next; } if (arg1 instanceof Time) { Time t = (Time) arg1; if (arg2 instanceof Duration) { return ((Duration) arg2).toTime(t, flags | RESOLVE_TO_FUTURE); } else { // TODO: flags? Temporal resolvedThis = arg2.resolve(t, RESOLVE_TO_FUTURE); if (resolvedThis != null) { if (resolvedThis instanceof Time) { if (((Time) resolvedThis).compareTo(t) <= 0) { return NEXT.apply(arg1, arg2); } } } return resolvedThis; } } else { throw new UnsupportedOperationException("NEXT_IMMEDIATE not implemented for arg1=" + arg1.getClass() + ", arg2=" + arg2.getClass()); } } }, // Use arg1 as reference to resolve arg2 (take more general fields from arg1 // and apply to arg2) THIS { @Override public Temporal apply(Temporal arg1, Temporal arg2, int flags) { if (arg1 == null) { return new RelativeTime(THIS, arg2, flags); } if (arg1 instanceof Time) { if (arg2 instanceof Duration) { return ((Duration) arg2).toTime((Time) arg1, flags); } else { // TODO: flags? return arg2.resolve((Time) arg1, flags | RESOLVE_TO_THIS); } } else { throw new UnsupportedOperationException("THIS not implemented for arg1=" + arg1.getClass() + ", arg2=" + arg2.getClass()); } } }, // (predecessor) Previous week/day/... PREV { @Override public Temporal apply(Temporal arg1, Temporal arg2, int flags) { if (arg2 == null) { return arg1; } Temporal arg2Prev = arg2.prev(); if (arg1 == null || arg2Prev == null) { return arg2Prev; } if (arg1 instanceof Time) { // TODO: flags? Temporal resolved = arg2Prev.resolve((Time) arg1, 0 /*RESOLVE_TO_PAST */); return resolved; } else { throw new UnsupportedOperationException("PREV not implemented for arg1=" + arg1.getClass() + ", arg2=" + arg2.getClass()); } } }, // This past week/friday PREV_IMMEDIATE { @Override public Temporal apply(Temporal arg1, Temporal arg2, int flags) { if (arg1 == null) { return new RelativeTime(PREV_IMMEDIATE, arg2); } if (arg2 == null) { return arg1; } // Temporal arg2Prev = arg2.prev(); // if (arg1 == null || arg2Prev == null) { return arg2Prev; } if (arg1 instanceof Time) { Time t = (Time) arg1; if (arg2 instanceof Duration) { return ((Duration) arg2).toTime(t, flags | RESOLVE_TO_PAST); } else { // TODO: flags? Temporal resolvedThis = arg2.resolve(t, RESOLVE_TO_PAST); if (resolvedThis != null) { if (resolvedThis instanceof Time) { if (((Time) resolvedThis).compareTo(t) >= 0) { return PREV.apply(arg1, arg2); } } } return resolvedThis; } } else { throw new UnsupportedOperationException("PREV_IMMEDIATE not implemented for arg1=" + arg1.getClass() + ", arg2=" + arg2.getClass()); } } }, UNION { @Override public Temporal apply(Temporal arg1, Temporal arg2, int flags) { if (arg1 == null) { return arg2; } if (arg2 == null) { return arg1; } // return arg1.union(arg2); throw new UnsupportedOperationException("UNION not implemented for arg1=" + arg1.getClass() + ", arg2=" + arg2.getClass()); } }, INTERSECT { @Override public Temporal apply(Temporal arg1, Temporal arg2, int flags) { if (arg1 == null) { return arg2; } if (arg2 == null) { return arg1; } Temporal t = arg1.intersect(arg2); if (t == null) { t = arg2.intersect(arg1); } return t; // throw new // UnsupportedOperationException("INTERSECT not implemented for arg1=" + // arg1.getClass() + ", arg2="+arg2.getClass()); } }, // arg2 is "in" arg1, composite datetime IN { @Override public Temporal apply(Temporal arg1, Temporal arg2, int flags) { if (arg1 == null) { return arg2; } if (arg1 instanceof Time) { // TODO: flags? return arg2.intersect((Time) arg1); } else { throw new UnsupportedOperationException("IN not implemented for arg1=" + arg1.getClass() + ", arg2=" + arg2.getClass()); } } }, OFFSET { // There is inexact offset where we remove anything from the result that is more granular than the duration @Override public Temporal apply(Temporal arg1, Temporal arg2, int flags) { if (arg1 == null) { return new RelativeTime(OFFSET, arg2); } if (arg1 instanceof Time && arg2 instanceof Duration) { return ((Time) arg1).offset((Duration) arg2, flags | RELATIVE_OFFSET_INEXACT); } else if (arg1 instanceof Range && arg2 instanceof Duration) { return ((Range) arg1).offset((Duration) arg2, flags | RELATIVE_OFFSET_INEXACT); } else { throw new UnsupportedOperationException("OFFSET not implemented for arg1=" + arg1.getClass() + ", arg2=" + arg2.getClass()); } } }, MINUS { @Override public Temporal apply(Temporal arg1, Temporal arg2, int flags) { if (arg1 == null) { return arg2; } if (arg2 == null) { return arg1; } if (arg1 instanceof Duration && arg2 instanceof Duration) { return ((Duration) arg1).subtract((Duration) arg2); } else if (arg1 instanceof Time && arg2 instanceof Duration) { return ((Time) arg1).subtract((Duration) arg2); } else if (arg1 instanceof Range && arg2 instanceof Duration) { return ((Range) arg1).subtract((Duration) arg2); } else { throw new UnsupportedOperationException("MINUS not implemented for arg1=" + arg1.getClass() + ", arg2=" + arg2.getClass()); } } }, PLUS { @Override public Temporal apply(Temporal arg1, Temporal arg2, int flags) { if (arg1 == null) { return arg2; } if (arg2 == null) { return arg1; } if (arg1 instanceof Duration && arg2 instanceof Duration) { return ((Duration) arg1).add((Duration) arg2); } else if (arg1 instanceof Time && arg2 instanceof Duration) { return ((Time) arg1).add((Duration) arg2); } else if (arg1 instanceof Range && arg2 instanceof Duration) { return ((Range) arg1).add((Duration) arg2); } else { throw new UnsupportedOperationException("PLUS not implemented for arg1=" + arg1.getClass() + ", arg2=" + arg2.getClass()); } } }, MIN { @Override public Temporal apply(Temporal arg1, Temporal arg2, int flags) { if (arg1 == null) { return arg2; } if (arg2 == null) { return arg1; } if (arg1 instanceof Time && arg2 instanceof Time) { return Time.min((Time) arg1, (Time) arg2); } else if (arg1 instanceof Duration && arg2 instanceof Duration) { return Duration.min((Duration) arg1, (Duration) arg2); } else { throw new UnsupportedOperationException("MIN not implemented for arg1=" + arg1.getClass() + ", arg2=" + arg2.getClass()); } } }, MAX { @Override public Temporal apply(Temporal arg1, Temporal arg2, int flags) { if (arg1 == null) { return arg2; } if (arg2 == null) { return arg1; } if (arg1 instanceof Time && arg2 instanceof Time) { return Time.max((Time) arg1, (Time) arg2); } else if (arg1 instanceof Duration && arg2 instanceof Duration) { return Duration.max((Duration) arg1, (Duration) arg2); } else { throw new UnsupportedOperationException("MAX not implemented for arg1=" + arg1.getClass() + ", arg2=" + arg2.getClass()); } } }, MULTIPLY { public Temporal apply(Duration d, int scale) { if (d == null) return null; if (scale == 1) return d; return d.multiplyBy(scale); } public Temporal apply(PeriodicTemporalSet d, int scale) { if (d == null) return null; if (scale == 1) return d; return d.multiplyDurationBy(scale); } @Override public Temporal apply(Object... args) { if (args.length == 2) { if (args[0] instanceof Duration && (args[1] instanceof Integer || args[1] instanceof Long)) { return apply((Duration) args[0], ((Number) args[1]).intValue()); } if (args[0] instanceof PeriodicTemporalSet && (args[1] instanceof Integer || args[1] instanceof Long)) { return apply((PeriodicTemporalSet) args[0], ((Number) args[1]).intValue()); } } throw new UnsupportedOperationException("apply(Object...) not implemented for TemporalOp " + this); } }, DIVIDE { public Temporal apply(Duration d, int scale) { if (d == null) return null; if (scale == 1) return d; return d.divideBy(scale); } public Temporal apply(PeriodicTemporalSet d, int scale) { if (d == null) return null; if (scale == 1) return d; return d.divideDurationBy(scale); } @Override public Temporal apply(Object... args) { if (args.length == 2) { if (args[0] instanceof Duration && (args[1] instanceof Integer || args[1] instanceof Long)) { return apply((Duration) args[0], ((Number) args[1]).intValue()); } if (args[0] instanceof PeriodicTemporalSet && (args[1] instanceof Integer || args[1] instanceof Long)) { return apply((PeriodicTemporalSet) args[0], ((Number) args[1]).intValue()); } } throw new UnsupportedOperationException("apply(Object...) not implemented for TemporalOp " + this); } }, CREATE { public Temporal apply(TimeUnit tu, int n) { return tu.createTemporal(n); } @Override public Temporal apply(Object... args) { if (args.length == 2) { if (args[0] instanceof TimeUnit && args[1] instanceof Number) { return apply((TimeUnit) args[0], ((Number) args[1]).intValue()); } else if (args[0] instanceof StandardTemporalType && args[1] instanceof Number) { return ((StandardTemporalType) args[0]).createTemporal(((Number) args[1]).intValue()); } else if (args[0] instanceof Temporal && args[1] instanceof Number) { return new OrdinalTime((Temporal) args[0], ((Number) args[1]).intValue()); } } throw new UnsupportedOperationException("apply(Object...) not implemented for TemporalOp " + this); } }, ADD_MODIFIER { public Temporal apply(Temporal t, String modifier) { return t.addMod(modifier); } @Override public Temporal apply(Object... args) { if (args.length == 2) { if (args[0] instanceof Temporal && args[1] instanceof String) { return apply((Temporal) args[0], (String) args[1]); } } throw new UnsupportedOperationException("apply(Object...) not implemented for TemporalOp " + this); } }, OFFSET_EXACT { // There is exact offset (more granular parts than the duration are kept) @Override public Temporal apply(Temporal arg1, Temporal arg2, int flags) { if (arg1 == null) { return new RelativeTime(OFFSET_EXACT, arg2); } if (arg1 instanceof Time && arg2 instanceof Duration) { return ((Time) arg1).offset((Duration) arg2, flags); } else if (arg1 instanceof Range && arg2 instanceof Duration) { return ((Range) arg1).offset((Duration) arg2, flags); } else { throw new UnsupportedOperationException("OFFSET_EXACT not implemented for arg1=" + arg1.getClass() + ", arg2=" + arg2.getClass()); } } }; public Temporal apply(Temporal arg1, Temporal arg2, int flags) { throw new UnsupportedOperationException("apply(Temporal, Temporal, int) not implemented for TemporalOp " + this); } public Temporal apply(Temporal arg1, Temporal arg2) { return apply(arg1, arg2, 0); } public Temporal apply(Temporal... args) { if (args.length == 2) { return apply(args[0], args[1]); } throw new UnsupportedOperationException("apply(Temporal...) not implemented for TemporalOp " + this); } public Temporal apply(Object... args) { throw new UnsupportedOperationException("apply(Object...) not implemented for TemporalOp " + this); } } /** * Time represents a time point on some time scale. * It is the base class for representing various types of time points. * Typically, since most time scales have marks with certain granularity * each time point can be represented as an interval. */ public abstract static class Time extends Temporal implements FuzzyInterval.FuzzyComparable




© 2015 - 2024 Weber Informatics LLC | Privacy Policy