1 /*
2  * hunt-time: A time library for D programming language.
3  *
4  * Copyright (C) 2015-2018 HuntLabs
5  *
6  * Website: https://www.huntlabs.net/
7  *
8  * Licensed under the Apache-2.0 License.
9  *
10  */
11 
12 module hunt.time.chrono.ChronoPeriod;
13 
14 import hunt.time.Exceptions;
15 import hunt.time.temporal.ChronoUnit;
16 import hunt.time.temporal.Temporal;
17 import hunt.time.temporal.TemporalAmount;
18 import hunt.time.temporal.TemporalUnit;
19 import hunt.time.Exceptions;
20 import hunt.collection.List;
21 import hunt.time.chrono.ChronoLocalDate;
22 import hunt.time.chrono.Chronology;
23 
24 /**
25  * A date-based amount of time, such as '3 years, 4 months and 5 days' _in an
26  * arbitrary chronology, intended for advanced globalization use cases.
27  * !(p)
28  * This interface models a date-based amount of time _in a calendar system.
29  * While most calendar systems use years, months and days, some do not.
30  * Therefore, this interface operates solely _in terms of a set of supported
31  * units that are defined by the {@code Chronology}.
32  * The set of supported units is fixed for a given chronology.
33  * The amount of a supported unit may be set to zero.
34  * !(p)
35  * The period is modeled as a directed amount of time, meaning that individual
36  * parts of the period may be negative.
37  *
38  * @implSpec
39  * This interface must be implemented with care to ensure other classes operate correctly.
40  * All implementations that can be instantiated must be final, immutable and thread-safe.
41  * Subclasses should be Serializable wherever possible.
42  *
43  * @since 1.8
44  */
45 public interface ChronoPeriod
46         : TemporalAmount {
47 
48     /**
49      * Obtains a {@code ChronoPeriod} consisting of amount of time between two dates.
50      * !(p)
51      * The start date is included, but the end date is not.
52      * The period is calculated using {@link ChronoLocalDate#until(ChronoLocalDate)}.
53      * As such, the calculation is chronology specific.
54      * !(p)
55      * The chronology of the first date is used.
56      * The chronology of the second date is ignored, with the date being converted
57      * to the target chronology system before the calculation starts.
58      * !(p)
59      * The result of this method can be a negative period if the end is before the start.
60      * In most cases, the positive/negative sign will be the same _in each of the supported fields.
61      *
62      * @param startDateInclusive  the start date, inclusive, specifying the chronology of the calculation, not null
63      * @param endDateExclusive  the end date, exclusive, _in any chronology, not null
64      * @return the period between this date and the end date, not null
65      * @see ChronoLocalDate#until(ChronoLocalDate)
66      */
67     public static ChronoPeriod between(ChronoLocalDate startDateInclusive, ChronoLocalDate endDateExclusive) {
68         assert(startDateInclusive, "startDateInclusive");
69         assert(endDateExclusive, "endDateExclusive");
70         return startDateInclusive.until(endDateExclusive);
71     }
72 
73     //-----------------------------------------------------------------------
74     /**
75      * Gets the value of the requested unit.
76      * !(p)
77      * The supported units are chronology specific.
78      * They will typically be {@link ChronoUnit#YEARS YEARS},
79      * {@link ChronoUnit#MONTHS MONTHS} and {@link ChronoUnit#DAYS DAYS}.
80      * Requesting an unsupported unit will throw an exception.
81      *
82      * @param unit the {@code TemporalUnit} for which to return the value
83      * @return the long value of the unit
84      * @throws DateTimeException if the unit is not supported
85      * @throws UnsupportedTemporalTypeException if the unit is not supported
86      */
87     override
88     long get(TemporalUnit unit);
89 
90     /**
91      * Gets the set of units supported by this period.
92      * !(p)
93      * The supported units are chronology specific.
94      * They will typically be {@link ChronoUnit#YEARS YEARS},
95      * {@link ChronoUnit#MONTHS MONTHS} and {@link ChronoUnit#DAYS DAYS}.
96      * They are returned _in order from largest to smallest.
97      * !(p)
98      * This set can be used _in conjunction with {@link #get(TemporalUnit)}
99      * to access the entire state of the period.
100      *
101      * @return a list containing the supported units, not null
102      */
103     override
104     List!(TemporalUnit) getUnits();
105 
106     /**
107      * Gets the chronology that defines the meaning of the supported units.
108      * !(p)
109      * The period is defined by the chronology.
110      * It controls the supported units and restricts addition/subtraction
111      * to {@code ChronoLocalDate} instances of the same chronology.
112      *
113      * @return the chronology defining the period, not null
114      */
115     Chronology getChronology();
116 
117     //-----------------------------------------------------------------------
118     /**
119      * Checks if all the supported units of this period are zero.
120      *
121      * @return true if this period is zero-length
122      */
123      bool isZero();
124     //  bool isZero() {
125     //     foreach(TemporalUnit unit ; getUnits()) {
126     //         if (get(unit) != 0) {
127     //             return false;
128     //         }
129     //     }
130     //     return true;
131     // }
132 
133     /**
134      * Checks if any of the supported units of this period are negative.
135      *
136      * @return true if any unit of this period is negative
137      */
138      bool isNegative();
139     //  bool isNegative() {
140     //     foreach(TemporalUnit unit ; getUnits()) {
141     //         if (get(unit) < 0) {
142     //             return true;
143     //         }
144     //     }
145     //     return false;
146     // }
147 
148     //-----------------------------------------------------------------------
149     /**
150      * Returns a copy of this period with the specified period added.
151      * !(p)
152      * If the specified amount is a {@code ChronoPeriod} then it must have
153      * the same chronology as this period. Implementations may choose to
154      * accept or reject other {@code TemporalAmount} implementations.
155      * !(p)
156      * This instance is immutable and unaffected by this method call.
157      *
158      * @param amountToAdd  the period to add, not null
159      * @return a {@code ChronoPeriod} based on this period with the requested period added, not null
160      * @throws ArithmeticException if numeric overflow occurs
161      */
162     ChronoPeriod plus(TemporalAmount amountToAdd);
163 
164     /**
165      * Returns a copy of this period with the specified period subtracted.
166      * !(p)
167      * If the specified amount is a {@code ChronoPeriod} then it must have
168      * the same chronology as this period. Implementations may choose to
169      * accept or reject other {@code TemporalAmount} implementations.
170      * !(p)
171      * This instance is immutable and unaffected by this method call.
172      *
173      * @param amountToSubtract  the period to subtract, not null
174      * @return a {@code ChronoPeriod} based on this period with the requested period subtracted, not null
175      * @throws ArithmeticException if numeric overflow occurs
176      */
177     ChronoPeriod minus(TemporalAmount amountToSubtract);
178 
179     //-----------------------------------------------------------------------
180     /**
181      * Returns a new instance with each amount _in this period _in this period
182      * multiplied by the specified scalar.
183      * !(p)
184      * This returns a period with each supported unit individually multiplied.
185      * For example, a period of "2 years, -3 months and 4 days" multiplied by
186      * 3 will return "6 years, -9 months and 12 days".
187      * No normalization is performed.
188      *
189      * @param scalar  the scalar to multiply by, not null
190      * @return a {@code ChronoPeriod} based on this period with the amounts multiplied
191      *  by the scalar, not null
192      * @throws ArithmeticException if numeric overflow occurs
193      */
194     ChronoPeriod multipliedBy(int scalar);
195 
196     /**
197      * Returns a new instance with each amount _in this period negated.
198      * !(p)
199      * This returns a period with each supported unit individually negated.
200      * For example, a period of "2 years, -3 months and 4 days" will be
201      * negated to "-2 years, 3 months and -4 days".
202      * No normalization is performed.
203      *
204      * @return a {@code ChronoPeriod} based on this period with the amounts negated, not null
205      * @throws ArithmeticException if numeric overflow occurs, which only happens if
206      *  one of the units has the value {@code Long.MIN_VALUE}
207      */
208      ChronoPeriod negated();
209     //  ChronoPeriod negated() {
210     //     return multipliedBy(-1);
211     // }
212 
213     //-----------------------------------------------------------------------
214     /**
215      * Returns a copy of this period with the amounts of each unit normalized.
216      * !(p)
217      * The process of normalization is specific to each calendar system.
218      * For example, _in the ISO calendar system, the years and months are
219      * normalized but the days are not, such that "15 months" would be
220      * normalized to "1 year and 3 months".
221      * !(p)
222      * This instance is immutable and unaffected by this method call.
223      *
224      * @return a {@code ChronoPeriod} based on this period with the amounts of each
225      *  unit normalized, not null
226      * @throws ArithmeticException if numeric overflow occurs
227      */
228     ChronoPeriod normalized();
229 
230     //-------------------------------------------------------------------------
231     /**
232      * Adds this period to the specified temporal object.
233      * !(p)
234      * This returns a temporal object of the same observable type as the input
235      * with this period added.
236      * !(p)
237      * In most cases, it is clearer to reverse the calling pattern by using
238      * {@link Temporal#plus(TemporalAmount)}.
239      * !(pre)
240      *   // these two lines are equivalent, but the second approach is recommended
241      *   dateTime = thisPeriod.addTo(dateTime);
242      *   dateTime = dateTime.plus(thisPeriod);
243      * </pre>
244      * !(p)
245      * The specified temporal must have the same chronology as this period.
246      * This returns a temporal with the non-zero supported units added.
247      * !(p)
248      * This instance is immutable and unaffected by this method call.
249      *
250      * @param temporal  the temporal object to adjust, not null
251      * @return an object of the same type with the adjustment made, not null
252      * @throws DateTimeException if unable to add
253      * @throws ArithmeticException if numeric overflow occurs
254      */
255     override
256     Temporal addTo(Temporal temporal);
257 
258     /**
259      * Subtracts this period from the specified temporal object.
260      * !(p)
261      * This returns a temporal object of the same observable type as the input
262      * with this period subtracted.
263      * !(p)
264      * In most cases, it is clearer to reverse the calling pattern by using
265      * {@link Temporal#minus(TemporalAmount)}.
266      * !(pre)
267      *   // these two lines are equivalent, but the second approach is recommended
268      *   dateTime = thisPeriod.subtractFrom(dateTime);
269      *   dateTime = dateTime.minus(thisPeriod);
270      * </pre>
271      * !(p)
272      * The specified temporal must have the same chronology as this period.
273      * This returns a temporal with the non-zero supported units subtracted.
274      * !(p)
275      * This instance is immutable and unaffected by this method call.
276      *
277      * @param temporal  the temporal object to adjust, not null
278      * @return an object of the same type with the adjustment made, not null
279      * @throws DateTimeException if unable to subtract
280      * @throws ArithmeticException if numeric overflow occurs
281      */
282     override
283     Temporal subtractFrom(Temporal temporal);
284 
285     //-----------------------------------------------------------------------
286     /**
287      * Checks if this period is equal to another period, including the chronology.
288      * !(p)
289      * Compares this period with another ensuring that the type, each amount and
290      * the chronology are the same.
291      * Note that this means that a period of "15 Months" is not equal to a period
292      * of "1 Year and 3 Months".
293      *
294      * @param obj  the object to check, null returns false
295      * @return true if this is equal to the other period
296      */
297     // override
298     // bool opEquals(Object obj);
299 
300     /**
301      * A hash code for this period.
302      *
303      * @return a suitable hash code
304      */
305     // override
306     // size_t toHash() @trusted nothrow;
307 
308     //-----------------------------------------------------------------------
309     /**
310      * Outputs this period as a {@code string}.
311      * !(p)
312      * The output will include the period amounts and chronology.
313      *
314      * @return a string representation of this period, not null
315      */
316     // override
317     // string toString();
318 
319 }