123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527 |
- package gnu.math;
- import java.io.*;
- public class Duration extends Quantity implements Externalizable
- {
- public Unit unit;
- /** Number of whole months. May be negative. */
- int months;
- /** Does not include any leap seconds.
- * I.e. @code{sign * ((24 * days + hours) * 60 + minutes) * 60 + seconds},
- * where {@code hours >= 0 && hours < 24 && minutes >= 0 && minutes < 60
- * && secconds >= 0 && minutes > 60}.
- */
- long seconds;
- /** Number of nanoseconds.
- * We could possibly include leap seconds in here. */
- int nanos;
- public static Duration make (int months, long seconds, int nanos, Unit unit)
- {
- Duration d = new Duration();
- d.months = months;
- d.seconds = seconds;
- d.nanos = nanos;
- d.unit = unit;
- return d;
- }
- public static Duration makeMonths(int months)
- {
- Duration d = new Duration();
- d.unit = Unit.month;
- d.months = months;
- return d;
- }
- public static Duration makeMinutes (int minutes)
- {
- Duration d = new Duration();
- d.unit = Unit.second;
- d.seconds = 60 * minutes;
- return d;
- }
- public static Duration parse (String str, Unit unit)
- {
- Duration d = Duration.valueOf(str, unit);
- if (d == null)
- throw new IllegalArgumentException("not a valid "+unit.getName()+" duration: '"+str+"'");
- return d;
- }
- public static Duration parseDuration (String str)
- {
- return parse(str, Unit.duration);
- }
- public static Duration parseYearMonthDuration (String str)
- {
- return parse(str, Unit.month);
- }
- public static Duration parseDayTimeDuration (String str)
- {
- return parse(str, Unit.second);
- }
- /** Parse a duration lexical value as specified by XML Schama.
- * Return null if invalid syntax.
- */
- public static Duration valueOf (String str, Unit unit)
- {
- str = str.trim();
- int pos = 0;
- int len = str.length();
- boolean negative;
- if (pos < len && str.charAt(pos) == '-')
- {
- negative = true;
- pos++;
- }
- else
- negative = false;
- if (pos + 1 >= len || str.charAt(pos) != 'P')
- return null;
- pos++;
- int months = 0, nanos = 0;
- long seconds = 0;
- long part = scanPart(str, pos);
- pos = ((int) part) >> 16;
- char ch = (char) part;
- if (unit == Unit.second && (ch == 'Y' || ch == 'M'))
- return null;
- if (ch == 'Y')
- {
- months = 12 * (int) (part >> 32);
- pos = ((int) part) >> 16;
- part = scanPart(str, pos);
- ch = (char) part;
- }
- if (ch == 'M')
- {
- months += (part >> 32);
- pos = ((int) part) >> 16;
- part = scanPart(str, pos);
- ch = (char) part;
- }
- if (unit == Unit.month && pos != len)
- return null;
- if (ch == 'D')
- {
- if (unit == Unit.month)
- return null;
- seconds = (long) (24 * 60 * 60) * (int) (part >> 32);
- pos = ((int) part) >> 16;
- part = scanPart (str, pos);
- }
- if (part != (pos << 16))
- return null;
- if (pos == len)
- {
- // No time part
- }
- else if (str.charAt(pos) != 'T' || ++pos == len)
- return null;
- else // saw 'T'
- {
- if (unit == Unit.month)
- return null;
- part = scanPart (str, pos);
- ch = (char) part;
- if (ch == 'H')
- {
- seconds += (60 * 60) * (int) (part >> 32);
- pos = ((int) part) >> 16;
- part = scanPart (str, pos);
- ch = (char) part;
- }
- if (ch == 'M')
- {
- seconds += 60 * (int) (part >> 32);
- pos = ((int) part) >> 16;
- part = scanPart (str, pos);
- ch = (char) part;
- }
- if (ch == 'S' || ch == '.')
- {
- seconds += (int) (part >> 32);
- pos = ((int) part) >> 16;
- }
- if (ch == '.' && pos + 1 < len
- && Character.digit(str.charAt(pos), 10) >= 0)
- {
- int nfrac = 0;
- for (; pos < len; nfrac++)
- {
- ch = str.charAt(pos++);
- int dig = Character.digit(ch, 10);
- if (dig < 0)
- break;
- if (nfrac < 9)
- nanos = 10 * nanos + dig;
- else if (nfrac == 9 && dig >= 5)
- nanos++;
- }
- while (nfrac++ < 9)
- nanos = 10 * nanos;
- if (ch != 'S')
- return null;
- }
- }
- if (pos != len)
- return null;
- Duration d = new Duration();
- if (negative)
- {
- months = -months;
- seconds = -seconds;
- nanos = -nanos;
- }
- d.months = months;
- d.seconds = seconds;
- d.nanos = nanos;
- d.unit = unit;
- return d;
- }
- public Numeric add (Object y, int k)
- {
- if (y instanceof Duration)
- return Duration.add (this, (Duration) y, k);
- if (y instanceof DateTime && k == 1)
- return DateTime.add((DateTime) y, this, 1);
- throw new IllegalArgumentException ();
- }
- public Numeric mul (Object y)
- {
- if (y instanceof RealNum)
- return Duration.times(this, ((RealNum) y).doubleValue());
- return ((Numeric)y).mulReversed (this);
- }
- public Numeric mulReversed (Numeric x)
- {
- if (! (x instanceof RealNum))
- throw new IllegalArgumentException ();
- return Duration.times(this, ((RealNum) x).doubleValue());
- }
- public static double div (Duration dur1, Duration dur2)
- {
- int months1 = dur1.months;
- int months2 = dur2.months;
- double sec1 = (double) dur1.seconds + dur1.nanos * 0.000000001;
- double sec2 = (double) dur2.seconds + dur1.nanos * 0.000000001;
- if (months2 == 0 && sec2 == 0)
- throw new ArithmeticException("divide duration by zero");
- if (months2 == 0)
- {
- if (months1 == 0)
- return sec1 /sec2;
- }
- else if (sec2 == 0)
- {
- if (sec1 == 0)
- return (double) months1 / (double) months2;
- }
- throw new ArithmeticException("divide of incompatible durations");
- }
- public Numeric div (Object y)
- {
- if (y instanceof RealNum)
- {
- double dy = ((RealNum) y).doubleValue();
- if (dy == 0 || Double.isNaN(dy))
- throw new ArithmeticException("divide of duration by 0 or NaN");
- return Duration.times(this, 1.0 / dy);
- }
- if (y instanceof Duration)
- return new DFloNum(div(this, (Duration) y));
- return ((Numeric)y).divReversed (this);
- }
- public static Duration add (Duration x, Duration y, int k)
- {
- long months = (long) x.months + k * (long) y.months;
- // FIXME does not handle leap-seconds represented as multiples of
- // 10^9 in the nanos field.
- long nanos = x.seconds * 1000000000L + (long) x.nanos
- + k * (y.seconds * 1000000000L + y.nanos);
- // FIXME check for overflow
- // FIXME handle inconsistent signs.
- Duration d = new Duration();
- d.months = (int) months;
- d.seconds = (int) (nanos / 1000000000L);
- d.nanos = (int) (nanos % 1000000000L);
- if (x.unit != y.unit || x.unit == Unit.duration)
- throw new ArithmeticException("cannot add these duration types");
- d.unit = x.unit;
- return d;
- }
- public static Duration times (Duration x, double y)
- {
- if (x.unit == Unit.duration)
- throw new IllegalArgumentException("cannot multiply general duration");
- double months = x.months * y;
- if (Double.isInfinite(months) || Double.isNaN(months))
- throw new ArithmeticException("overflow/NaN when multiplying a duration");
- double nanos = (x.seconds * 1000000000L + x.nanos) * y;
- Duration d = new Duration();
- d.months = (int) Math.floor(months + 0.5);
- d.seconds = (int) (nanos / 1000000000L);
- d.nanos = (int) (nanos % 1000000000L);
- d.unit = x.unit;
- return d;
- }
- public static int compare (Duration x, Duration y)
- {
- long months = (long) x.months - (long) y.months;
- long nanos = x.seconds * 1000000000L + (long) x.nanos
- - (y.seconds * 1000000000L + y.nanos);
- if (months < 0 && nanos <= 0)
- return -1;
- if (months > 0 && nanos >= 0)
- return 1;
- if (months == 0)
- return nanos < 0 ? -1 : nanos > 0 ? 1 : 0;
- return -2;
- }
- public int compare (Object obj)
- {
- if (obj instanceof Duration)
- return compare(this, (Duration) obj);
- // Could also compare other Quanties if units match appropriately. FIXME.
- throw new IllegalArgumentException ();
- }
- public String toString ()
- {
- StringBuffer sbuf = new StringBuffer();
- int m = months;
- long s = seconds;
- int n = nanos;
- boolean neg = m < 0 || s < 0 || n < 0;
- if (neg)
- {
- m = -m;
- s = -s;
- n = -n;
- sbuf.append('-');
- }
- sbuf.append('P');
- int y = m / 12;
- if (y != 0)
- {
- sbuf.append(y);
- sbuf.append('Y');
- m -= y * 12;
- }
- if (m != 0)
- {
- sbuf.append(m);
- sbuf.append('M');
- }
- long d = s / (24 * 60 * 60);
- if (d != 0)
- {
- sbuf.append(d);
- sbuf.append('D');
- s -= 24 * 60 * 60 * d;
- }
- if (s != 0 || n != 0)
- {
- sbuf.append('T');
- long hr = s / (60 * 60);
- if (hr != 0)
- {
- sbuf.append(hr);
- sbuf.append('H');
- s -= 60 * 60 * hr;
- }
- long mn = s / 60;
- if (mn != 0)
- {
- sbuf.append(mn);
- sbuf.append('M');
- s -= 60 * mn;
- }
- if (s != 0 || n != 0)
- {
- sbuf.append(s);
- appendNanoSeconds(n, sbuf);
- sbuf.append('S');
- }
- }
- else if (sbuf.length() == 1)
- sbuf.append(unit == Unit.month ? "0M" : "T0S");
- return sbuf.toString();
- }
- static void appendNanoSeconds (int nanoSeconds, StringBuffer sbuf)
- {
- if (nanoSeconds == 0)
- return;
- sbuf.append('.');
- int pos = sbuf.length();
- sbuf.append(nanoSeconds);
- int len = sbuf.length();
- int pad = pos + 9 - len;
- while (--pad >= 0)
- sbuf.insert(pos, '0');
- len = pos + 9;
- do { --len; } while (sbuf.charAt(len) == '0');
- sbuf.setLength(len+1);
- }
- /** Parse digits following by a terminator char
- * @return {@code (VALUE << 32)|(FOLLOWING_POS<<16)|FOLLOWING_CHAR}.
- * If there are no digits return @code{START<<16}.
- * Otherwise, on overflow or digits followed by end-of-string, return -1.
- */
- private static long scanPart (String str, int start)
- {
- int i = start;
- long val = -1;
- int len = str.length();
- while (i < len)
- {
- char ch = str.charAt(i);
- i++;
- int dig = Character.digit(ch, 10);
- if (dig < 0)
- {
- if (val < 0) return start << 16;
- return (val << 32) | (i << 16) | ((int) ch);
- }
- val = val < 0 ? dig : 10 * val + dig;
- if (val > Integer.MAX_VALUE)
- return -1; // overflow
- }
- return val < 0 ? (start << 16) : -1;
- }
- /** The number of years in the canonical representation. */
- public int getYears ()
- {
- return months / 12;
- }
- public int getMonths()
- {
- return months % 12;
- }
- public int getDays ()
- {
- return (int) (seconds / (24 * 60 * 60));
- }
- public int getHours ()
- {
- return (int) ((seconds / (60 * 60)) % 24);
- }
- public int getMinutes ()
- {
- return (int) ((seconds / 60) % 60);
- }
- public int getSecondsOnly ()
- {
- return (int) (seconds % 60);
- }
- public int getNanoSecondsOnly ()
- {
- return nanos;
- }
- public int getTotalMonths ()
- {
- return months;
- }
- public long getTotalSeconds ()
- {
- return seconds;
- }
- public long getTotalMinutes ()
- {
- return seconds / 60;
- }
- public long getNanoSeconds ()
- {
- return seconds * 1000000000L + nanos;
- }
- public boolean isZero ()
- {
- return months == 0 && seconds == 0 && nanos == 0;
- }
- public boolean isExact ()
- {
- return false;
- }
- public void writeExternal(ObjectOutput out) throws IOException
- {
- out.writeInt(months);
- out.writeLong(seconds);
- out.writeInt(nanos);
- out.writeObject(unit);
- }
- public void readExternal(ObjectInput in)
- throws IOException, ClassNotFoundException
- {
- months = in.readInt();
- seconds = in.readLong();
- nanos = in.readInt();
- unit = (Unit) in.readObject();
- }
- public Unit unit() { return unit; }
- public Complex number ()
- {
- throw new UnsupportedOperationException("number needs to be implemented!");
- }
- public int hashCode ()
- {
- return months ^ (int) seconds ^ nanos;
- }
- /** Compare for equality.
- * Ignores unit.
- */
- public static boolean equals (Duration x, Duration y)
- {
- return x.months == y.months
- && x.seconds == y.seconds
- && x.nanos == y.nanos;
- }
- /** Compare for equality.
- * Ignores unit.
- */
- public boolean equals (Object obj)
- {
- if (obj == null || ! (obj instanceof Duration))
- return false;
- return Duration.equals (this, (Duration) obj);
- }
- }
|