View Javadoc
1   /*
2    * Oceanus: Java Utilities
3    * Copyright 2012-2026. Tony Washer
4    *
5    * Licensed under the Apache License, Version 2.0 (the "License"); you may not
6    * use this file except in compliance with the License.  You may obtain a copy
7    * of the License at
8    *
9    *   http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
13   * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
14   * License for the specific language governing permissions and limitations under
15   * the License.
16   */
17  package io.github.tonywasher.joceanus.oceanus.date;
18  
19  import java.time.temporal.ChronoUnit;
20  import java.util.Locale;
21  import java.util.Objects;
22  
23  /**
24   * Represents a contiguous Range of dates.
25   */
26  public class OceanusDateRange
27          implements Comparable<OceanusDateRange> {
28      /**
29       * Unbounded range description.
30       */
31      protected static final String DESC_UNBOUNDED = OceanusDateResource.RANGE_UNBOUNDED.getValue();
32  
33      /**
34       * link range description.
35       */
36      protected static final String DESC_LINK = OceanusDateResource.RANGE_TO.getValue();
37  
38      /**
39       * link range description.
40       */
41      protected static final char CHAR_BLANK = ' ';
42  
43      /**
44       * The Start Date for the range.
45       */
46      private OceanusDate theStart;
47  
48      /**
49       * The End Date for the range.
50       */
51      private OceanusDate theEnd;
52  
53      /**
54       * Construct a Range from a Start Date and an End Date.
55       *
56       * @param pStart the start date
57       * @param pEnd   the end date
58       */
59      public OceanusDateRange(final OceanusDate pStart,
60                              final OceanusDate pEnd) {
61          if (pStart != null) {
62              theStart = new OceanusDate(pStart);
63          }
64          if (pEnd != null) {
65              theEnd = new OceanusDate(pEnd);
66          }
67      }
68  
69      /**
70       * Construct a range from another range.
71       *
72       * @param pRange the range to copy from
73       */
74      public OceanusDateRange(final OceanusDateRange pRange) {
75          this(pRange.getStart(), pRange.getEnd());
76      }
77  
78      /**
79       * Construct an unbounded Range.
80       */
81      public OceanusDateRange() {
82          this(null, null);
83      }
84  
85      /**
86       * Get the start date for the range.
87       *
88       * @return the Start date
89       */
90      public OceanusDate getStart() {
91          return theStart;
92      }
93  
94      /**
95       * Get the end date for the range.
96       *
97       * @return the End date
98       */
99      public OceanusDate getEnd() {
100         return theEnd;
101     }
102 
103     /**
104      * Determine whether a Date is within this range.
105      *
106      * @param pDate the date to test
107      * @return -1 if the date is after the range, 0 if the date is within the range, 1 if the date
108      * is before the range
109      */
110     public int compareToDate(final OceanusDate pDate) {
111         /* Check start date */
112         if (theStart != null
113                 && theStart.compareTo(pDate) > 0) {
114             return 1;
115         }
116 
117         /* Check end date */
118         if (theEnd != null
119                 && theEnd.compareTo(pDate) < 0) {
120             return -1;
121         }
122 
123         /* Date must be within range */
124         return 0;
125     }
126 
127     @Override
128     public int compareTo(final OceanusDateRange pThat) {
129         /* Handle the trivial cases */
130         if (this.equals(pThat)) {
131             return 0;
132         }
133         if (pThat == null) {
134             return -1;
135         }
136 
137         /* Access target start date */
138         final OceanusDate myStart = pThat.getStart();
139 
140         /* If our start is null */
141         if (theStart == null) {
142             /* Handle non-null target start */
143             if (myStart != null) {
144                 return 1;
145             }
146 
147             /* else start is non-null */
148         } else {
149             /* Handle null target start */
150             if (myStart == null) {
151                 return -1;
152             }
153 
154             /* Compare the start dates */
155             final int result = theStart.compareTo(myStart);
156             if (result != 0) {
157                 return result;
158             }
159         }
160 
161         /* Access target end date */
162         final OceanusDate myEnd = pThat.getEnd();
163 
164         /* If our end is null */
165         if (theEnd == null) {
166             /* Handle non-null target end */
167             if (myEnd != null) {
168                 return 1;
169             }
170 
171             /* else start is non-null */
172         } else {
173             /* Handle null target end */
174             if (myStart == null) {
175                 return -1;
176             }
177 
178             /* Compare the end dates */
179             final int result = theEnd.compareTo(myEnd);
180             if (result != 0) {
181                 return result;
182             }
183         }
184 
185         /* Ranges are identical */
186         return 0;
187     }
188 
189     @Override
190     public String toString() {
191         /* Build range description */
192         final StringBuilder myBuilder = new StringBuilder();
193         myBuilder.append(theStart == null
194                 ? DESC_UNBOUNDED
195                 : theStart.toString());
196         myBuilder.append(CHAR_BLANK);
197         myBuilder.append(DESC_LINK);
198         myBuilder.append(CHAR_BLANK);
199         myBuilder.append(theEnd == null
200                 ? DESC_UNBOUNDED
201                 : theEnd.toString());
202 
203         /* return the format */
204         return myBuilder.toString();
205     }
206 
207     @Override
208     public boolean equals(final Object pThat) {
209         /* Handle the trivial cases */
210         if (this == pThat) {
211             return true;
212         }
213         if (pThat == null) {
214             return false;
215         }
216 
217         /* Make sure that the object is a JDateDayRange */
218         if (pThat.getClass() != this.getClass()) {
219             return false;
220         }
221 
222         /* Access the object as a DateRange */
223         final OceanusDateRange myThat = (OceanusDateRange) pThat;
224 
225         /* Check components */
226         if (theStart == null) {
227             if (myThat.getStart() != null) {
228                 return false;
229             }
230         } else if (!theStart.equals(myThat.getStart())) {
231             return false;
232         }
233         if (theEnd == null) {
234             if (myThat.getEnd() != null) {
235                 return false;
236             }
237         } else if (!theEnd.equals(myThat.getEnd())) {
238             return false;
239         }
240         return true;
241 
242     }
243 
244     @Override
245     public int hashCode() {
246         return Objects.hash(theStart, theEnd);
247     }
248 
249     /**
250      * Set the locale.
251      *
252      * @param pLocale the locale
253      */
254     public void setLocale(final Locale pLocale) {
255         if (theStart != null) {
256             theStart.setLocale(pLocale);
257         }
258         if (theEnd != null) {
259             theEnd.setLocale(pLocale);
260         }
261     }
262 
263     /**
264      * Determine whether two DateDay objects differ.
265      *
266      * @param pCurr The current Date
267      * @param pNew  The new Date
268      * @return <code>true</code> if the objects differ, <code>false</code> otherwise
269      */
270     public static boolean isDifferent(final OceanusDateRange pCurr,
271                                       final OceanusDateRange pNew) {
272         /* Handle case where current value is null */
273         if (pCurr == null) {
274             return pNew != null;
275         }
276 
277         /* Handle case where new value is null */
278         if (pNew == null) {
279             return true;
280         }
281 
282         /* Handle Standard cases */
283         return !pCurr.equals(pNew);
284     }
285 
286     /**
287      * Obtain the number of days in the range.
288      *
289      * @return the number of days (or -1 if unbounded)
290      */
291     public long getNumDays() {
292         /* Handle unbounded */
293         if (theStart == null || theEnd == null) {
294             return -1;
295         }
296 
297         /* Calculate the number of days */
298         return 1 + ChronoUnit.DAYS.between(theStart.getDate(), theEnd.getDate());
299     }
300 }