1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package io.github.tonywasher.joceanus.oceanus.date;
18
19 import io.github.tonywasher.joceanus.oceanus.base.OceanusLocale;
20 import io.github.tonywasher.joceanus.oceanus.convert.OceanusDataConverter;
21 import io.github.tonywasher.joceanus.oceanus.event.OceanusEventManager;
22 import io.github.tonywasher.joceanus.oceanus.event.OceanusEventRegistrar;
23 import io.github.tonywasher.joceanus.oceanus.event.OceanusEventRegistrar.OceanusEventProvider;
24
25 import java.text.ParseException;
26 import java.text.SimpleDateFormat;
27 import java.time.LocalDate;
28 import java.time.format.DateTimeFormatter;
29 import java.time.format.DateTimeParseException;
30 import java.util.Calendar;
31 import java.util.Date;
32 import java.util.Locale;
33
34
35
36
37
38
39 public class OceanusDateFormatter
40 implements OceanusEventProvider<OceanusDateEvent> {
41
42
43
44 public static final int BYTE_LEN = Long.BYTES;
45
46
47
48
49 private static final int PATCH_JAVA_VER = 16;
50
51
52
53
54 private static final String PATCH_SEPT_NEW = "-Sept-";
55
56
57
58
59 private static final String PATCH_SEPT_OLD = "-Sep-";
60
61
62
63
64 private static final String DEFAULT_FORMAT = "dd-MMM-yyyy";
65
66
67
68
69 private static final int YEARS_CENTURY = 100;
70
71
72
73
74 private final OceanusEventManager<OceanusDateEvent> theEventManager;
75
76
77
78
79 private Locale theLocale;
80
81
82
83
84 private String theFormat;
85
86
87
88
89 private SimpleDateFormat theDateFormat;
90
91
92
93
94 private DateTimeFormatter theLocalDateFormat;
95
96
97
98
99 public OceanusDateFormatter() {
100
101 this(OceanusLocale.getDefaultLocale());
102 }
103
104
105
106
107
108
109 public OceanusDateFormatter(final Locale pLocale) {
110
111 theLocale = pLocale;
112 theEventManager = new OceanusEventManager<>();
113 setFormat(DEFAULT_FORMAT);
114 }
115
116 @Override
117 public OceanusEventRegistrar<OceanusDateEvent> getEventRegistrar() {
118 return theEventManager.getEventRegistrar();
119 }
120
121
122
123
124
125
126 public final void setFormat(final String pFormat) {
127
128 if (pFormat.equals(theFormat)) {
129
130 return;
131 }
132
133
134 theFormat = pFormat;
135 theDateFormat = new SimpleDateFormat(theFormat, theLocale);
136 theLocalDateFormat = DateTimeFormatter.ofPattern(theFormat, theLocale);
137
138
139 theEventManager.fireEvent(OceanusDateEvent.FORMATCHANGED);
140 }
141
142
143
144
145
146
147 public final void setLocale(final Locale pLocale) {
148
149 if (theLocale.equals(pLocale)) {
150
151 return;
152 }
153
154
155 theLocale = pLocale;
156 final String pFormat = theFormat;
157 theFormat = null;
158 setFormat(pFormat);
159 }
160
161
162
163
164
165
166
167 public String formatCalendarDay(final Calendar pDate) {
168
169 if (pDate == null) {
170 return null;
171 }
172
173
174 return formatJavaDate(pDate.getTime());
175 }
176
177
178
179
180
181
182
183 public String formatLocalDate(final LocalDate pDate) {
184
185 if (pDate == null) {
186 return null;
187 }
188
189
190 return pDate.format(theLocalDateFormat);
191 }
192
193
194
195
196
197
198
199 public String formatJavaDate(final Date pDate) {
200
201 if (pDate == null) {
202 return null;
203 }
204
205
206 return theDateFormat.format(pDate);
207 }
208
209
210
211
212
213
214
215 public String formatDate(final OceanusDate pDate) {
216
217 if (pDate == null) {
218 return null;
219 }
220
221
222 return formatLocalDate(pDate.getDate());
223 }
224
225
226
227
228
229
230
231 public String formatDateRange(final OceanusDateRange pRange) {
232
233 if (pRange == null) {
234 return null;
235 }
236
237
238 final OceanusDate myStart = pRange.getStart();
239 final OceanusDate myEnd = pRange.getEnd();
240
241
242 return ((myStart == null)
243 ? OceanusDateRange.DESC_UNBOUNDED
244 : formatDate(myStart))
245 + OceanusDateRange.CHAR_BLANK
246 + OceanusDateRange.DESC_LINK
247 + OceanusDateRange.CHAR_BLANK
248 + ((myEnd == null)
249 ? OceanusDateRange.DESC_UNBOUNDED
250 : formatDate(myEnd));
251 }
252
253
254
255
256
257
258
259
260 public Date parseJavaDate(final String pValue) {
261
262 try {
263 return theDateFormat.parse(pValue);
264 } catch (ParseException e) {
265 throw new IllegalArgumentException("Invalid date: "
266 + pValue, e);
267 }
268 }
269
270
271
272
273
274
275
276
277 public LocalDate parseLocalDate(final String pValue) {
278
279 try {
280 return LocalDate.parse(pValue, theLocalDateFormat);
281 } catch (DateTimeParseException e) {
282
283 final int myVersion = Runtime.version().feature();
284 if (pValue.contains(PATCH_SEPT_OLD)
285 && myVersion >= PATCH_JAVA_VER) {
286 return parsePatchedLocalDate(pValue.replace(PATCH_SEPT_OLD, PATCH_SEPT_NEW));
287 }
288 if (pValue.contains(PATCH_SEPT_NEW)
289 && Runtime.version().feature() < PATCH_JAVA_VER) {
290 return parsePatchedLocalDate(pValue.replace(PATCH_SEPT_NEW, PATCH_SEPT_OLD));
291 }
292
293
294 throw new IllegalArgumentException("Invalid date: "
295 + pValue, e);
296 }
297 }
298
299
300
301
302
303
304
305
306 private LocalDate parsePatchedLocalDate(final String pValue) {
307
308 try {
309 return LocalDate.parse(pValue, theLocalDateFormat);
310 } catch (DateTimeParseException e) {
311
312 throw new IllegalArgumentException("Invalid date: "
313 + pValue, e);
314 }
315 }
316
317
318
319
320
321
322
323
324 public Calendar parseCalendarDay(final String pValue) {
325 final Date myDate = parseJavaDate(pValue);
326 final Calendar myCalendar = Calendar.getInstance(theLocale);
327 myCalendar.setTime(myDate);
328 return myCalendar;
329 }
330
331
332
333
334
335
336
337
338 public OceanusDate parseDate(final String pValue) {
339 final LocalDate myDate = parseLocalDate(pValue);
340 return new OceanusDate(myDate);
341 }
342
343
344
345
346
347
348
349
350
351
352
353 public OceanusDate parseDateBase(final String pValue,
354 final int pBaseYear) {
355 LocalDate myDate = parseLocalDate(pValue);
356 if (myDate.getYear() >= pBaseYear + YEARS_CENTURY) {
357 myDate = myDate.minusYears(YEARS_CENTURY);
358 }
359 return new OceanusDate(myDate);
360 }
361
362
363
364
365
366
367 public Locale getLocale() {
368 return theLocale;
369 }
370
371
372
373
374
375
376
377 public byte[] toBytes(final OceanusDate pDate) {
378 final long myEpoch = pDate.getDate().toEpochDay();
379 return OceanusDataConverter.longToByteArray(myEpoch);
380 }
381
382
383
384
385
386
387
388 public OceanusDate fromBytes(final byte[] pBuffer) {
389 if (pBuffer == null || pBuffer.length < Long.BYTES) {
390 throw new IllegalArgumentException();
391 }
392 final long myEpoch = OceanusDataConverter.byteArrayToLong(pBuffer);
393 final LocalDate myDate = LocalDate.ofEpochDay(myEpoch);
394 return new OceanusDate(myDate);
395 }
396 }