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.SimpleDateFormat;
26 import java.time.LocalDate;
27 import java.time.format.DateTimeFormatter;
28 import java.time.format.DateTimeParseException;
29 import java.util.Locale;
30
31
32
33
34
35
36 public class OceanusDateFormatter
37 implements OceanusEventProvider<OceanusDateEvent> {
38
39
40
41 public static final int BYTE_LEN = Long.BYTES;
42
43
44
45
46 private static final int PATCH_JAVA_VER = 16;
47
48
49
50
51 private static final String PATCH_SEPT_NEW = "-Sept-";
52
53
54
55
56 private static final String PATCH_SEPT_OLD = "-Sep-";
57
58
59
60
61 private static final String DEFAULT_FORMAT = "dd-MMM-yyyy";
62
63
64
65
66 private static final int YEARS_CENTURY = 100;
67
68
69
70
71 private final OceanusEventManager<OceanusDateEvent> theEventManager;
72
73
74
75
76 private Locale theLocale;
77
78
79
80
81 private String theFormat;
82
83
84
85
86 private SimpleDateFormat theDateFormat;
87
88
89
90
91 private DateTimeFormatter theLocalDateFormat;
92
93
94
95
96 public OceanusDateFormatter() {
97
98 this(OceanusLocale.getDefaultLocale());
99 }
100
101
102
103
104
105
106 public OceanusDateFormatter(final Locale pLocale) {
107
108 theLocale = pLocale;
109 theEventManager = new OceanusEventManager<>();
110 setFormat(DEFAULT_FORMAT);
111 }
112
113 @Override
114 public OceanusEventRegistrar<OceanusDateEvent> getEventRegistrar() {
115 return theEventManager.getEventRegistrar();
116 }
117
118
119
120
121
122
123 public final void setFormat(final String pFormat) {
124
125 if (pFormat.equals(theFormat)) {
126
127 return;
128 }
129
130
131 theFormat = pFormat;
132 theDateFormat = new SimpleDateFormat(theFormat, theLocale);
133 theLocalDateFormat = DateTimeFormatter.ofPattern(theFormat, theLocale);
134
135
136 theEventManager.fireEvent(OceanusDateEvent.FORMATCHANGED);
137 }
138
139
140
141
142
143
144 public final void setLocale(final Locale pLocale) {
145
146 if (theLocale.equals(pLocale)) {
147
148 return;
149 }
150
151
152 theLocale = pLocale;
153 final String pFormat = theFormat;
154 theFormat = null;
155 setFormat(pFormat);
156 }
157
158
159
160
161
162
163
164 public String formatLocalDate(final LocalDate pDate) {
165
166 if (pDate == null) {
167 return null;
168 }
169
170
171 return pDate.format(theLocalDateFormat);
172 }
173
174
175
176
177
178
179
180 public String formatDate(final OceanusDate pDate) {
181
182 if (pDate == null) {
183 return null;
184 }
185
186
187 return formatLocalDate(pDate.getDate());
188 }
189
190
191
192
193
194
195
196 public String formatDateRange(final OceanusDateRange pRange) {
197
198 if (pRange == null) {
199 return null;
200 }
201
202
203 final OceanusDate myStart = pRange.getStart();
204 final OceanusDate myEnd = pRange.getEnd();
205
206
207 return ((myStart == null)
208 ? OceanusDateRange.DESC_UNBOUNDED
209 : formatDate(myStart))
210 + OceanusDateRange.CHAR_BLANK
211 + OceanusDateRange.DESC_LINK
212 + OceanusDateRange.CHAR_BLANK
213 + ((myEnd == null)
214 ? OceanusDateRange.DESC_UNBOUNDED
215 : formatDate(myEnd));
216 }
217
218
219
220
221
222
223
224
225 public LocalDate parseLocalDate(final String pValue) {
226
227 try {
228 return LocalDate.parse(pValue, theLocalDateFormat);
229 } catch (DateTimeParseException e) {
230
231 final int myVersion = Runtime.version().feature();
232 if (pValue.contains(PATCH_SEPT_OLD)
233 && myVersion >= PATCH_JAVA_VER) {
234 return parsePatchedLocalDate(pValue.replace(PATCH_SEPT_OLD, PATCH_SEPT_NEW));
235 }
236 if (pValue.contains(PATCH_SEPT_NEW)
237 && Runtime.version().feature() < PATCH_JAVA_VER) {
238 return parsePatchedLocalDate(pValue.replace(PATCH_SEPT_NEW, PATCH_SEPT_OLD));
239 }
240
241
242 throw new IllegalArgumentException("Invalid date: "
243 + pValue, e);
244 }
245 }
246
247
248
249
250
251
252
253
254 private LocalDate parsePatchedLocalDate(final String pValue) {
255
256 try {
257 return LocalDate.parse(pValue, theLocalDateFormat);
258 } catch (DateTimeParseException e) {
259
260 throw new IllegalArgumentException("Invalid date: "
261 + pValue, e);
262 }
263 }
264
265
266
267
268
269
270
271
272
273 public OceanusDate parseDate(final String pValue) {
274 final LocalDate myDate = parseLocalDate(pValue);
275 return new OceanusDate(myDate);
276 }
277
278
279
280
281
282
283
284
285
286
287
288 public OceanusDate parseDateBase(final String pValue,
289 final int pBaseYear) {
290 LocalDate myDate = parseLocalDate(pValue);
291 if (myDate.getYear() >= pBaseYear + YEARS_CENTURY) {
292 myDate = myDate.minusYears(YEARS_CENTURY);
293 }
294 return new OceanusDate(myDate);
295 }
296
297
298
299
300
301
302 public Locale getLocale() {
303 return theLocale;
304 }
305
306
307
308
309
310
311
312 public byte[] toBytes(final OceanusDate pDate) {
313 final long myEpoch = pDate.getDate().toEpochDay();
314 return OceanusDataConverter.longToByteArray(myEpoch);
315 }
316
317
318
319
320
321
322
323 public OceanusDate fromBytes(final byte[] pBuffer) {
324 if (pBuffer == null || pBuffer.length < Long.BYTES) {
325 throw new IllegalArgumentException();
326 }
327 final long myEpoch = OceanusDataConverter.byteArrayToLong(pBuffer);
328 final LocalDate myDate = LocalDate.ofEpochDay(myEpoch);
329 return new OceanusDate(myDate);
330 }
331 }