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 }