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

org.joda.time.Interval Maven / Gradle / Ivy

There is a newer version: 2024.11.18751.20241128T090041Z-241100
Show newest version
/*
 *  Copyright 2001-2006 Stephen Colebourne
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */
package org.joda.time;

import java.io.Serializable;

import org.joda.time.base.BaseInterval;
import org.joda.time.chrono.ISOChronology;
import org.joda.time.format.DateTimeFormatter;
import org.joda.time.format.ISODateTimeFormat;
import org.joda.time.format.ISOPeriodFormat;
import org.joda.time.format.PeriodFormatter;

/**
 * Interval is the standard implementation of an immutable time interval.
 * 

* A time interval represents a period of time between two instants. * Intervals are inclusive of the start instant and exclusive of the end. * The end instant is always greater than or equal to the start instant. *

* Intervals have a fixed millisecond duration. * This is the difference between the start and end instants. * The duration is represented separately by {@link ReadableDuration}. * As a result, intervals are not comparable. * To compare the length of two intervals, you should compare their durations. *

* An interval can also be converted to a {@link ReadablePeriod}. * This represents the difference between the start and end points in terms of fields * such as years and days. *

* Interval is thread-safe and immutable. * * @author Brian S O'Neill * @author Sean Geoghegan * @author Stephen Colebourne * @author Julen Parra * @since 1.0 */ public final class Interval extends BaseInterval implements ReadableInterval, Serializable { /** Serialization version */ private static final long serialVersionUID = 4922451897541386752L; //----------------------------------------------------------------------- /** * Parses an {@code Interval} from the specified string. *

* The String formats are described by {@link ISODateTimeFormat#dateTimeParser()} * and {@link ISOPeriodFormat#standard()}, and may be 'datetime/datetime', * 'datetime/period' or 'period/datetime'. *

* This method operates by parsing in the default time-zone. * Any offset contained within the string being parsed will be normalised to the * offset of the default time-zone. See also {@link #parseWithOffset(String)}. * * @param str the string to parse, not null * @since 2.0 */ public static Interval parse(String str) { return new Interval(str); } /** * Parses an {@code Interval} from the specified string, using any offset it contains. *

* The String formats are described by * {@link ISODateTimeFormat#dateTimeParser()}{@code .withOffsetParsed()} * and {@link ISOPeriodFormat#standard()}, and may be 'datetime/datetime', * 'datetime/period' or 'period/datetime'. *

* Sometimes this method and {@code new Interval(str)} return different results. * This can be confusing as the difference is not visible in {@link #toString()}. *

* When passed a string without an offset, such as '2010-06-30T01:20/P1D', * both the constructor and this method use the default time-zone. * As such, {@code Interval.parseWithOffset("2010-06-30T01:20/P1D")} and * {@code new Interval("2010-06-30T01:20/P1D"))} are equal. *

* However, when this method is passed a string with an offset, * the offset is directly parsed and stored. * As such, {@code Interval.parseWithOffset("2010-06-30T01:20+02:00/P1D")} and * {@code new Interval("2010-06-30T01:20+02:00/P1D"))} are NOT equal. * The object produced via this method has a zone of {@code DateTimeZone.forOffsetHours(2)}. * The object produced via the constructor has a zone of {@code DateTimeZone.getDefault()}. * * @param str the string to parse, not null * @since 2.9 */ public static Interval parseWithOffset(String str) { int separator = str.indexOf('/'); if (separator < 0) { throw new IllegalArgumentException("Format requires a '/' separator: " + str); } String leftStr = str.substring(0, separator); if (leftStr.length() <= 0) { throw new IllegalArgumentException("Format invalid: " + str); } String rightStr = str.substring(separator + 1); if (rightStr.length() <= 0) { throw new IllegalArgumentException("Format invalid: " + str); } DateTimeFormatter dateTimeParser = ISODateTimeFormat.dateTimeParser().withOffsetParsed(); PeriodFormatter periodParser = ISOPeriodFormat.standard(); DateTime start = null; Period period = null; // before slash char c = leftStr.charAt(0); if (c == 'P' || c == 'p') { period = periodParser.withParseType(PeriodType.standard()).parsePeriod(leftStr); } else { start = dateTimeParser.parseDateTime(leftStr); } // after slash c = rightStr.charAt(0); if (c == 'P' || c == 'p') { if (period != null) { throw new IllegalArgumentException("Interval composed of two durations: " + str); } period = periodParser.withParseType(PeriodType.standard()).parsePeriod(rightStr); return new Interval(start, period); } else { DateTime end = dateTimeParser.parseDateTime(rightStr); if (period != null) { return new Interval(period, end); } else { return new Interval(start, end); } } } //----------------------------------------------------------------------- /** * Constructs an interval from a start and end instant with the ISO * default chronology in the default time zone. * * @param startInstant start of this interval, as milliseconds from 1970-01-01T00:00:00Z. * @param endInstant end of this interval, as milliseconds from 1970-01-01T00:00:00Z. * @throws IllegalArgumentException if the end is before the start */ public Interval(long startInstant, long endInstant) { super(startInstant, endInstant, null); } /** * Constructs an interval from a start and end instant with the ISO * default chronology in the specified time zone. * * @param startInstant start of this interval, as milliseconds from 1970-01-01T00:00:00Z. * @param endInstant end of this interval, as milliseconds from 1970-01-01T00:00:00Z. * @param zone the time zone to use, null means default zone * @throws IllegalArgumentException if the end is before the start * @since 1.5 */ public Interval(long startInstant, long endInstant, DateTimeZone zone) { super(startInstant, endInstant, ISOChronology.getInstance(zone)); } /** * Constructs an interval from a start and end instant with the * specified chronology. * * @param chronology the chronology to use, null is ISO default * @param startInstant start of this interval, as milliseconds from 1970-01-01T00:00:00Z. * @param endInstant end of this interval, as milliseconds from 1970-01-01T00:00:00Z. * @throws IllegalArgumentException if the end is before the start */ public Interval(long startInstant, long endInstant, Chronology chronology) { super(startInstant, endInstant, chronology); } /** * Constructs an interval from a start and end instant. *

* The chronology used is that of the start instant. * * @param start start of this interval, null means now * @param end end of this interval, null means now * @throws IllegalArgumentException if the end is before the start */ public Interval(ReadableInstant start, ReadableInstant end) { super(start, end); } /** * Constructs an interval from a start instant and a duration. * * @param start start of this interval, null means now * @param duration the duration of this interval, null means zero length * @throws IllegalArgumentException if the end is before the start * @throws ArithmeticException if the end instant exceeds the capacity of a long */ public Interval(ReadableInstant start, ReadableDuration duration) { super(start, duration); } /** * Constructs an interval from a millisecond duration and an end instant. * * @param duration the duration of this interval, null means zero length * @param end end of this interval, null means now * @throws IllegalArgumentException if the end is before the start * @throws ArithmeticException if the start instant exceeds the capacity of a long */ public Interval(ReadableDuration duration, ReadableInstant end) { super(duration, end); } /** * Constructs an interval from a start instant and a time period. *

* When forming the interval, the chronology from the instant is used * if present, otherwise the chronology of the period is used. * * @param start start of this interval, null means now * @param period the period of this interval, null means zero length * @throws IllegalArgumentException if the end is before the start * @throws ArithmeticException if the end instant exceeds the capacity of a long */ public Interval(ReadableInstant start, ReadablePeriod period) { super(start, period); } /** * Constructs an interval from a time period and an end instant. *

* When forming the interval, the chronology from the instant is used * if present, otherwise the chronology of the period is used. * * @param period the period of this interval, null means zero length * @param end end of this interval, null means now * @throws IllegalArgumentException if the end is before the start * @throws ArithmeticException if the start instant exceeds the capacity of a long */ public Interval(ReadablePeriod period, ReadableInstant end) { super(period, end); } /** * Constructs a time interval by converting or copying from another object. *

* The recognised object types are defined in * {@link org.joda.time.convert.ConverterManager ConverterManager} and * include ReadableInterval and String. * The String formats are described by {@link ISODateTimeFormat#dateTimeParser()} * and {@link ISOPeriodFormat#standard()}, and may be 'datetime/datetime', * 'datetime/period' or 'period/datetime'. * * @param interval the time interval to copy * @throws IllegalArgumentException if the interval is invalid */ public Interval(Object interval) { super(interval, null); } /** * Constructs a time interval by converting or copying from another object, * overriding the chronology. *

* The recognised object types are defined in * {@link org.joda.time.convert.ConverterManager ConverterManager} and * include ReadableInterval and String. * The String formats are described by {@link ISODateTimeFormat#dateTimeParser()} * and {@link ISOPeriodFormat#standard()}, and may be 'datetime/datetime', * 'datetime/period' or 'period/datetime'. * * @param interval the time interval to copy * @param chronology the chronology to use, null means ISO default * @throws IllegalArgumentException if the interval is invalid */ public Interval(Object interval, Chronology chronology) { super(interval, chronology); } //----------------------------------------------------------------------- /** * Get this interval as an immutable Interval object * by returning this. * * @return this */ public Interval toInterval() { return this; } //----------------------------------------------------------------------- /** * Gets the overlap between this interval and another interval. *

* Intervals are inclusive of the start instant and exclusive of the end. * An interval overlaps another if it shares some common part of the * datetime continuum. This method returns the amount of the overlap, * only if the intervals actually do overlap. * If the intervals do not overlap, then null is returned. *

* When two intervals are compared the result is one of three states: * (a) they abut, (b) there is a gap between them, (c) they overlap. * The abuts state takes precedence over the other two, thus a zero duration * interval at the start of a larger interval abuts and does not overlap. *

* The chronology of the returned interval is the same as that of * this interval (the chronology of the interval parameter is not used). * Note that the use of the chronology was only correctly implemented * in version 1.3. * * @param interval the interval to examine, null means now * @return the overlap interval, null if no overlap * @since 1.1 */ public Interval overlap(ReadableInterval interval) { interval = DateTimeUtils.getReadableInterval(interval); if (overlaps(interval) == false) { return null; } long start = Math.max(getStartMillis(), interval.getStartMillis()); long end = Math.min(getEndMillis(), interval.getEndMillis()); return new Interval(start, end, getChronology()); } //----------------------------------------------------------------------- /** * Gets the gap between this interval and another interval. * The other interval can be either before or after this interval. *

* Intervals are inclusive of the start instant and exclusive of the end. * An interval has a gap to another interval if there is a non-zero * duration between them. This method returns the amount of the gap only * if the intervals do actually have a gap between them. * If the intervals overlap or abut, then null is returned. *

* When two intervals are compared the result is one of three states: * (a) they abut, (b) there is a gap between them, (c) they overlap. * The abuts state takes precedence over the other two, thus a zero duration * interval at the start of a larger interval abuts and does not overlap. *

* The chronology of the returned interval is the same as that of * this interval (the chronology of the interval parameter is not used). * Note that the use of the chronology was only correctly implemented * in version 1.3. * * @param interval the interval to examine, null means now * @return the gap interval, null if no gap * @since 1.1 */ public Interval gap(ReadableInterval interval) { interval = DateTimeUtils.getReadableInterval(interval); long otherStart = interval.getStartMillis(); long otherEnd = interval.getEndMillis(); long thisStart = getStartMillis(); long thisEnd = getEndMillis(); if (thisStart > otherEnd) { return new Interval(otherEnd, thisStart, getChronology()); } else if (otherStart > thisEnd) { return new Interval(thisEnd, otherStart, getChronology()); } else { return null; } } //----------------------------------------------------------------------- /** * Does this interval abut with the interval specified. *

* Intervals are inclusive of the start instant and exclusive of the end. * An interval abuts if it starts immediately after, or ends immediately * before this interval without overlap. * A zero duration interval abuts with itself. *

* When two intervals are compared the result is one of three states: * (a) they abut, (b) there is a gap between them, (c) they overlap. * The abuts state takes precedence over the other two, thus a zero duration * interval at the start of a larger interval abuts and does not overlap. *

* For example: *

     * [09:00 to 10:00) abuts [08:00 to 08:30)  = false (completely before)
     * [09:00 to 10:00) abuts [08:00 to 09:00)  = true
     * [09:00 to 10:00) abuts [08:00 to 09:01)  = false (overlaps)
     * 
     * [09:00 to 10:00) abuts [09:00 to 09:00)  = true
     * [09:00 to 10:00) abuts [09:00 to 09:01)  = false (overlaps)
     * 
     * [09:00 to 10:00) abuts [10:00 to 10:00)  = true
     * [09:00 to 10:00) abuts [10:00 to 10:30)  = true
     * 
     * [09:00 to 10:00) abuts [10:30 to 11:00)  = false (completely after)
     * 
     * [14:00 to 14:00) abuts [14:00 to 14:00)  = true
     * [14:00 to 14:00) abuts [14:00 to 15:00)  = true
     * [14:00 to 14:00) abuts [13:00 to 14:00)  = true
     * 
* * @param interval the interval to examine, null means now * @return true if the interval abuts * @since 1.1 */ public boolean abuts(ReadableInterval interval) { if (interval == null) { long now = DateTimeUtils.currentTimeMillis(); return (getStartMillis() == now || getEndMillis() == now); } else { return (interval.getEndMillis() == getStartMillis() || getEndMillis() == interval.getStartMillis()); } } //----------------------------------------------------------------------- /** * Creates a new interval with the same start and end, but a different chronology. * * @param chronology the chronology to use, null means ISO default * @return an interval with a different chronology */ public Interval withChronology(Chronology chronology) { if (getChronology() == chronology) { return this; } return new Interval(getStartMillis(), getEndMillis(), chronology); } /** * Creates a new interval with the specified start millisecond instant. * * @param startInstant the start instant for the new interval * @return an interval with the end from this interval and the specified start * @throws IllegalArgumentException if the resulting interval has end before start */ public Interval withStartMillis(long startInstant) { if (startInstant == getStartMillis()) { return this; } return new Interval(startInstant, getEndMillis(), getChronology()); } /** * Creates a new interval with the specified start instant. * * @param start the start instant for the new interval, null means now * @return an interval with the end from this interval and the specified start * @throws IllegalArgumentException if the resulting interval has end before start */ public Interval withStart(ReadableInstant start) { long startMillis = DateTimeUtils.getInstantMillis(start); return withStartMillis(startMillis); } /** * Creates a new interval with the specified end millisecond instant. * * @param endInstant the end instant for the new interval * @return an interval with the start from this interval and the specified end * @throws IllegalArgumentException if the resulting interval has end before start */ public Interval withEndMillis(long endInstant) { if (endInstant == getEndMillis()) { return this; } return new Interval(getStartMillis(), endInstant, getChronology()); } /** * Creates a new interval with the specified end instant. * * @param end the end instant for the new interval, null means now * @return an interval with the start from this interval and the specified end * @throws IllegalArgumentException if the resulting interval has end before start */ public Interval withEnd(ReadableInstant end) { long endMillis = DateTimeUtils.getInstantMillis(end); return withEndMillis(endMillis); } //----------------------------------------------------------------------- /** * Creates a new interval with the specified duration after the start instant. * * @param duration the duration to add to the start to get the new end instant, null means zero * @return an interval with the start from this interval and a calculated end * @throws IllegalArgumentException if the duration is negative */ public Interval withDurationAfterStart(ReadableDuration duration) { long durationMillis = DateTimeUtils.getDurationMillis(duration); if (durationMillis == toDurationMillis()) { return this; } Chronology chrono = getChronology(); long startMillis = getStartMillis(); long endMillis = chrono.add(startMillis, durationMillis, 1); return new Interval(startMillis, endMillis, chrono); } /** * Creates a new interval with the specified duration before the end instant. * * @param duration the duration to subtract from the end to get the new start instant, null means zero * @return an interval with the end from this interval and a calculated start * @throws IllegalArgumentException if the duration is negative */ public Interval withDurationBeforeEnd(ReadableDuration duration) { long durationMillis = DateTimeUtils.getDurationMillis(duration); if (durationMillis == toDurationMillis()) { return this; } Chronology chrono = getChronology(); long endMillis = getEndMillis(); long startMillis = chrono.add(endMillis, durationMillis, -1); return new Interval(startMillis, endMillis, chrono); } //----------------------------------------------------------------------- /** * Creates a new interval with the specified period after the start instant. * * @param period the period to add to the start to get the new end instant, null means zero * @return an interval with the start from this interval and a calculated end * @throws IllegalArgumentException if the period is negative */ public Interval withPeriodAfterStart(ReadablePeriod period) { if (period == null) { return withDurationAfterStart(null); } Chronology chrono = getChronology(); long startMillis = getStartMillis(); long endMillis = chrono.add(period, startMillis, 1); return new Interval(startMillis, endMillis, chrono); } /** * Creates a new interval with the specified period before the end instant. * * @param period the period to subtract from the end to get the new start instant, null means zero * @return an interval with the end from this interval and a calculated start * @throws IllegalArgumentException if the period is negative */ public Interval withPeriodBeforeEnd(ReadablePeriod period) { if (period == null) { return withDurationBeforeEnd(null); } Chronology chrono = getChronology(); long endMillis = getEndMillis(); long startMillis = chrono.add(period, endMillis, -1); return new Interval(startMillis, endMillis, chrono); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy