-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathDateTimeUtils.java
More file actions
413 lines (363 loc) · 13.8 KB
/
Copy pathDateTimeUtils.java
File metadata and controls
413 lines (363 loc) · 13.8 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
package ch.fhnw.timerecordingbackend.util;
import java.time.*;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
import java.time.temporal.ChronoUnit;
import java.time.temporal.TemporalAdjusters;
import java.util.ArrayList;
import java.util.List;
/**
* Utils Klasse für Datum und Zeit Operationen
* Hilfsmethoden für häufig verwendete Datum/Zeit Berechnungen
* @author PD
* @version 1.0
*/
public class DateTimeUtils {
// Standard-Formatierungen
public static final DateTimeFormatter DATE_FORMAT = DateTimeFormatter.ofPattern("yyyy-MM-dd");
public static final DateTimeFormatter TIME_FORMAT = DateTimeFormatter.ofPattern("HH:mm");
public static final DateTimeFormatter DATETIME_FORMAT = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
public static final DateTimeFormatter DISPLAY_DATE_FORMAT = DateTimeFormatter.ofPattern("dd.MM.yyyy");
public static final DateTimeFormatter DISPLAY_DATETIME_FORMAT = DateTimeFormatter.ofPattern("dd.MM.yyyy HH:mm");
/**
* Private Constructor - Utility-Klasse soll nicht instanziiert werden
*/
private DateTimeUtils() {
throw new IllegalStateException("Utility-Klasse kann nicht instanziiert werden");
}
// ==================== FORMATIERUNG ====================
/**
* Formatiert LocalDate zu String im Format "yyyy-MM-dd"
* @param date Das zu formatierende Datum
* @return Formatiertes Datum als String oder null wenn date null ist
*/
public static String formatDate(LocalDate date) {
return date != null ? date.format(DATE_FORMAT) : null;
}
/**
* Formatiert LocalDate zu String im Anzeigeformat "dd.MM.yyyy"
* @param date Das zu formatierende Datum
* @return Formatiertes Datum als String oder null wenn date null ist
*/
public static String formatDateDisplay(LocalDate date) {
return date != null ? date.format(DISPLAY_DATE_FORMAT) : null;
}
/**
* Formatiert LocalTime zu String im Format "HH:mm"
* @param time Die zu formatierende Zeit
* @return Formatierte Zeit als String oder null wenn time null ist
*/
public static String formatTime(LocalTime time) {
return time != null ? time.format(TIME_FORMAT) : null;
}
/**
* Formatiert LocalDateTime zu String im Format "yyyy-MM-dd HH:mm:ss"
* @param dateTime Das zu formatierende Datum/Zeit
* @return Formatiertes Datum/Zeit als String oder null wenn dateTime null ist
*/
public static String formatDateTime(LocalDateTime dateTime) {
return dateTime != null ? dateTime.format(DATETIME_FORMAT) : null;
}
/**
* Formatiert LocalDateTime zu String im Anzeigeformat "dd.MM.yyyy HH:mm"
* @param dateTime Das zu formatierende Datum/Zeit
* @return Formatiertes Datum/Zeit als String oder null wenn dateTime null ist
*/
public static String formatDateTimeDisplay(LocalDateTime dateTime) {
return dateTime != null ? dateTime.format(DISPLAY_DATETIME_FORMAT) : null;
}
// ==================== PARSING ====================
/**
* Parst String zu LocalDate
* @param dateString Datum als String im Format "yyyy-MM-dd"
* @return LocalDate oder null bei ungültigem Format
*/
public static LocalDate parseDate(String dateString) {
if (dateString == null || dateString.trim().isEmpty()) {
return null;
}
try {
return LocalDate.parse(dateString, DATE_FORMAT);
} catch (DateTimeParseException e) {
return null;
}
}
/**
* Parst String zu LocalTime
* @param timeString Zeit als String im Format "HH:mm"
* @return LocalTime oder null bei ungültigem Format
*/
public static LocalTime parseTime(String timeString) {
if (timeString == null || timeString.trim().isEmpty()) {
return null;
}
try {
return LocalTime.parse(timeString, TIME_FORMAT);
} catch (DateTimeParseException e) {
return null;
}
}
/**
* Parst String zu LocalDateTime
* @param dateTimeString Datum/Zeit als String im Format "yyyy-MM-dd HH:mm:ss"
* @return LocalDateTime oder null bei ungültigem Format
*/
public static LocalDateTime parseDateTime(String dateTimeString) {
if (dateTimeString == null || dateTimeString.trim().isEmpty()) {
return null;
}
try {
return LocalDateTime.parse(dateTimeString, DATETIME_FORMAT);
} catch (DateTimeParseException e) {
return null;
}
}
// ==================== STUNDEN-BERECHNUNGEN ====================
/**
* Konvertiert Stunden (Double) zu "HH:mm" Format
* @param hours Stunden als Double (z.B. 8.5 für 8:30)
* @return Zeit im Format "HH:mm"
*/
public static String formatHours(Double hours) {
if (hours == null) {
return "00:00";
}
int totalMinutes = (int) Math.round(hours * 60);
int h = totalMinutes / 60;
int m = Math.abs(totalMinutes % 60);
// Negative Stunden mit Vorzeichen darstellen
if (totalMinutes < 0 && h == 0) {
return String.format("-%02d:%02d", Math.abs(h), m);
}
return String.format("%02d:%02d", h, m);
}
/**
* Konvertiert "HH:mm" Format zu Stunden (Double)
* @param timeString Zeit im Format "HH:mm" oder "-HH:mm"
* @return Stunden als Double
*/
public static Double parseHours(String timeString) {
if (timeString == null || timeString.trim().isEmpty()) {
return 0.0;
}
try {
boolean negative = timeString.startsWith("-");
String cleanTime = timeString.replace("-", "");
String[] parts = cleanTime.split(":");
if (parts.length == 2) {
int hours = Integer.parseInt(parts[0]);
int minutes = Integer.parseInt(parts[1]);
double result = hours + (minutes / 60.0);
return negative ? -result : result;
}
return Double.parseDouble(timeString);
} catch (NumberFormatException e) {
return 0.0;
}
}
/**
* Berechnet die Differenz zwischen zwei Zeitpunkten in Stunden
* @param startTime Startzeit
* @param endTime Endzeit
* @return Differenz in Stunden als Double
*/
public static Double calculateHoursBetween(LocalTime startTime, LocalTime endTime) {
if (startTime == null || endTime == null) {
return 0.0;
}
Duration duration = Duration.between(startTime, endTime);
return duration.toMinutes() / 60.0;
}
/**
* Addiert zwei Zeitangaben im "HH:mm" Format
* @param time1 Erste Zeitangabe
* @param time2 Zweite Zeitangabe
* @return Summe im "HH:mm" Format
*/
public static String addHours(String time1, String time2) {
Double hours1 = parseHours(time1);
Double hours2 = parseHours(time2);
return formatHours(hours1 + hours2);
}
/**
* Subtrahiert zwei Zeitangaben im "HH:mm" Format
* @param time1 Erste Zeitangabe (Minuend)
* @param time2 Zweite Zeitangabe (Subtrahend)
* @return Differenz im "HH:mm" Format
*/
public static String subtractHours(String time1, String time2) {
Double hours1 = parseHours(time1);
Double hours2 = parseHours(time2);
return formatHours(hours1 - hours2);
}
// ==================== DATUM-BERECHNUNGEN ====================
/**
* Prüft ob ein Datum ein Werktag ist (Montag bis Freitag)
* @param date Das zu prüfende Datum
* @return true wenn Werktag, false wenn Wochenende
*/
public static boolean isWorkday(LocalDate date) {
if (date == null) {
return false;
}
DayOfWeek dayOfWeek = date.getDayOfWeek();
return dayOfWeek != DayOfWeek.SATURDAY && dayOfWeek != DayOfWeek.SUNDAY;
}
/**
* Berechnet alle Werktage zwischen zwei Daten (inklusive)
* @param startDate Startdatum
* @param endDate Enddatum
* @return Anzahl der Werktage
*/
public static long countWorkdays(LocalDate startDate, LocalDate endDate) {
if (startDate == null || endDate == null || startDate.isAfter(endDate)) {
return 0;
}
return startDate.datesUntil(endDate.plusDays(1))
.filter(DateTimeUtils::isWorkday)
.count();
}
/**
* Gibt alle Werktage zwischen zwei Daten zurück
* @param startDate Startdatum
* @param endDate Enddatum
* @return Liste aller Werktage
*/
public static List<LocalDate> getWorkdaysBetween(LocalDate startDate, LocalDate endDate) {
if (startDate == null || endDate == null || startDate.isAfter(endDate)) {
return new ArrayList<>();
}
return startDate.datesUntil(endDate.plusDays(1))
.filter(DateTimeUtils::isWorkday)
.toList();
}
/**
* Gibt den ersten Tag des Monats zurück
* @param date Beliebiges Datum im gewünschten Monat
* @return Erster Tag des Monats
*/
public static LocalDate getFirstDayOfMonth(LocalDate date) {
return date != null ? date.with(TemporalAdjusters.firstDayOfMonth()) : null;
}
/**
* Gibt den letzten Tag des Monats zurück
* @param date Beliebiges Datum im gewünschten Monat
* @return Letzter Tag des Monats
*/
public static LocalDate getLastDayOfMonth(LocalDate date) {
return date != null ? date.with(TemporalAdjusters.lastDayOfMonth()) : null;
}
/**
* Gibt den ersten Tag der Woche (Montag) zurück
* @param date Beliebiges Datum in der gewünschten Woche
* @return Montag der Woche
*/
public static LocalDate getFirstDayOfWeek(LocalDate date) {
return date != null ? date.with(TemporalAdjusters.previousOrSame(DayOfWeek.MONDAY)) : null;
}
/**
* Gibt den letzten Tag der Woche (Sonntag) zurück
* @param date Beliebiges Datum in der gewünschten Woche
* @return Sonntag der Woche
*/
public static LocalDate getLastDayOfWeek(LocalDate date) {
return date != null ? date.with(TemporalAdjusters.nextOrSame(DayOfWeek.SUNDAY)) : null;
}
// ==================== VALIDIERUNG ====================
/**
* Prüft ob ein Datum in der Vergangenheit liegt
* @param date Das zu prüfende Datum
* @return true wenn in der Vergangenheit
*/
public static boolean isInPast(LocalDate date) {
return date != null && date.isBefore(LocalDate.now());
}
/**
* Prüft ob ein Datum in der Zukunft liegt
* @param date Das zu prüfende Datum
* @return true wenn in der Zukunft
*/
public static boolean isInFuture(LocalDate date) {
return date != null && date.isAfter(LocalDate.now());
}
/**
* Prüft ob ein Datum heute ist
* @param date Das zu prüfende Datum
* @return true wenn heute
*/
public static boolean isToday(LocalDate date) {
return date != null && date.equals(LocalDate.now());
}
/**
* Prüft ob sich zwei Datumsbereiche überschneiden
* @param start1 Start des ersten Bereichs
* @param end1 Ende des ersten Bereichs
* @param start2 Start des zweiten Bereichs
* @param end2 Ende des zweiten Bereichs
* @return true wenn sich die Bereiche überschneiden
*/
public static boolean dateRangesOverlap(LocalDate start1, LocalDate end1, LocalDate start2, LocalDate end2) {
if (start1 == null || end1 == null || start2 == null || end2 == null) {
return false;
}
return !(end1.isBefore(start2) || start1.isAfter(end2));
}
/**
* Berechnet die Anzahl der Tage zwischen zwei Daten
* @param startDate Startdatum
* @param endDate Enddatum
* @return Anzahl der Tage (kann negativ sein wenn startDate nach endDate liegt)
*/
public static long daysBetween(LocalDate startDate, LocalDate endDate) {
if (startDate == null || endDate == null) {
return 0;
}
return ChronoUnit.DAYS.between(startDate, endDate);
}
// ==================== ZEITZONE-UNTERSTÜTZUNG ====================
/**
* Konvertiert LocalDateTime zu Instant mit System-Zeitzone
* @param dateTime LocalDateTime
* @return Instant
*/
public static Instant toInstant(LocalDateTime dateTime) {
return dateTime != null ? dateTime.atZone(ZoneId.systemDefault()).toInstant() : null;
}
/**
* Konvertiert Instant zu LocalDateTime mit System-Zeitzone
* @param instant Instant
* @return LocalDateTime
*/
public static LocalDateTime fromInstant(Instant instant) {
return instant != null ? LocalDateTime.ofInstant(instant, ZoneId.systemDefault()) : null;
}
// ==================== HILFSMETHODEN FÜR REPORTS ====================
/**
* Erstellt YearMonth aus Jahr und Monat
* @param year Jahr
* @param month Monat (1-12)
* @return YearMonth
* @throws IllegalArgumentException bei ungültigen Werten
*/
public static YearMonth createYearMonth(int year, int month) {
if (month < 1 || month > 12) {
throw new IllegalArgumentException("Monat muss zwischen 1 und 12 liegen");
}
return YearMonth.of(year, month);
}
/**
* Parst YearMonth aus String im Format "yyyy-MM"
* @param yearMonthString String im Format "yyyy-MM"
* @return YearMonth oder null bei ungültigem Format
*/
public static YearMonth parseYearMonth(String yearMonthString) {
if (yearMonthString == null || yearMonthString.trim().isEmpty()) {
return null;
}
try {
return YearMonth.parse(yearMonthString);
} catch (DateTimeParseException e) {
return null;
}
}
}