1
2
3
4 package de.powerstat.validation.values;
5
6
7 import java.util.Objects;
8
9 import de.powerstat.validation.interfaces.IValueObject;
10
11
12
13
14
15
16
17
18
19
20 public final class Year implements Comparable<Year>, IValueObject
21 {
22
23
24
25 private static final String UNSUPPORTED_CALENDAR_SYSTEM = "Unsupported calendar system!";
26
27
28
29
30
31
32
33
34
35
36
37 private static final long BEFORE_GREGORIAN_YEAR = 1582;
38
39
40
41
42 private final CalendarSystems calendarSystem;
43
44
45
46
47 private final long year;
48
49
50
51
52
53
54
55
56
57
58 private Year(final CalendarSystems calendarSystem, final long year)
59 {
60 super();
61 Objects.requireNonNull(calendarSystem, "calendarSystem");
62 if (year == 0)
63 {
64 throw new IndexOutOfBoundsException("Year 0 does not exist!");
65 }
66 this.calendarSystem = calendarSystem;
67 this.year = year;
68 }
69
70
71
72
73
74
75
76
77
78 public static Year of(final CalendarSystems calendarSystem, final long year)
79 {
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94 return new Year(calendarSystem, year);
95 }
96
97
98
99
100
101
102
103
104 public static Year of(final long year)
105 {
106 return of(CalendarSystems.GREGORIAN, year);
107 }
108
109
110
111
112
113
114
115
116 public static Year of(final String value)
117 {
118 return of(CalendarSystems.GREGORIAN, Long.parseLong(value));
119 }
120
121
122
123
124
125
126
127 public long longValue()
128 {
129 return this.year;
130 }
131
132
133
134
135
136
137
138 @Override
139 public String stringValue()
140 {
141 return String.valueOf(this.year);
142 }
143
144
145
146
147
148
149
150 public static Months monthsWithin()
151 {
152 return Months.of(12);
153 }
154
155
156
157
158
159
160
161
162 private static boolean isJulianLeapYear(final long year)
163 {
164 if (year <= 0)
165 {
166 return ((-year) % 4) == 1;
167 }
168 else
169 {
170 return (year % 4) == 0;
171 }
172 }
173
174
175
176
177
178
179
180
181 private static boolean isGregorianLeapYear(final long year)
182 {
183 return (((year % 4) == 0) && (((year % 100) > 0) || ((year % 400) == 0)));
184 }
185
186
187
188
189
190
191
192
193 public boolean isLeapYear()
194 {
195 switch (this.calendarSystem)
196 {
197 case JULIAN:
198 return isJulianLeapYear(this.year);
199 case GREGORIAN:
200 if (this.year < BEFORE_GREGORIAN_YEAR)
201 {
202 return isJulianLeapYear(this.year);
203 }
204 else
205 {
206 return isGregorianLeapYear(this.year);
207 }
208 default:
209 throw new IllegalStateException(UNSUPPORTED_CALENDAR_SYSTEM);
210 }
211 }
212
213
214
215
216
217
218
219
220 public Days daysWithin()
221 {
222 switch (this.calendarSystem)
223 {
224 case JULIAN:
225 return Days.of(365L + (this.isLeapYear() ? 1 : 0));
226 case GREGORIAN:
227 if (this.year == BEFORE_GREGORIAN_YEAR)
228 {
229 return Days.of(365L - 10);
230 }
231 return Days.of(365L + (this.isLeapYear() ? 1 : 0));
232 default:
233 throw new IllegalStateException(UNSUPPORTED_CALENDAR_SYSTEM);
234 }
235 }
236
237
238
239
240
241
242
243
244 @Override
245 public int hashCode()
246 {
247 return Objects.hash(this.calendarSystem, this.year);
248 }
249
250
251
252
253
254
255
256
257
258 @Override
259 public boolean equals(final Object obj)
260 {
261 if (this == obj)
262 {
263 return true;
264 }
265 if (!(obj instanceof Year))
266 {
267 return false;
268 }
269 final Year other = (Year)obj;
270 return this.year == other.year;
271 }
272
273
274
275
276
277
278
279
280
281
282
283
284 @Override
285 public String toString()
286 {
287 final var builder = new StringBuilder(28);
288 builder.append("Year[calendarSystem=").append(this.calendarSystem).append(", year=").append(this.year).append(']');
289 return builder.toString();
290 }
291
292
293
294
295
296
297
298
299
300
301 @Override
302 public int compareTo(final Year obj)
303 {
304 Objects.requireNonNull(obj, "obj");
305 if (this.calendarSystem.compareTo(obj.calendarSystem) != 0)
306 {
307 throw new IllegalStateException("CalendarSystems are not equal!");
308 }
309 return Long.compare(this.year, obj.year);
310 }
311
312
313
314
315
316
317
318
319
320 public Year add(final Years years)
321 {
322 long newYear = Math.addExact(this.year, years.longValue());
323 if ((this.year < 0) && (newYear >= 0))
324 {
325 newYear = Math.incrementExact(newYear);
326 }
327 return Year.of(this.calendarSystem, newYear);
328 }
329
330
331
332
333
334
335
336
337
338 public Year subtract(final Years years)
339 {
340 long newYear = Math.subtractExact(this.year, years.longValue());
341 if ((this.year > 0) && (newYear <= 0))
342 {
343 newYear = Math.decrementExact(newYear);
344 }
345 return Year.of(this.calendarSystem, newYear);
346 }
347
348
349
350
351
352
353
354
355 public Year increment()
356 {
357 long newYear = Math.incrementExact(this.year);
358 if (this.year == -1)
359 {
360 newYear = Math.incrementExact(newYear);
361 }
362 return Year.of(newYear);
363 }
364
365
366
367
368
369
370
371
372 public Year decrement()
373 {
374 long newYear = Math.decrementExact(this.year);
375 if (this.year == 1)
376 {
377 newYear = Math.decrementExact(newYear);
378 }
379 return Year.of(newYear);
380 }
381
382 }