0
# Date/Time Utilities
1
2
Apache Commons Lang provides comprehensive date and time utilities through DateUtils, StopWatch, and various formatting classes. These utilities offer safe date manipulation, performance timing, and flexible formatting options that complement Java's standard date/time API.
3
4
## Core Classes
5
6
### DateUtils - Date Manipulation and Operations
7
8
Provides 52 static methods for date arithmetic, comparison, and manipulation:
9
10
```java { .api }
11
import org.apache.commons.lang3.time.DateUtils;
12
```
13
14
#### Date Constants
15
16
```java { .api }
17
// Time unit constants in milliseconds
18
public static final long MILLIS_PER_SECOND = 1000L
19
public static final long MILLIS_PER_MINUTE = 60 * MILLIS_PER_SECOND
20
public static final long MILLIS_PER_HOUR = 60 * MILLIS_PER_MINUTE
21
public static final long MILLIS_PER_DAY = 24 * MILLIS_PER_HOUR
22
23
// Calendar field constants
24
public static final int SEMI_MONTH = 1001
25
26
// Range iteration constants
27
public static final int RANGE_WEEK_SUNDAY = 1
28
public static final int RANGE_WEEK_MONDAY = 2
29
public static final int RANGE_WEEK_RELATIVE = 3
30
public static final int RANGE_WEEK_CENTER = 4
31
public static final int RANGE_MONTH_SUNDAY = 5
32
public static final int RANGE_MONTH_MONDAY = 6
33
```
34
35
#### Date Arithmetic Operations
36
37
```java { .api }
38
// Add time units to dates (returns new Date)
39
public static Date addYears(Date date, int amount)
40
public static Date addMonths(Date date, int amount)
41
public static Date addWeeks(Date date, int amount)
42
public static Date addDays(Date date, int amount)
43
public static Date addHours(Date date, int amount)
44
public static Date addMinutes(Date date, int amount)
45
public static Date addSeconds(Date date, int amount)
46
public static Date addMilliseconds(Date date, int amount)
47
```
48
49
**Usage Examples:**
50
```java { .api }
51
Date now = new Date();
52
53
// Adding time units
54
Date nextWeek = DateUtils.addWeeks(now, 1); // 7 days from now
55
Date nextMonth = DateUtils.addMonths(now, 1); // 1 month from now
56
Date nextYear = DateUtils.addYears(now, 1); // 1 year from now
57
Date tomorrow = DateUtils.addDays(now, 1); // Tomorrow
58
Date inTwoHours = DateUtils.addHours(now, 2); // 2 hours from now
59
60
// Subtracting time (use negative amounts)
61
Date lastWeek = DateUtils.addDays(now, -7); // 7 days ago
62
Date lastMonth = DateUtils.addMonths(now, -1); // 1 month ago
63
Date tenMinutesAgo = DateUtils.addMinutes(now, -10); // 10 minutes ago
64
65
// Combining operations
66
Date futureDate = DateUtils.addDays(
67
DateUtils.addHours(now, 2), 5); // 5 days and 2 hours from now
68
```
69
70
#### Date Truncation and Ceiling
71
72
```java { .api }
73
// Truncate dates to specific calendar fields
74
public static Date truncate(Date date, int field)
75
public static Calendar truncate(Calendar date, int field)
76
77
// Round dates up to next calendar field boundary
78
public static Date ceiling(Date date, int field)
79
public static Calendar ceiling(Calendar date, int field)
80
81
// Round dates to nearest calendar field boundary
82
public static Date round(Date date, int field)
83
public static Calendar round(Calendar date, int field)
84
```
85
86
**Usage Examples:**
87
```java { .api }
88
Date now = new Date(); // 2024-03-15 14:30:45.123
89
90
// Truncation (set lower fields to minimum)
91
Date dayStart = DateUtils.truncate(now, Calendar.DAY_OF_MONTH); // 2024-03-15 00:00:00.000
92
Date hourStart = DateUtils.truncate(now, Calendar.HOUR_OF_DAY); // 2024-03-15 14:00:00.000
93
Date monthStart = DateUtils.truncate(now, Calendar.MONTH); // 2024-03-01 00:00:00.000
94
Date yearStart = DateUtils.truncate(now, Calendar.YEAR); // 2024-01-01 00:00:00.000
95
96
// Ceiling (round up to next boundary)
97
Date nextDay = DateUtils.ceiling(now, Calendar.DAY_OF_MONTH); // 2024-03-16 00:00:00.000
98
Date nextHour = DateUtils.ceiling(now, Calendar.HOUR_OF_DAY); // 2024-03-15 15:00:00.000
99
Date nextMonth = DateUtils.ceiling(now, Calendar.MONTH); // 2024-04-01 00:00:00.000
100
101
// Rounding (to nearest boundary)
102
Date rounded = DateUtils.round(now, Calendar.HOUR_OF_DAY); // 2024-03-15 15:00:00.000 (rounds up at 30+ minutes)
103
```
104
105
#### Date Comparison and Equality
106
107
```java { .api }
108
// Date comparison methods
109
public static boolean isSameDay(Date date1, Date date2)
110
public static boolean isSameDay(Calendar cal1, Calendar cal2)
111
public static boolean isSameInstant(Date date1, Date date2)
112
public static boolean isSameInstant(Calendar cal1, Calendar cal2)
113
public static boolean isSameLocalTime(Calendar cal1, Calendar cal2)
114
```
115
116
**Usage Examples:**
117
```java { .api }
118
Date date1 = new Date();
119
Date date2 = DateUtils.addHours(date1, 5); // Same day, different time
120
Date date3 = DateUtils.addDays(date1, 1); // Different day
121
122
// Same day comparison (ignores time)
123
boolean sameDay = DateUtils.isSameDay(date1, date2); // true
124
boolean differentDay = DateUtils.isSameDay(date1, date3); // false
125
126
// Same instant comparison (exact timestamp)
127
boolean sameInstant = DateUtils.isSameInstant(date1, date1); // true
128
boolean differentInstant = DateUtils.isSameInstant(date1, date2); // false
129
130
// Calendar comparison
131
Calendar cal1 = Calendar.getInstance();
132
Calendar cal2 = Calendar.getInstance();
133
cal2.setTime(date2);
134
boolean sameLocalTime = DateUtils.isSameLocalTime(cal1, cal2); // false
135
```
136
137
#### Date Parsing
138
139
```java { .api }
140
// Parse dates with multiple format attempts
141
public static Date parseDate(String str, String... parsePatterns) throws ParseException
142
public static Date parseDate(String str, Locale locale, String... parsePatterns) throws ParseException
143
public static Date parseDateStrictly(String str, String... parsePatterns) throws ParseException
144
public static Date parseDateStrictly(String str, Locale locale, String... parsePatterns) throws ParseException
145
```
146
147
**Usage Examples:**
148
```java { .api }
149
// Multiple format parsing
150
String[] patterns = {
151
"yyyy-MM-dd",
152
"dd/MM/yyyy",
153
"MM-dd-yyyy",
154
"yyyy-MM-dd HH:mm:ss"
155
};
156
157
try {
158
// Try multiple formats until one succeeds
159
Date date1 = DateUtils.parseDate("2024-03-15", patterns); // Uses first pattern
160
Date date2 = DateUtils.parseDate("15/03/2024", patterns); // Uses second pattern
161
Date date3 = DateUtils.parseDate("03-15-2024", patterns); // Uses third pattern
162
Date date4 = DateUtils.parseDate("2024-03-15 14:30:00", patterns); // Uses fourth pattern
163
164
// Strict parsing (no lenient parsing)
165
Date strict = DateUtils.parseDateStrictly("2024-02-30", patterns); // Throws ParseException
166
167
} catch (ParseException e) {
168
// Handle parsing failure
169
System.err.println("Unable to parse date: " + e.getMessage());
170
}
171
172
// Locale-specific parsing
173
try {
174
Date localeDate = DateUtils.parseDate("15 mars 2024", Locale.FRENCH, "dd MMMM yyyy");
175
} catch (ParseException e) {
176
// Handle error
177
}
178
```
179
180
#### Date Range Iteration
181
182
```java { .api }
183
// Iterate over date ranges
184
public static Iterator<Calendar> iterator(Date focus, int rangeStyle)
185
public static Iterator<Calendar> iterator(Calendar focus, int rangeStyle)
186
```
187
188
**Usage Examples:**
189
```java { .api }
190
Date today = new Date();
191
192
// Iterate over week (Sunday to Saturday)
193
Iterator<Calendar> weekIterator = DateUtils.iterator(today, DateUtils.RANGE_WEEK_SUNDAY);
194
List<Date> weekDates = new ArrayList<>();
195
while (weekIterator.hasNext()) {
196
weekDates.add(weekIterator.next().getTime());
197
}
198
199
// Iterate over month view (includes partial weeks)
200
Iterator<Calendar> monthIterator = DateUtils.iterator(today, DateUtils.RANGE_MONTH_SUNDAY);
201
List<Date> monthViewDates = new ArrayList<>();
202
while (monthIterator.hasNext()) {
203
monthViewDates.add(monthIterator.next().getTime());
204
}
205
206
// Custom iteration
207
public List<Date> getDateRange(Date start, Date end) {
208
List<Date> dates = new ArrayList<>();
209
Date current = DateUtils.truncate(start, Calendar.DAY_OF_MONTH);
210
Date endDate = DateUtils.truncate(end, Calendar.DAY_OF_MONTH);
211
212
while (!current.after(endDate)) {
213
dates.add(new Date(current.getTime()));
214
current = DateUtils.addDays(current, 1);
215
}
216
217
return dates;
218
}
219
```
220
221
### StopWatch - Performance Timing
222
223
Provides high-precision timing for performance monitoring and benchmarking:
224
225
```java { .api }
226
import org.apache.commons.lang3.time.StopWatch;
227
```
228
229
#### Basic Stopwatch Operations
230
231
```java { .api }
232
// Stopwatch creation
233
public static StopWatch create()
234
public static StopWatch createStarted()
235
236
// Timing control
237
public StopWatch start()
238
public StopWatch stop()
239
public StopWatch reset()
240
public StopWatch suspend()
241
public StopWatch resume()
242
243
// Time retrieval
244
public long getTime()
245
public long getTime(TimeUnit timeUnit)
246
public long getNanoTime()
247
public long getSplitTime()
248
public long getSplitNanoTime()
249
```
250
251
**Usage Examples:**
252
```java { .api }
253
// Basic timing
254
StopWatch stopwatch = StopWatch.createStarted();
255
performExpensiveOperation();
256
stopwatch.stop();
257
258
long milliseconds = stopwatch.getTime(); // Time in milliseconds
259
long nanoseconds = stopwatch.getNanoTime(); // Time in nanoseconds
260
long seconds = stopwatch.getTime(TimeUnit.SECONDS); // Time in seconds
261
262
System.out.println("Operation took: " + milliseconds + "ms");
263
264
// Reusable stopwatch
265
StopWatch reusableTimer = StopWatch.create();
266
for (int i = 0; i < 10; i++) {
267
reusableTimer.reset();
268
reusableTimer.start();
269
270
performOperation(i);
271
272
reusableTimer.stop();
273
System.out.printf("Iteration %d: %dms%n", i, reusableTimer.getTime());
274
}
275
```
276
277
#### Advanced Timing Features
278
279
```java { .api }
280
// Split timing (lap times)
281
StopWatch lapTimer = StopWatch.createStarted();
282
283
performTask1();
284
lapTimer.split();
285
long task1Time = lapTimer.getSplitTime();
286
System.out.println("Task 1: " + task1Time + "ms");
287
288
performTask2();
289
lapTimer.split();
290
long task2Time = lapTimer.getSplitTime() - task1Time;
291
System.out.println("Task 2: " + task2Time + "ms");
292
293
lapTimer.stop();
294
long totalTime = lapTimer.getTime();
295
System.out.println("Total: " + totalTime + "ms");
296
297
// Suspend and resume
298
StopWatch pausableTimer = StopWatch.createStarted();
299
performPartialTask();
300
301
pausableTimer.suspend(); // Pause timing
302
handleInterruption(); // This time is not counted
303
304
pausableTimer.resume(); // Resume timing
305
completeTask();
306
307
pausableTimer.stop();
308
long actualWorkTime = pausableTimer.getTime(); // Only counts non-suspended time
309
```
310
311
### DateFormatUtils - Date Formatting
312
313
Provides thread-safe date formatting utilities:
314
315
```java { .api }
316
import org.apache.commons.lang3.time.DateFormatUtils;
317
```
318
319
#### Predefined ISO Formats
320
321
```java { .api }
322
// ISO format constants
323
public static final FastDateFormat ISO_DATETIME_FORMAT // yyyy-MM-dd'T'HH:mm:ss
324
public static final FastDateFormat ISO_DATETIME_TIME_ZONE_FORMAT // yyyy-MM-dd'T'HH:mm:ssZZ
325
public static final FastDateFormat ISO_DATE_FORMAT // yyyy-MM-dd
326
public static final FastDateFormat ISO_TIME_NO_T_FORMAT // HH:mm:ss
327
public static final FastDateFormat ISO_TIME_NO_T_TIME_ZONE_FORMAT // HH:mm:ssZZ
328
```
329
330
#### Date Formatting Methods
331
332
```java { .api }
333
// Format dates with patterns
334
public static String format(Date date, String pattern)
335
public static String format(Date date, String pattern, Locale locale)
336
public static String format(Date date, String pattern, TimeZone timeZone)
337
public static String format(Date date, String pattern, TimeZone timeZone, Locale locale)
338
339
// Format timestamps
340
public static String format(long millis, String pattern)
341
public static String format(long millis, String pattern, Locale locale)
342
public static String format(long millis, String pattern, TimeZone timeZone)
343
344
// Format Calendar objects
345
public static String format(Calendar calendar, String pattern)
346
public static String format(Calendar calendar, String pattern, Locale locale)
347
public static String format(Calendar calendar, String pattern, TimeZone timeZone)
348
```
349
350
**Usage Examples:**
351
```java { .api }
352
Date now = new Date();
353
354
// Basic formatting
355
String date1 = DateFormatUtils.format(now, "yyyy-MM-dd"); // "2024-03-15"
356
String date2 = DateFormatUtils.format(now, "dd/MM/yyyy HH:mm:ss"); // "15/03/2024 14:30:45"
357
String date3 = DateFormatUtils.format(now, "EEEE, MMMM d, yyyy"); // "Friday, March 15, 2024"
358
359
// ISO formats
360
String iso1 = DateFormatUtils.ISO_DATE_FORMAT.format(now); // "2024-03-15"
361
String iso2 = DateFormatUtils.ISO_DATETIME_FORMAT.format(now); // "2024-03-15T14:30:45"
362
363
// Timezone and locale formatting
364
TimeZone utc = TimeZone.getTimeZone("UTC");
365
String utcDate = DateFormatUtils.format(now, "yyyy-MM-dd HH:mm:ss", utc); // UTC time
366
367
Locale french = Locale.FRENCH;
368
String frenchDate = DateFormatUtils.format(now, "EEEE d MMMM yyyy", french); // "vendredi 15 mars 2024"
369
370
// Timestamp formatting
371
long timestamp = System.currentTimeMillis();
372
String formatted = DateFormatUtils.format(timestamp, "yyyy-MM-dd HH:mm:ss.SSS");
373
```
374
375
### DurationFormatUtils - Duration Formatting
376
377
Provides utilities for formatting time durations:
378
379
```java { .api }
380
import org.apache.commons.lang3.time.DurationFormatUtils;
381
```
382
383
#### Duration Formatting Methods
384
385
```java { .api }
386
// Format duration in milliseconds
387
public static String formatDuration(long durationMillis, String format)
388
public static String formatDuration(long durationMillis, String format, boolean padWithZeros)
389
390
// Predefined formats
391
public static String formatDurationHMS(long durationMillis) // H:mm:ss
392
public static String formatDurationISO(long durationMillis) // ISO 8601 duration
393
394
// Format periods between two timestamps
395
public static String formatPeriod(long startMillis, long endMillis, String format)
396
public static String formatPeriodISO(long startMillis, long endMillis)
397
398
// Human-readable duration
399
public static String formatDurationWords(long durationMillis, boolean suppressLeadingZeroElements, boolean suppressTrailingZeroElements)
400
```
401
402
**Usage Examples:**
403
```java { .api }
404
long duration = 3_661_000L; // 1 hour, 1 minute, 1 second in milliseconds
405
406
// Basic formatting
407
String formatted1 = DurationFormatUtils.formatDuration(duration, "HH:mm:ss"); // "01:01:01"
408
String formatted2 = DurationFormatUtils.formatDuration(duration, "H:mm:ss"); // "1:01:01"
409
String formatted3 = DurationFormatUtils.formatDuration(duration, "H 'hours', m 'minutes', s 'seconds'"); // "1 hours, 1 minutes, 1 seconds"
410
411
// Predefined formats
412
String hms = DurationFormatUtils.formatDurationHMS(duration); // "1:01:01"
413
String iso = DurationFormatUtils.formatDurationISO(duration); // "PT1H1M1S"
414
415
// Human-readable format
416
String words = DurationFormatUtils.formatDurationWords(duration, true, true); // "1 hour 1 minute 1 second"
417
418
// Period formatting
419
long start = System.currentTimeMillis();
420
Thread.sleep(5000); // Simulate 5-second operation
421
long end = System.currentTimeMillis();
422
423
String period = DurationFormatUtils.formatPeriod(start, end, "ss.SSS 'seconds'"); // "5.000 seconds"
424
String isoPeriod = DurationFormatUtils.formatPeriodISO(start, end); // "PT5S"
425
```
426
427
## Advanced Date/Time Operations
428
429
### Custom Date Utilities
430
431
```java { .api }
432
public class CustomDateUtils {
433
434
// Business day calculations
435
public static Date addBusinessDays(Date date, int businessDays) {
436
Date result = new Date(date.getTime());
437
int added = 0;
438
439
while (added < businessDays) {
440
result = DateUtils.addDays(result, 1);
441
Calendar cal = Calendar.getInstance();
442
cal.setTime(result);
443
444
int dayOfWeek = cal.get(Calendar.DAY_OF_WEEK);
445
if (dayOfWeek != Calendar.SATURDAY && dayOfWeek != Calendar.SUNDAY) {
446
added++;
447
}
448
}
449
450
return result;
451
}
452
453
// Age calculation
454
public static int calculateAge(Date birthDate, Date currentDate) {
455
Calendar birth = Calendar.getInstance();
456
Calendar current = Calendar.getInstance();
457
birth.setTime(birthDate);
458
current.setTime(currentDate);
459
460
int age = current.get(Calendar.YEAR) - birth.get(Calendar.YEAR);
461
462
if (current.get(Calendar.DAY_OF_YEAR) < birth.get(Calendar.DAY_OF_YEAR)) {
463
age--;
464
}
465
466
return age;
467
}
468
469
// Quarter calculations
470
public static int getQuarter(Date date) {
471
Calendar cal = Calendar.getInstance();
472
cal.setTime(date);
473
return (cal.get(Calendar.MONTH) / 3) + 1;
474
}
475
476
public static Date getQuarterStart(Date date) {
477
Calendar cal = Calendar.getInstance();
478
cal.setTime(date);
479
480
int quarter = getQuarter(date);
481
int startMonth = (quarter - 1) * 3;
482
483
cal.set(Calendar.MONTH, startMonth);
484
return DateUtils.truncate(cal.getTime(), Calendar.MONTH);
485
}
486
487
// Time zone conversions
488
public static Date convertTimeZone(Date date, TimeZone from, TimeZone to) {
489
long offset = to.getOffset(date.getTime()) - from.getOffset(date.getTime());
490
return new Date(date.getTime() + offset);
491
}
492
}
493
```
494
495
### Performance Monitoring
496
497
```java { .api }
498
public class PerformanceMonitor {
499
500
private final Map<String, StopWatch> timers = new ConcurrentHashMap<>();
501
private final Map<String, List<Long>> measurements = new ConcurrentHashMap<>();
502
503
// Start timing an operation
504
public void startTimer(String operation) {
505
StopWatch timer = timers.computeIfAbsent(operation, k -> StopWatch.create());
506
timer.reset();
507
timer.start();
508
}
509
510
// Stop timing and record measurement
511
public long stopTimer(String operation) {
512
StopWatch timer = timers.get(operation);
513
if (timer != null && timer.isStarted()) {
514
timer.stop();
515
long duration = timer.getTime();
516
517
measurements.computeIfAbsent(operation, k -> new ArrayList<>()).add(duration);
518
return duration;
519
}
520
return -1;
521
}
522
523
// Get statistics for an operation
524
public OperationStats getStats(String operation) {
525
List<Long> times = measurements.get(operation);
526
if (times == null || times.isEmpty()) {
527
return null;
528
}
529
530
return new OperationStats(
531
times.size(),
532
Collections.min(times),
533
Collections.max(times),
534
times.stream().mapToLong(Long::longValue).average().orElse(0.0)
535
);
536
}
537
538
// Auto-timing with try-with-resources
539
public Timer time(String operation) {
540
return new Timer(operation, this);
541
}
542
543
public static class Timer implements AutoCloseable {
544
private final String operation;
545
private final PerformanceMonitor monitor;
546
private final StopWatch stopwatch;
547
548
Timer(String operation, PerformanceMonitor monitor) {
549
this.operation = operation;
550
this.monitor = monitor;
551
this.stopwatch = StopWatch.createStarted();
552
}
553
554
@Override
555
public void close() {
556
stopwatch.stop();
557
monitor.measurements
558
.computeIfAbsent(operation, k -> new ArrayList<>())
559
.add(stopwatch.getTime());
560
}
561
}
562
563
public static class OperationStats {
564
public final int count;
565
public final long min;
566
public final long max;
567
public final double average;
568
569
OperationStats(int count, long min, long max, double average) {
570
this.count = count;
571
this.min = min;
572
this.max = max;
573
this.average = average;
574
}
575
576
@Override
577
public String toString() {
578
return String.format("Count: %d, Min: %dms, Max: %dms, Avg: %.2fms",
579
count, min, max, average);
580
}
581
}
582
}
583
584
// Usage example
585
PerformanceMonitor monitor = new PerformanceMonitor();
586
587
// Manual timing
588
monitor.startTimer("database-query");
589
executeQuery();
590
long queryTime = monitor.stopTimer("database-query");
591
592
// Auto timing with try-with-resources
593
try (PerformanceMonitor.Timer timer = monitor.time("api-call")) {
594
callExternalApi();
595
}
596
597
// Get statistics
598
OperationStats stats = monitor.getStats("database-query");
599
System.out.println("Database query stats: " + stats);
600
```
601
602
## Integration Examples
603
604
### Spring Framework Integration
605
606
```java { .api }
607
@Service
608
public class DateService {
609
610
private static final String[] DATE_PATTERNS = {
611
"yyyy-MM-dd",
612
"dd/MM/yyyy",
613
"MM-dd-yyyy HH:mm:ss",
614
"yyyy-MM-dd'T'HH:mm:ss",
615
"yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"
616
};
617
618
public Date parseFlexibleDate(String dateStr) throws ParseException {
619
if (StringUtils.isBlank(dateStr)) {
620
throw new IllegalArgumentException("Date string cannot be blank");
621
}
622
623
return DateUtils.parseDate(dateStr.trim(), DATE_PATTERNS);
624
}
625
626
public boolean isBusinessDay(Date date) {
627
Calendar cal = Calendar.getInstance();
628
cal.setTime(date);
629
int dayOfWeek = cal.get(Calendar.DAY_OF_WEEK);
630
return dayOfWeek >= Calendar.MONDAY && dayOfWeek <= Calendar.FRIDAY;
631
}
632
633
@Cacheable("date-ranges")
634
public List<Date> getDateRange(Date start, Date end) {
635
List<Date> dates = new ArrayList<>();
636
Date current = DateUtils.truncate(start, Calendar.DAY_OF_MONTH);
637
Date endDate = DateUtils.truncate(end, Calendar.DAY_OF_MONTH);
638
639
while (!current.after(endDate)) {
640
dates.add(new Date(current.getTime()));
641
current = DateUtils.addDays(current, 1);
642
}
643
644
return dates;
645
}
646
}
647
```
648
649
### Configuration Properties
650
651
```java { .api }
652
@ConfigurationProperties(prefix = "app.timing")
653
@Data
654
public class TimingConfiguration {
655
656
private Duration cacheTimeout = Duration.ofMinutes(30);
657
private Duration requestTimeout = Duration.ofSeconds(30);
658
private String dateFormat = "yyyy-MM-dd HH:mm:ss";
659
private String timeZone = "UTC";
660
661
public FastDateFormat getDateFormatter() {
662
TimeZone tz = TimeZone.getTimeZone(timeZone);
663
return FastDateFormat.getInstance(dateFormat, tz);
664
}
665
666
public String formatCurrentTime() {
667
return getDateFormatter().format(new Date());
668
}
669
}
670
```
671
672
### Audit and Logging
673
674
```java { .api }
675
@Component
676
public class AuditLogger {
677
678
private final FastDateFormat timestampFormat =
679
FastDateFormat.getInstance("yyyy-MM-dd HH:mm:ss.SSS");
680
681
public void logOperation(String operation, Runnable task) {
682
StopWatch stopwatch = StopWatch.createStarted();
683
String startTime = timestampFormat.format(new Date());
684
685
try {
686
task.run();
687
stopwatch.stop();
688
689
log.info("Operation '{}' started at {} and completed in {}ms",
690
operation, startTime, stopwatch.getTime());
691
} catch (Exception e) {
692
stopwatch.stop();
693
694
log.error("Operation '{}' started at {} and failed after {}ms: {}",
695
operation, startTime, stopwatch.getTime(), e.getMessage());
696
throw e;
697
}
698
}
699
700
public void logScheduledTask(String taskName, Date scheduledTime, Date actualTime) {
701
long delay = actualTime.getTime() - scheduledTime.getTime();
702
String delayFormat = DurationFormatUtils.formatDurationWords(Math.abs(delay), true, true);
703
704
if (delay > 0) {
705
log.warn("Task '{}' executed {} late (scheduled: {}, actual: {})",
706
taskName, delayFormat,
707
timestampFormat.format(scheduledTime),
708
timestampFormat.format(actualTime));
709
} else {
710
log.info("Task '{}' executed on time at {}",
711
taskName, timestampFormat.format(actualTime));
712
}
713
}
714
}
715
```
716
717
The date/time utilities in Apache Commons Lang provide essential functionality for date manipulation, performance timing, and formatting that complements Java's built-in date/time capabilities while offering better null safety and more convenient APIs.