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

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

The newest version!
/*
 *  Copyright 2001-2010 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.convert.FromString;
import org.joda.time.base.BasePeriod;
import org.joda.time.chrono.ISOChronology;
import org.joda.time.field.FieldUtils;
import org.joda.time.format.ISOPeriodFormat;
import org.joda.time.format.PeriodFormatter;

/**
 * An immutable time period specifying a set of duration field values.
 * 

* A time period is divided into a number of fields, such as hours and seconds. * Which fields are supported is defined by the PeriodType class. * The default is the standard period type, which supports years, months, weeks, days, * hours, minutes, seconds and millis. *

* When this time period is added to an instant, the effect is of adding each field in turn. * As a result, this takes into account daylight savings time. * Adding a time period of 1 day to the day before daylight savings starts will only add * 23 hours rather than 24 to ensure that the time remains the same. * If this is not the behaviour you want, then see {@link Duration}. *

* The definition of a period also affects the equals method. A period of 1 * day is not equal to a period of 24 hours, nor 1 hour equal to 60 minutes. * This is because periods represent an abstracted definition of a time period * (eg. a day may not actually be 24 hours, it might be 23 or 25 at daylight * savings boundary). To compare the actual duration of two periods, convert * both to durations using toDuration, an operation that emphasises that the * result may differ according to the date you choose. *

* Period is thread-safe and immutable, provided that the PeriodType is as well. * All standard PeriodType classes supplied are thread-safe and immutable. * * @author Brian S O'Neill * @author Stephen Colebourne * @since 1.0 * @see MutablePeriod */ public final class Period extends BasePeriod implements ReadablePeriod, Serializable { /** * A period of zero length and standard period type. * @since 1.4 */ public static final Period ZERO = new Period(); /** Serialization version */ private static final long serialVersionUID = 741052353876488155L; //----------------------------------------------------------------------- /** * Parses a {@code Period} from the specified string. *

* This uses {@link ISOPeriodFormat#standard()}. * * @param str the string to parse, not null * @since 2.0 */ @FromString public static Period parse(String str) { return parse(str, ISOPeriodFormat.standard()); } /** * Parses a {@code Period} from the specified string using a formatter. * * @param str the string to parse, not null * @param formatter the formatter to use, not null * @since 2.0 */ public static Period parse(String str, PeriodFormatter formatter) { return formatter.parsePeriod(str); } //----------------------------------------------------------------------- /** * Create a period with a specified number of years. *

* The standard period type is used, thus you can add other fields such * as months or days using the withXxx() methods. * For example, Period.years(2).withMonths(6); *

* If you want a year-based period that cannot have other fields added, * then you should consider using {@link Years}. * * @param years the amount of years in this period * @return the period */ public static Period years(int years) { return new Period(new int[] {years, 0, 0, 0, 0, 0, 0, 0, 0}, PeriodType.standard()); } /** * Create a period with a specified number of months. *

* The standard period type is used, thus you can add other fields such * as years or days using the withXxx() methods. * For example, Period.months(2).withDays(6); *

* If you want a month-based period that cannot have other fields added, * then you should consider using {@link Months}. * * @param months the amount of months in this period * @return the period */ public static Period months(int months) { return new Period(new int[] {0, months, 0, 0, 0, 0, 0, 0}, PeriodType.standard()); } /** * Create a period with a specified number of weeks. *

* The standard period type is used, thus you can add other fields such * as months or days using the withXxx() methods. * For example, Period.weeks(2).withDays(6); *

* If you want a week-based period that cannot have other fields added, * then you should consider using {@link Weeks}. * * @param weeks the amount of weeks in this period * @return the period */ public static Period weeks(int weeks) { return new Period(new int[] {0, 0, weeks, 0, 0, 0, 0, 0}, PeriodType.standard()); } /** * Create a period with a specified number of days. *

* The standard period type is used, thus you can add other fields such * as months or weeks using the withXxx() methods. * For example, Period.days(2).withHours(6); *

* If you want a day-based period that cannot have other fields added, * then you should consider using {@link Days}. * * @param days the amount of days in this period * @return the period */ public static Period days(int days) { return new Period(new int[] {0, 0, 0, days, 0, 0, 0, 0}, PeriodType.standard()); } /** * Create a period with a specified number of hours. *

* The standard period type is used, thus you can add other fields such * as months or days using the withXxx() methods. * For example, Period.hours(2).withMinutes(30); *

* If you want a hour-based period that cannot have other fields added, * then you should consider using {@link Hours}. * * @param hours the amount of hours in this period * @return the period */ public static Period hours(int hours) { return new Period(new int[] {0, 0, 0, 0, hours, 0, 0, 0}, PeriodType.standard()); } /** * Create a period with a specified number of minutes. *

* The standard period type is used, thus you can add other fields such * as days or hours using the withXxx() methods. * For example, Period.minutes(2).withSeconds(30); *

* If you want a minute-based period that cannot have other fields added, * then you should consider using {@link Minutes}. * * @param minutes the amount of minutes in this period * @return the period */ public static Period minutes(int minutes) { return new Period(new int[] {0, 0, 0, 0, 0, minutes, 0, 0}, PeriodType.standard()); } /** * Create a period with a specified number of seconds. *

* The standard period type is used, thus you can add other fields such * as days or hours using the withXxx() methods. * For example, Period.seconds(2).withMillis(30); *

* If you want a second-based period that cannot have other fields added, * then you should consider using {@link Seconds}. * * @param seconds the amount of seconds in this period * @return the period */ public static Period seconds(int seconds) { return new Period(new int[] {0, 0, 0, 0, 0, 0, seconds, 0}, PeriodType.standard()); } /** * Create a period with a specified number of millis. *

* The standard period type is used, thus you can add other fields such * as days or hours using the withXxx() methods. * For example, Period.millis(20).withSeconds(30); * * @param millis the amount of millis in this period * @return the period */ public static Period millis(int millis) { return new Period(new int[] {0, 0, 0, 0, 0, 0, 0, millis}, PeriodType.standard()); } //----------------------------------------------------------------------- /** * Creates a period from two partially specified times, calculating * by field difference. *

* The two partials must contain the same fields, thus you can specify * two LocalDate objects, or two LocalTime objects, * but not one of each. Also, the partial may not contain overlapping * fields, such as dayOfWeek and dayOfMonth. *

* Calculation by field difference works by extracting the difference * one field at a time and not wrapping into other fields. * Thus 2005-06-09/2007-04-12 will yield P1Y-2M3D. *

* For example, you have an event that always runs from the 27th of * each month to the 2nd of the next month. If you calculate this * period using a standard constructor, then you will get between * P3D and P6D depending on the month. If you use this method, then * you will get P1M-25D. This field-difference based period can * be successfully applied to each month of the year to obtain the * correct end date for a given start date. * * @param start the start of the period, must not be null * @param end the end of the period, must not be null * @throws IllegalArgumentException if the partials are null or invalid * @since 1.1 */ public static Period fieldDifference(ReadablePartial start, ReadablePartial end) { if (start == null || end == null) { throw new IllegalArgumentException("ReadablePartial objects must not be null"); } if (start.size() != end.size()) { throw new IllegalArgumentException("ReadablePartial objects must have the same set of fields"); } DurationFieldType[] types = new DurationFieldType[start.size()]; int[] values = new int[start.size()]; for (int i = 0, isize = start.size(); i < isize; i++) { if (start.getFieldType(i) != end.getFieldType(i)) { throw new IllegalArgumentException("ReadablePartial objects must have the same set of fields"); } types[i] = start.getFieldType(i).getDurationType(); if (i > 0 && types[i - 1] == types[i]) { throw new IllegalArgumentException("ReadablePartial objects must not have overlapping fields"); } values[i] = end.getValue(i) - start.getValue(i); } return new Period(values, PeriodType.forFields(types)); } //----------------------------------------------------------------------- /** * Creates a new empty period with the standard set of fields. *

* One way to initialise a period is as follows: *

     * Period = new Period().withYears(6).withMonths(3).withSeconds(23);
     * 
* Bear in mind that this creates four period instances in total, three of * which are immediately discarded. * The alterative is more efficient, but less readable: *
     * Period = new Period(6, 3, 0, 0, 0, 0, 23, 0);
     * 
* The following is also slightly less wasteful: *
     * Period = Period.years(6).withMonths(3).withSeconds(23);
     * 
*/ public Period() { super(0L, null, null); } /** * Create a period from a set of field values using the standard set of fields. * Note that the parameters specify the time fields hours, minutes, * seconds and millis, not the date fields. * * @param hours amount of hours in this period * @param minutes amount of minutes in this period * @param seconds amount of seconds in this period * @param millis amount of milliseconds in this period */ public Period(int hours, int minutes, int seconds, int millis) { super(0, 0, 0, 0, hours, minutes, seconds, millis, PeriodType.standard()); } /** * Create a period from a set of field values using the standard set of fields. * * @param years amount of years in this period * @param months amount of months in this period * @param weeks amount of weeks in this period * @param days amount of days in this period * @param hours amount of hours in this period * @param minutes amount of minutes in this period * @param seconds amount of seconds in this period * @param millis amount of milliseconds in this period */ public Period(int years, int months, int weeks, int days, int hours, int minutes, int seconds, int millis) { super(years, months, weeks, days, hours, minutes, seconds, millis, PeriodType.standard()); } /** * Create a period from a set of field values. *

* There is usually little need to use this constructor. * The period type is used primarily to define how to split an interval into a period. * As this constructor already is split, the period type does no real work. * * @param years amount of years in this period, which must be zero if unsupported * @param months amount of months in this period, which must be zero if unsupported * @param weeks amount of weeks in this period, which must be zero if unsupported * @param days amount of days in this period, which must be zero if unsupported * @param hours amount of hours in this period, which must be zero if unsupported * @param minutes amount of minutes in this period, which must be zero if unsupported * @param seconds amount of seconds in this period, which must be zero if unsupported * @param millis amount of milliseconds in this period, which must be zero if unsupported * @param type which set of fields this period supports, null means AllType * @throws IllegalArgumentException if an unsupported field's value is non-zero */ public Period(int years, int months, int weeks, int days, int hours, int minutes, int seconds, int millis, PeriodType type) { super(years, months, weeks, days, hours, minutes, seconds, millis, type); } /** * Creates a period from the given millisecond duration using the standard * set of fields. *

* Only precise fields in the period type will be used. * For the standard period type this is the time fields only. * Thus the year, month, week and day fields will not be populated. *

* If the duration is small, less than one day, then this method will perform * as you might expect and split the fields evenly. *

* If the duration is larger than one day then all the remaining duration will * be stored in the largest available precise field, hours in this case. *

* For example, a duration equal to (365 + 60 + 5) days will be converted to * ((365 + 60 + 5) * 24) hours by this constructor. *

* For more control over the conversion process, you have two options: *

    *
  • convert the duration to an {@link Interval}, and from there obtain the period *
  • specify a period type that contains precise definitions of the day and larger * fields, such as UTC *
* * @param duration the duration, in milliseconds */ public Period(long duration) { super(duration); } /** * Creates a period from the given millisecond duration. *

* Only precise fields in the period type will be used. * Imprecise fields will not be populated. *

* If the duration is small then this method will perform * as you might expect and split the fields evenly. *

* If the duration is large then all the remaining duration will * be stored in the largest available precise field. * For details as to which fields are precise, review the period type javadoc. * * @param duration the duration, in milliseconds * @param type which set of fields this period supports, null means standard */ public Period(long duration, PeriodType type) { super(duration, type, null); } /** * Creates a period from the given millisecond duration using the standard * set of fields. *

* Only precise fields in the period type will be used. * Imprecise fields will not be populated. *

* If the duration is small then this method will perform * as you might expect and split the fields evenly. *

* If the duration is large then all the remaining duration will * be stored in the largest available precise field. * For details as to which fields are precise, review the period type javadoc. * * @param duration the duration, in milliseconds * @param chronology the chronology to use to split the duration, null means ISO default */ public Period(long duration, Chronology chronology) { super(duration, null, chronology); } /** * Creates a period from the given millisecond duration. *

* Only precise fields in the period type will be used. * Imprecise fields will not be populated. *

* If the duration is small then this method will perform * as you might expect and split the fields evenly. *

* If the duration is large then all the remaining duration will * be stored in the largest available precise field. * For details as to which fields are precise, review the period type javadoc. * * @param duration the duration, in milliseconds * @param type which set of fields this period supports, null means standard * @param chronology the chronology to use to split the duration, null means ISO default */ public Period(long duration, PeriodType type, Chronology chronology) { super(duration, type, chronology); } /** * Creates a period from the given interval endpoints using the standard * set of fields. * * @param startInstant interval start, in milliseconds * @param endInstant interval end, in milliseconds */ public Period(long startInstant, long endInstant) { super(startInstant, endInstant, null, null); } /** * Creates a period from the given interval endpoints. * * @param startInstant interval start, in milliseconds * @param endInstant interval end, in milliseconds * @param type which set of fields this period supports, null means standard */ public Period(long startInstant, long endInstant, PeriodType type) { super(startInstant, endInstant, type, null); } /** * Creates a period from the given interval endpoints using the standard * set of fields. * * @param startInstant interval start, in milliseconds * @param endInstant interval end, in milliseconds * @param chrono the chronology to use, null means ISO in default zone */ public Period(long startInstant, long endInstant, Chronology chrono) { super(startInstant, endInstant, null, chrono); } /** * Creates a period from the given interval endpoints. * * @param startInstant interval start, in milliseconds * @param endInstant interval end, in milliseconds * @param type which set of fields this period supports, null means standard * @param chrono the chronology to use, null means ISO in default zone */ public Period(long startInstant, long endInstant, PeriodType type, Chronology chrono) { super(startInstant, endInstant, type, chrono); } /** * Creates a period from the given interval endpoints using the standard * set of fields. * * @param startInstant interval start, null means now * @param endInstant interval end, null means now */ public Period(ReadableInstant startInstant, ReadableInstant endInstant) { super(startInstant, endInstant, null); } /** * Creates a period from the given interval endpoints. * * @param startInstant interval start, null means now * @param endInstant interval end, null means now * @param type which set of fields this period supports, null means standard */ public Period(ReadableInstant startInstant, ReadableInstant endInstant, PeriodType type) { super(startInstant, endInstant, type); } /** * Creates a period from two partially specified times. *

* The two partials must contain the same fields, thus you can specify * two LocalDate objects, or two LocalTime objects, * but not one of each. * As these are Partial objects, time zones have no effect on the result. *

* The two partials must also both be contiguous - see * {@link DateTimeUtils#isContiguous(ReadablePartial)} for a definition. * Both LocalDate and LocalTime are contiguous. *

* An alternative way of constructing a Period from two Partials * is {@link #fieldDifference(ReadablePartial, ReadablePartial)}. * That method handles all kinds of partials. * * @param start the start of the period, must not be null * @param end the end of the period, must not be null * @throws IllegalArgumentException if the partials are null or invalid * @since 1.1 */ public Period(ReadablePartial start, ReadablePartial end) { super(start, end, null); } /** * Creates a period from two partially specified times. *

* The two partials must contain the same fields, thus you can specify * two LocalDate objects, or two LocalTime objects, * but not one of each. * As these are Partial objects, time zones have no effect on the result. *

* The two partials must also both be contiguous - see * {@link DateTimeUtils#isContiguous(ReadablePartial)} for a definition. * Both LocalDate and LocalTime are contiguous. *

* An alternative way of constructing a Period from two Partials * is {@link #fieldDifference(ReadablePartial, ReadablePartial)}. * That method handles all kinds of partials. * * @param start the start of the period, must not be null * @param end the end of the period, must not be null * @param type which set of fields this period supports, null means standard * @throws IllegalArgumentException if the partials are null or invalid * @since 1.1 */ public Period(ReadablePartial start, ReadablePartial end, PeriodType type) { super(start, end, type); } /** * Creates a period from the given start point and the duration. * * @param startInstant the interval start, null means now * @param duration the duration of the interval, null means zero-length */ public Period(ReadableInstant startInstant, ReadableDuration duration) { super(startInstant, duration, null); } /** * Creates a period from the given start point and the duration. * * @param startInstant the interval start, null means now * @param duration the duration of the interval, null means zero-length * @param type which set of fields this period supports, null means standard */ public Period(ReadableInstant startInstant, ReadableDuration duration, PeriodType type) { super(startInstant, duration, type); } /** * Creates a period from the given duration and end point. * * @param duration the duration of the interval, null means zero-length * @param endInstant the interval end, null means now */ public Period(ReadableDuration duration, ReadableInstant endInstant) { super(duration, endInstant, null); } /** * Creates a period from the given duration and end point. * * @param duration the duration of the interval, null means zero-length * @param endInstant the interval end, null means now * @param type which set of fields this period supports, null means standard */ public Period(ReadableDuration duration, ReadableInstant endInstant, PeriodType type) { super(duration, endInstant, type); } /** * Creates a period by converting or copying from another object. *

* The recognised object types are defined in * {@link org.joda.time.convert.ConverterManager ConverterManager} and * include ReadablePeriod, ReadableInterval and String. * The String formats are described by {@link ISOPeriodFormat#standard()}. * * @param period period to convert * @throws IllegalArgumentException if period is invalid * @throws UnsupportedOperationException if an unsupported field's value is non-zero */ public Period(Object period) { super(period, null, null); } /** * Creates a period by converting or copying from another object. *

* The recognised object types are defined in * {@link org.joda.time.convert.ConverterManager ConverterManager} and * include ReadablePeriod, ReadableInterval and String. * The String formats are described by {@link ISOPeriodFormat#standard()}. * * @param period period to convert * @param type which set of fields this period supports, null means use converter * @throws IllegalArgumentException if period is invalid * @throws UnsupportedOperationException if an unsupported field's value is non-zero */ public Period(Object period, PeriodType type) { super(period, type, null); } /** * Creates a period by converting or copying from another object. *

* The recognised object types are defined in * {@link org.joda.time.convert.ConverterManager ConverterManager} and * include ReadablePeriod, ReadableInterval and String. * The String formats are described by {@link ISOPeriodFormat#standard()}. * * @param period period to convert * @param chrono the chronology to use, null means ISO in default zone * @throws IllegalArgumentException if period is invalid * @throws UnsupportedOperationException if an unsupported field's value is non-zero */ public Period(Object period, Chronology chrono) { super(period, null, chrono); } /** * Creates a period by converting or copying from another object. *

* The recognised object types are defined in * {@link org.joda.time.convert.ConverterManager ConverterManager} and * include ReadablePeriod, ReadableInterval and String. * The String formats are described by {@link ISOPeriodFormat#standard()}. * * @param period period to convert * @param type which set of fields this period supports, null means use converter * @param chrono the chronology to use, null means ISO in default zone * @throws IllegalArgumentException if period is invalid * @throws UnsupportedOperationException if an unsupported field's value is non-zero */ public Period(Object period, PeriodType type, Chronology chrono) { super(period, type, chrono); } /** * Constructor used when we trust ourselves. * * @param values the values to use, not null, not cloned * @param type which set of fields this period supports, not null */ private Period(int[] values, PeriodType type) { super(values, type); } //----------------------------------------------------------------------- /** * Get this period as an immutable Period object * by returning this. * * @return this */ public Period toPeriod() { return this; } //----------------------------------------------------------------------- /** * Gets the years field part of the period. * * @return the number of years in the period, zero if unsupported */ public int getYears() { return getPeriodType().getIndexedField(this, PeriodType.YEAR_INDEX); } /** * Gets the months field part of the period. * * @return the number of months in the period, zero if unsupported */ public int getMonths() { return getPeriodType().getIndexedField(this, PeriodType.MONTH_INDEX); } /** * Gets the weeks field part of the period. * * @return the number of weeks in the period, zero if unsupported */ public int getWeeks() { return getPeriodType().getIndexedField(this, PeriodType.WEEK_INDEX); } /** * Gets the days field part of the period. * * @return the number of days in the period, zero if unsupported */ public int getDays() { return getPeriodType().getIndexedField(this, PeriodType.DAY_INDEX); } //----------------------------------------------------------------------- /** * Gets the hours field part of the period. * * @return the number of hours in the period, zero if unsupported */ public int getHours() { return getPeriodType().getIndexedField(this, PeriodType.HOUR_INDEX); } /** * Gets the minutes field part of the period. * * @return the number of minutes in the period, zero if unsupported */ public int getMinutes() { return getPeriodType().getIndexedField(this, PeriodType.MINUTE_INDEX); } /** * Gets the seconds field part of the period. * * @return the number of seconds in the period, zero if unsupported */ public int getSeconds() { return getPeriodType().getIndexedField(this, PeriodType.SECOND_INDEX); } /** * Gets the millis field part of the period. * * @return the number of millis in the period, zero if unsupported */ public int getMillis() { return getPeriodType().getIndexedField(this, PeriodType.MILLI_INDEX); } //----------------------------------------------------------------------- /** * Creates a new Period instance with the same field values but * different PeriodType. *

* This period instance is immutable and unaffected by this method call. * * @param type the period type to use, null means standard * @return the new period instance * @throws IllegalArgumentException if the new period won't accept all of the current fields */ public Period withPeriodType(PeriodType type) { type = DateTimeUtils.getPeriodType(type); if (type.equals(getPeriodType())) { return this; } return new Period(this, type); } /** * Creates a new Period instance with the fields from the specified period * copied on top of those from this period. *

* This period instance is immutable and unaffected by this method call. * * @param period the period to copy from, null ignored * @return the new period instance * @throws IllegalArgumentException if a field type is unsupported */ public Period withFields(ReadablePeriod period) { if (period == null) { return this; } int[] newValues = getValues(); // cloned newValues = super.mergePeriodInto(newValues, period); return new Period(newValues, getPeriodType()); } //----------------------------------------------------------------------- /** * Creates a new Period instance with the specified field set to a new value. *

* This period instance is immutable and unaffected by this method call. * * @param field the field to set, not null * @param value the value to set to * @return the new period instance * @throws IllegalArgumentException if the field type is null or unsupported */ public Period withField(DurationFieldType field, int value) { if (field == null) { throw new IllegalArgumentException("Field must not be null"); } int[] newValues = getValues(); // cloned super.setFieldInto(newValues, field, value); return new Period(newValues, getPeriodType()); } /** * Creates a new Period instance with the valueToAdd added to the specified field. *

* This period instance is immutable and unaffected by this method call. * * @param field the field to set, not null * @param value the value to add * @return the new period instance * @throws IllegalArgumentException if the field type is null or unsupported */ public Period withFieldAdded(DurationFieldType field, int value) { if (field == null) { throw new IllegalArgumentException("Field must not be null"); } if (value == 0) { return this; } int[] newValues = getValues(); // cloned super.addFieldInto(newValues, field, value); return new Period(newValues, getPeriodType()); } //----------------------------------------------------------------------- /** * Returns a new period with the specified number of years. *

* This period instance is immutable and unaffected by this method call. * * @param years the amount of years to add, may be negative * @return the new period with the increased years * @throws UnsupportedOperationException if the field is not supported */ public Period withYears(int years) { int[] values = getValues(); // cloned getPeriodType().setIndexedField(this, PeriodType.YEAR_INDEX, values, years); return new Period(values, getPeriodType()); } /** * Returns a new period with the specified number of months. *

* This period instance is immutable and unaffected by this method call. * * @param months the amount of months to add, may be negative * @return the new period with the increased months * @throws UnsupportedOperationException if the field is not supported */ public Period withMonths(int months) { int[] values = getValues(); // cloned getPeriodType().setIndexedField(this, PeriodType.MONTH_INDEX, values, months); return new Period(values, getPeriodType()); } /** * Returns a new period with the specified number of weeks. *

* This period instance is immutable and unaffected by this method call. * * @param weeks the amount of weeks to add, may be negative * @return the new period with the increased weeks * @throws UnsupportedOperationException if the field is not supported */ public Period withWeeks(int weeks) { int[] values = getValues(); // cloned getPeriodType().setIndexedField(this, PeriodType.WEEK_INDEX, values, weeks); return new Period(values, getPeriodType()); } /** * Returns a new period with the specified number of days. *

* This period instance is immutable and unaffected by this method call. * * @param days the amount of days to add, may be negative * @return the new period with the increased days * @throws UnsupportedOperationException if the field is not supported */ public Period withDays(int days) { int[] values = getValues(); // cloned getPeriodType().setIndexedField(this, PeriodType.DAY_INDEX, values, days); return new Period(values, getPeriodType()); } /** * Returns a new period with the specified number of hours. *

* This period instance is immutable and unaffected by this method call. * * @param hours the amount of hours to add, may be negative * @return the new period with the increased hours * @throws UnsupportedOperationException if the field is not supported */ public Period withHours(int hours) { int[] values = getValues(); // cloned getPeriodType().setIndexedField(this, PeriodType.HOUR_INDEX, values, hours); return new Period(values, getPeriodType()); } /** * Returns a new period with the specified number of minutes. *

* This period instance is immutable and unaffected by this method call. * * @param minutes the amount of minutes to add, may be negative * @return the new period with the increased minutes * @throws UnsupportedOperationException if the field is not supported */ public Period withMinutes(int minutes) { int[] values = getValues(); // cloned getPeriodType().setIndexedField(this, PeriodType.MINUTE_INDEX, values, minutes); return new Period(values, getPeriodType()); } /** * Returns a new period with the specified number of seconds. *

* This period instance is immutable and unaffected by this method call. * * @param seconds the amount of seconds to add, may be negative * @return the new period with the increased seconds * @throws UnsupportedOperationException if the field is not supported */ public Period withSeconds(int seconds) { int[] values = getValues(); // cloned getPeriodType().setIndexedField(this, PeriodType.SECOND_INDEX, values, seconds); return new Period(values, getPeriodType()); } /** * Returns a new period with the specified number of millis. *

* This period instance is immutable and unaffected by this method call. * * @param millis the amount of millis to add, may be negative * @return the new period with the increased millis * @throws UnsupportedOperationException if the field is not supported */ public Period withMillis(int millis) { int[] values = getValues(); // cloned getPeriodType().setIndexedField(this, PeriodType.MILLI_INDEX, values, millis); return new Period(values, getPeriodType()); } //----------------------------------------------------------------------- /** * Returns a new period with the specified period added. *

* Each field of the period is added separately. Thus a period of * 2 hours 30 minutes plus 3 hours 40 minutes will produce a result * of 5 hours 70 minutes - see {@link #normalizedStandard()}. *

* If the period being added contains a non-zero amount for a field that * is not supported in this period then an exception is thrown. *

* This period instance is immutable and unaffected by this method call. * * @param period the period to add, null adds zero and returns this * @return the new updated period * @throws UnsupportedOperationException if any field is not supported * @since 1.5 */ public Period plus(ReadablePeriod period) { if (period == null) { return this; } int[] values = getValues(); // cloned getPeriodType().addIndexedField(this, PeriodType.YEAR_INDEX, values, period.get(DurationFieldType.YEARS_TYPE)); getPeriodType().addIndexedField(this, PeriodType.MONTH_INDEX, values, period.get(DurationFieldType.MONTHS_TYPE)); getPeriodType().addIndexedField(this, PeriodType.WEEK_INDEX, values, period.get(DurationFieldType.WEEKS_TYPE)); getPeriodType().addIndexedField(this, PeriodType.DAY_INDEX, values, period.get(DurationFieldType.DAYS_TYPE)); getPeriodType().addIndexedField(this, PeriodType.HOUR_INDEX, values, period.get(DurationFieldType.HOURS_TYPE)); getPeriodType().addIndexedField(this, PeriodType.MINUTE_INDEX, values, period.get(DurationFieldType.MINUTES_TYPE)); getPeriodType().addIndexedField(this, PeriodType.SECOND_INDEX, values, period.get(DurationFieldType.SECONDS_TYPE)); getPeriodType().addIndexedField(this, PeriodType.MILLI_INDEX, values, period.get(DurationFieldType.MILLIS_TYPE)); return new Period(values, getPeriodType()); } //----------------------------------------------------------------------- /** * Returns a new period with the specified number of years added. *

* This period instance is immutable and unaffected by this method call. * * @param years the amount of years to add, may be negative * @return the new period with the increased years * @throws UnsupportedOperationException if the field is not supported */ public Period plusYears(int years) { if (years == 0) { return this; } int[] values = getValues(); // cloned getPeriodType().addIndexedField(this, PeriodType.YEAR_INDEX, values, years); return new Period(values, getPeriodType()); } /** * Returns a new period plus the specified number of months added. *

* This period instance is immutable and unaffected by this method call. * * @param months the amount of months to add, may be negative * @return the new period plus the increased months * @throws UnsupportedOperationException if the field is not supported */ public Period plusMonths(int months) { if (months == 0) { return this; } int[] values = getValues(); // cloned getPeriodType().addIndexedField(this, PeriodType.MONTH_INDEX, values, months); return new Period(values, getPeriodType()); } /** * Returns a new period plus the specified number of weeks added. *

* This period instance is immutable and unaffected by this method call. * * @param weeks the amount of weeks to add, may be negative * @return the new period plus the increased weeks * @throws UnsupportedOperationException if the field is not supported */ public Period plusWeeks(int weeks) { if (weeks == 0) { return this; } int[] values = getValues(); // cloned getPeriodType().addIndexedField(this, PeriodType.WEEK_INDEX, values, weeks); return new Period(values, getPeriodType()); } /** * Returns a new period plus the specified number of days added. *

* This period instance is immutable and unaffected by this method call. * * @param days the amount of days to add, may be negative * @return the new period plus the increased days * @throws UnsupportedOperationException if the field is not supported */ public Period plusDays(int days) { if (days == 0) { return this; } int[] values = getValues(); // cloned getPeriodType().addIndexedField(this, PeriodType.DAY_INDEX, values, days); return new Period(values, getPeriodType()); } /** * Returns a new period plus the specified number of hours added. *

* This period instance is immutable and unaffected by this method call. * * @param hours the amount of hours to add, may be negative * @return the new period plus the increased hours * @throws UnsupportedOperationException if the field is not supported */ public Period plusHours(int hours) { if (hours == 0) { return this; } int[] values = getValues(); // cloned getPeriodType().addIndexedField(this, PeriodType.HOUR_INDEX, values, hours); return new Period(values, getPeriodType()); } /** * Returns a new period plus the specified number of minutes added. *

* This period instance is immutable and unaffected by this method call. * * @param minutes the amount of minutes to add, may be negative * @return the new period plus the increased minutes * @throws UnsupportedOperationException if the field is not supported */ public Period plusMinutes(int minutes) { if (minutes == 0) { return this; } int[] values = getValues(); // cloned getPeriodType().addIndexedField(this, PeriodType.MINUTE_INDEX, values, minutes); return new Period(values, getPeriodType()); } /** * Returns a new period plus the specified number of seconds added. *

* This period instance is immutable and unaffected by this method call. * * @param seconds the amount of seconds to add, may be negative * @return the new period plus the increased seconds * @throws UnsupportedOperationException if the field is not supported */ public Period plusSeconds(int seconds) { if (seconds == 0) { return this; } int[] values = getValues(); // cloned getPeriodType().addIndexedField(this, PeriodType.SECOND_INDEX, values, seconds); return new Period(values, getPeriodType()); } /** * Returns a new period plus the specified number of millis added. *

* This period instance is immutable and unaffected by this method call. * * @param millis the amount of millis to add, may be negative * @return the new period plus the increased millis * @throws UnsupportedOperationException if the field is not supported */ public Period plusMillis(int millis) { if (millis == 0) { return this; } int[] values = getValues(); // cloned getPeriodType().addIndexedField(this, PeriodType.MILLI_INDEX, values, millis); return new Period(values, getPeriodType()); } //----------------------------------------------------------------------- /** * Returns a new period with the specified period subtracted. *

* Each field of the period is subtracted separately. Thus a period of * 3 hours 30 minutes minus 2 hours 40 minutes will produce a result * of 1 hour and -10 minutes - see {@link #normalizedStandard()}. *

* If the period being added contains a non-zero amount for a field that * is not supported in this period then an exception is thrown. *

* This period instance is immutable and unaffected by this method call. * * @param period the period to add, null adds zero and returns this * @return the new updated period * @throws UnsupportedOperationException if any field is not supported * @since 1.5 */ public Period minus(ReadablePeriod period) { if (period == null) { return this; } int[] values = getValues(); // cloned getPeriodType().addIndexedField(this, PeriodType.YEAR_INDEX, values, -period.get(DurationFieldType.YEARS_TYPE)); getPeriodType().addIndexedField(this, PeriodType.MONTH_INDEX, values, -period.get(DurationFieldType.MONTHS_TYPE)); getPeriodType().addIndexedField(this, PeriodType.WEEK_INDEX, values, -period.get(DurationFieldType.WEEKS_TYPE)); getPeriodType().addIndexedField(this, PeriodType.DAY_INDEX, values, -period.get(DurationFieldType.DAYS_TYPE)); getPeriodType().addIndexedField(this, PeriodType.HOUR_INDEX, values, -period.get(DurationFieldType.HOURS_TYPE)); getPeriodType().addIndexedField(this, PeriodType.MINUTE_INDEX, values, -period.get(DurationFieldType.MINUTES_TYPE)); getPeriodType().addIndexedField(this, PeriodType.SECOND_INDEX, values, -period.get(DurationFieldType.SECONDS_TYPE)); getPeriodType().addIndexedField(this, PeriodType.MILLI_INDEX, values, -period.get(DurationFieldType.MILLIS_TYPE)); return new Period(values, getPeriodType()); } //----------------------------------------------------------------------- /** * Returns a new period with the specified number of years taken away. *

* This period instance is immutable and unaffected by this method call. * * @param years the amount of years to take away, may be negative * @return the new period with the increased years * @throws UnsupportedOperationException if the field is not supported */ public Period minusYears(int years) { return plusYears(-years); } /** * Returns a new period minus the specified number of months taken away. *

* This period instance is immutable and unaffected by this method call. * * @param months the amount of months to take away, may be negative * @return the new period minus the increased months * @throws UnsupportedOperationException if the field is not supported */ public Period minusMonths(int months) { return plusMonths(-months); } /** * Returns a new period minus the specified number of weeks taken away. *

* This period instance is immutable and unaffected by this method call. * * @param weeks the amount of weeks to take away, may be negative * @return the new period minus the increased weeks * @throws UnsupportedOperationException if the field is not supported */ public Period minusWeeks(int weeks) { return plusWeeks(-weeks); } /** * Returns a new period minus the specified number of days taken away. *

* This period instance is immutable and unaffected by this method call. * * @param days the amount of days to take away, may be negative * @return the new period minus the increased days * @throws UnsupportedOperationException if the field is not supported */ public Period minusDays(int days) { return plusDays(-days); } /** * Returns a new period minus the specified number of hours taken away. *

* This period instance is immutable and unaffected by this method call. * * @param hours the amount of hours to take away, may be negative * @return the new period minus the increased hours * @throws UnsupportedOperationException if the field is not supported */ public Period minusHours(int hours) { return plusHours(-hours); } /** * Returns a new period minus the specified number of minutes taken away. *

* This period instance is immutable and unaffected by this method call. * * @param minutes the amount of minutes to take away, may be negative * @return the new period minus the increased minutes * @throws UnsupportedOperationException if the field is not supported */ public Period minusMinutes(int minutes) { return plusMinutes(-minutes); } /** * Returns a new period minus the specified number of seconds taken away. *

* This period instance is immutable and unaffected by this method call. * * @param seconds the amount of seconds to take away, may be negative * @return the new period minus the increased seconds * @throws UnsupportedOperationException if the field is not supported */ public Period minusSeconds(int seconds) { return plusSeconds(-seconds); } /** * Returns a new period minus the specified number of millis taken away. *

* This period instance is immutable and unaffected by this method call. * * @param millis the amount of millis to take away, may be negative * @return the new period minus the increased millis * @throws UnsupportedOperationException if the field is not supported */ public Period minusMillis(int millis) { return plusMillis(-millis); } //----------------------------------------------------------------------- /** * Returns a new instance with each element in this period multiplied * by the specified scalar. * * @param scalar the scalar to multiply by, not null * @return a {@code Period} based on this period with the amounts multiplied by the scalar, never null * @throws ArithmeticException if the capacity of any field is exceeded * @since 2.1 */ public Period multipliedBy(int scalar) { if (this == ZERO || scalar == 1) { return this; } int[] values = getValues(); // cloned for (int i = 0; i < values.length; i++) { values[i] = FieldUtils.safeMultiply(values[i], scalar); } return new Period(values, getPeriodType()); } /** * Returns a new instance with each amount in this period negated. * * @return a {@code Period} based on this period with the amounts negated, never null * @throws ArithmeticException if any field has the minimum value * @since 2.1 */ public Period negated() { return multipliedBy(-1); } //----------------------------------------------------------------------- /** * Converts this period to a period in weeks assuming a * 7 day week, 24 hour day, 60 minute hour and 60 second minute. *

* This method allows you to convert between different types of period. * However to achieve this it makes the assumption that all * weeks are 7 days, all days are 24 hours, all hours are 60 minutes and * all minutes are 60 seconds. This is not true when daylight savings time * is considered, and may also not be true for some unusual chronologies. * However, it is included as it is a useful operation for many * applications and business rules. *

* If the period contains years or months, an exception will be thrown. * * @return a period representing the number of standard weeks in this period * @throws UnsupportedOperationException if the period contains years or months * @throws ArithmeticException if the number of weeks is too large to be represented * @since 1.5 */ public Weeks toStandardWeeks() { checkYearsAndMonths("Weeks"); long millis = getMillis(); // assign to a long millis += ((long) getSeconds()) * DateTimeConstants.MILLIS_PER_SECOND; millis += ((long) getMinutes()) * DateTimeConstants.MILLIS_PER_MINUTE; millis += ((long) getHours()) * DateTimeConstants.MILLIS_PER_HOUR; millis += ((long) getDays()) * DateTimeConstants.MILLIS_PER_DAY; long weeks = ((long) getWeeks()) + millis / DateTimeConstants.MILLIS_PER_WEEK; return Weeks.weeks(FieldUtils.safeToInt(weeks)); } /** * Converts this period to a period in days assuming a * 7 day week, 24 hour day, 60 minute hour and 60 second minute. *

* This method allows you to convert between different types of period. * However to achieve this it makes the assumption that all * weeks are 7 days, all days are 24 hours, all hours are 60 minutes and * all minutes are 60 seconds. This is not true when daylight savings time * is considered, and may also not be true for some unusual chronologies. * However, it is included as it is a useful operation for many * applications and business rules. *

* If the period contains years or months, an exception will be thrown. * * @return a period representing the number of standard days in this period * @throws UnsupportedOperationException if the period contains years or months * @throws ArithmeticException if the number of days is too large to be represented * @since 1.5 */ public Days toStandardDays() { checkYearsAndMonths("Days"); long millis = getMillis(); // assign to a long millis += ((long) getSeconds()) * DateTimeConstants.MILLIS_PER_SECOND; millis += ((long) getMinutes()) * DateTimeConstants.MILLIS_PER_MINUTE; millis += ((long) getHours()) * DateTimeConstants.MILLIS_PER_HOUR; long days = millis / DateTimeConstants.MILLIS_PER_DAY; days = FieldUtils.safeAdd(days, getDays()); days = FieldUtils.safeAdd(days, ((long) getWeeks()) * ((long) DateTimeConstants.DAYS_PER_WEEK)); return Days.days(FieldUtils.safeToInt(days)); } /** * Converts this period to a period in hours assuming a * 7 day week, 24 hour day, 60 minute hour and 60 second minute. *

* This method allows you to convert between different types of period. * However to achieve this it makes the assumption that all * weeks are 7 days, all days are 24 hours, all hours are 60 minutes and * all minutes are 60 seconds. This is not true when daylight savings time * is considered, and may also not be true for some unusual chronologies. * However, it is included as it is a useful operation for many * applications and business rules. *

* If the period contains years or months, an exception will be thrown. * * @return a period representing the number of standard hours in this period * @throws UnsupportedOperationException if the period contains years or months * @throws ArithmeticException if the number of hours is too large to be represented * @since 1.5 */ public Hours toStandardHours() { checkYearsAndMonths("Hours"); long millis = getMillis(); // assign to a long millis += ((long) getSeconds()) * DateTimeConstants.MILLIS_PER_SECOND; millis += ((long) getMinutes()) * DateTimeConstants.MILLIS_PER_MINUTE; long hours = millis / DateTimeConstants.MILLIS_PER_HOUR; hours = FieldUtils.safeAdd(hours, getHours()); hours = FieldUtils.safeAdd(hours, ((long) getDays()) * ((long) DateTimeConstants.HOURS_PER_DAY)); hours = FieldUtils.safeAdd(hours, ((long) getWeeks()) * ((long) DateTimeConstants.HOURS_PER_WEEK)); return Hours.hours(FieldUtils.safeToInt(hours)); } /** * Converts this period to a period in minutes assuming a * 7 day week, 24 hour day, 60 minute hour and 60 second minute. *

* This method allows you to convert between different types of period. * However to achieve this it makes the assumption that all * weeks are 7 days, all days are 24 hours, all hours are 60 minutes and * all minutes are 60 seconds. This is not true when daylight savings time * is considered, and may also not be true for some unusual chronologies. * However, it is included as it is a useful operation for many * applications and business rules. *

* If the period contains years or months, an exception will be thrown. * * @return a period representing the number of standard minutes in this period * @throws UnsupportedOperationException if the period contains years or months * @throws ArithmeticException if the number of minutes is too large to be represented * @since 1.5 */ public Minutes toStandardMinutes() { checkYearsAndMonths("Minutes"); long millis = getMillis(); // assign to a long millis += ((long) getSeconds()) * DateTimeConstants.MILLIS_PER_SECOND; long minutes = millis / DateTimeConstants.MILLIS_PER_MINUTE; minutes = FieldUtils.safeAdd(minutes, getMinutes()); minutes = FieldUtils.safeAdd(minutes, ((long) getHours()) * ((long) DateTimeConstants.MINUTES_PER_HOUR)); minutes = FieldUtils.safeAdd(minutes, ((long) getDays()) * ((long) DateTimeConstants.MINUTES_PER_DAY)); minutes = FieldUtils.safeAdd(minutes, ((long) getWeeks()) * ((long) DateTimeConstants.MINUTES_PER_WEEK)); return Minutes.minutes(FieldUtils.safeToInt(minutes)); } /** * Converts this period to a period in seconds assuming a * 7 day week, 24 hour day, 60 minute hour and 60 second minute. *

* This method allows you to convert between different types of period. * However to achieve this it makes the assumption that all * weeks are 7 days, all days are 24 hours, all hours are 60 minutes and * all minutes are 60 seconds. This is not true when daylight savings time * is considered, and may also not be true for some unusual chronologies. * However, it is included as it is a useful operation for many * applications and business rules. *

* If the period contains years or months, an exception will be thrown. * * @return a period representing the number of standard seconds in this period * @throws UnsupportedOperationException if the period contains years or months * @throws ArithmeticException if the number of seconds is too large to be represented * @since 1.5 */ public Seconds toStandardSeconds() { checkYearsAndMonths("Seconds"); long seconds = getMillis() / DateTimeConstants.MILLIS_PER_SECOND; seconds = FieldUtils.safeAdd(seconds, getSeconds()); seconds = FieldUtils.safeAdd(seconds, ((long) getMinutes()) * ((long) DateTimeConstants.SECONDS_PER_MINUTE)); seconds = FieldUtils.safeAdd(seconds, ((long) getHours()) * ((long) DateTimeConstants.SECONDS_PER_HOUR)); seconds = FieldUtils.safeAdd(seconds, ((long) getDays()) * ((long) DateTimeConstants.SECONDS_PER_DAY)); seconds = FieldUtils.safeAdd(seconds, ((long) getWeeks()) * ((long) DateTimeConstants.SECONDS_PER_WEEK)); return Seconds.seconds(FieldUtils.safeToInt(seconds)); } //----------------------------------------------------------------------- /** * Converts this period to a duration assuming a * 7 day week, 24 hour day, 60 minute hour and 60 second minute. *

* This method allows you to convert from a period to a duration. * However to achieve this it makes the assumption that all * weeks are 7 days, all days are 24 hours, all hours are 60 minutes and * all minutes are 60 seconds. This is not true when daylight savings time * is considered, and may also not be true for some unusual chronologies. * However, it is included as it is a useful operation for many * applications and business rules. *

* If the period contains years or months, an exception will be thrown. * * @return a duration equivalent to this period * @throws UnsupportedOperationException if the period contains years or months * @since 1.5 */ public Duration toStandardDuration() { checkYearsAndMonths("Duration"); long millis = getMillis(); // no overflow can happen, even with Integer.MAX_VALUEs millis += (((long) getSeconds()) * ((long) DateTimeConstants.MILLIS_PER_SECOND)); millis += (((long) getMinutes()) * ((long) DateTimeConstants.MILLIS_PER_MINUTE)); millis += (((long) getHours()) * ((long) DateTimeConstants.MILLIS_PER_HOUR)); millis += (((long) getDays()) * ((long) DateTimeConstants.MILLIS_PER_DAY)); millis += (((long) getWeeks()) * ((long) DateTimeConstants.MILLIS_PER_WEEK)); return new Duration(millis); } /** * Check that there are no years or months in the period. * * @param destintionType the destination type, not null * @throws UnsupportedOperationException if the period contains years or months */ private void checkYearsAndMonths(String destintionType) { if (getMonths() != 0) { throw new UnsupportedOperationException("Cannot convert to " + destintionType + " as this period contains months and months vary in length"); } if (getYears() != 0) { throw new UnsupportedOperationException("Cannot convert to " + destintionType + " as this period contains years and years vary in length"); } } //----------------------------------------------------------------------- /** * Normalizes this period using standard rules, assuming a 12 month year, * 7 day week, 24 hour day, 60 minute hour and 60 second minute. *

* This method allows you to normalize a period. * However to achieve this it makes the assumption that all years are * 12 months, all weeks are 7 days, all days are 24 hours, * all hours are 60 minutes and all minutes are 60 seconds. This is not * true when daylight savings time is considered, and may also not be true * for some chronologies. However, it is included as it is a useful operation * for many applications and business rules. *

* If the period contains years or months, then the months will be * normalized to be between 0 and 11. The days field and below will be * normalized as necessary, however this will not overflow into the months * field. Thus a period of 1 year 15 months will normalize to 2 years 3 months. * But a period of 1 month 40 days will remain as 1 month 40 days. *

* The result will always have a PeriodType of standard, thus * days will be grouped into weeks. * * @return a normalized period equivalent to this period * @throws ArithmeticException if any field is too large to be represented * @since 1.5 */ public Period normalizedStandard() { return normalizedStandard(PeriodType.standard()); } //----------------------------------------------------------------------- /** * Normalizes this period using standard rules, assuming a 12 month year, * 7 day week, 24 hour day, 60 minute hour and 60 second minute, * providing control over how the result is split into fields. *

* This method allows you to normalize a period. * However to achieve this it makes the assumption that all years are * 12 months, all weeks are 7 days, all days are 24 hours, * all hours are 60 minutes and all minutes are 60 seconds. This is not * true when daylight savings time is considered, and may also not be true * for some chronologies. However, it is included as it is a useful operation * for many applications and business rules. *

* If the period contains years or months, then the months will be * normalized to be between 0 and 11. The days field and below will be * normalized as necessary, however this will not overflow into the months * field. Thus a period of 1 year 15 months will normalize to 2 years 3 months. * But a period of 1 month 40 days will remain as 1 month 40 days. *

* The PeriodType parameter controls how the result is created. It allows * you to omit certain fields from the result if desired. For example, * you may not want the result to include weeks, in which case you pass * in PeriodType.yearMonthDayTime(). * * @param type the period type of the new period, null means standard type * @return a normalized period equivalent to this period * @throws ArithmeticException if any field is too large to be represented * @throws UnsupportedOperationException if this period contains non-zero * years or months but the specified period type does not support them * @since 1.5 */ public Period normalizedStandard(PeriodType type) { long millis = getMillis(); // no overflow can happen, even with Integer.MAX_VALUEs millis += (((long) getSeconds()) * ((long) DateTimeConstants.MILLIS_PER_SECOND)); millis += (((long) getMinutes()) * ((long) DateTimeConstants.MILLIS_PER_MINUTE)); millis += (((long) getHours()) * ((long) DateTimeConstants.MILLIS_PER_HOUR)); millis += (((long) getDays()) * ((long) DateTimeConstants.MILLIS_PER_DAY)); millis += (((long) getWeeks()) * ((long) DateTimeConstants.MILLIS_PER_WEEK)); Period result = new Period(millis, DateTimeUtils.getPeriodType(type), ISOChronology.getInstanceUTC()); int years = getYears(); int months = getMonths(); if (years != 0 || months != 0) { years = FieldUtils.safeAdd(years, months / 12); months = months % 12; if (years != 0) { result = result.withYears(years); } if (months != 0) { result = result.withMonths(months); } } return result; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy