0
# JSR310 Date/Time Parameters
1
2
Parameter converters for Java 8 date/time API types providing type-safe parsing of date and time values from HTTP requests. Supports all major JSR310 temporal types with automatic validation and error handling.
3
4
## Capabilities
5
6
### Instant Parameters
7
8
Parameter converters for Instant values representing points in time.
9
10
```java { .api }
11
/**
12
* Parameter wrapper for Instant values
13
* Parses ISO-8601 instant strings (e.g., "2023-12-25T10:30:00Z")
14
*/
15
public class InstantParam extends AbstractParam<Instant> {
16
17
/** Creates InstantParam from ISO-8601 string input */
18
public InstantParam(String input);
19
20
/** Creates InstantParam with custom parameter name for error messages */
21
public InstantParam(String input, String parameterName);
22
}
23
24
/**
25
* Parameter wrapper for Instant values from seconds since epoch
26
* Parses numeric strings as seconds since Unix epoch
27
*/
28
public class InstantSecondParam extends AbstractParam<Instant> {
29
30
/** Creates InstantSecondParam from seconds string input */
31
public InstantSecondParam(String input);
32
33
/** Creates InstantSecondParam with custom parameter name */
34
public InstantSecondParam(String input, String parameterName);
35
}
36
```
37
38
**Usage Examples:**
39
40
```java
41
import io.dropwizard.jersey.jsr310.*;
42
import jakarta.ws.rs.*;
43
import java.time.Instant;
44
45
@Path("/events")
46
public class EventResource {
47
48
@GET
49
public List<Event> getEvents(@QueryParam("since") InstantParam since,
50
@QueryParam("until") InstantParam until) {
51
Instant sinceTime = since != null ? since.get() : Instant.now().minus(Duration.ofDays(7));
52
Instant untilTime = until != null ? until.get() : Instant.now();
53
54
return eventService.getEventsBetween(sinceTime, untilTime);
55
}
56
57
@GET
58
@Path("/unix")
59
public List<Event> getEventsUnix(@QueryParam("since") InstantSecondParam since) {
60
// Handles Unix timestamps like "1703505000"
61
Instant sinceTime = since != null ? since.get() : Instant.now().minus(Duration.ofDays(1));
62
return eventService.getEventsSince(sinceTime);
63
}
64
}
65
66
// Example URLs:
67
// GET /events?since=2023-12-25T10:30:00Z&until=2023-12-26T10:30:00Z
68
// GET /events/unix?since=1703505000
69
```
70
71
### Local Date/Time Parameters
72
73
Parameter converters for local date and time values without timezone information.
74
75
```java { .api }
76
/**
77
* Parameter wrapper for LocalDate values
78
* Parses ISO-8601 date strings (e.g., "2023-12-25")
79
*/
80
public class LocalDateParam extends AbstractParam<LocalDate> {
81
82
/** Creates LocalDateParam from ISO-8601 date string */
83
public LocalDateParam(String input);
84
85
/** Creates LocalDateParam with custom parameter name */
86
public LocalDateParam(String input, String parameterName);
87
}
88
89
/**
90
* Parameter wrapper for LocalDateTime values
91
* Parses ISO-8601 date-time strings (e.g., "2023-12-25T10:30:00")
92
*/
93
public class LocalDateTimeParam extends AbstractParam<LocalDateTime> {
94
95
/** Creates LocalDateTimeParam from ISO-8601 date-time string */
96
public LocalDateTimeParam(String input);
97
98
/** Creates LocalDateTimeParam with custom parameter name */
99
public LocalDateTimeParam(String input, String parameterName);
100
}
101
102
/**
103
* Parameter wrapper for LocalTime values
104
* Parses ISO-8601 time strings (e.g., "10:30:00")
105
*/
106
public class LocalTimeParam extends AbstractParam<LocalTime> {
107
108
/** Creates LocalTimeParam from ISO-8601 time string */
109
public LocalTimeParam(String input);
110
111
/** Creates LocalTimeParam with custom parameter name */
112
public LocalTimeParam(String input, String parameterName);
113
}
114
```
115
116
**Usage Examples:**
117
118
```java
119
import java.time.*;
120
import jakarta.ws.rs.*;
121
122
@Path("/schedule")
123
public class ScheduleResource {
124
125
@GET
126
@Path("/daily")
127
public List<Appointment> getDailySchedule(@QueryParam("date") LocalDateParam date) {
128
LocalDate scheduleDate = date != null ? date.get() : LocalDate.now();
129
return scheduleService.getAppointmentsForDate(scheduleDate);
130
}
131
132
@GET
133
@Path("/appointments")
134
public List<Appointment> getAppointments(@QueryParam("from") LocalDateTimeParam from,
135
@QueryParam("to") LocalDateTimeParam to) {
136
LocalDateTime fromTime = from != null ? from.get() : LocalDateTime.now();
137
LocalDateTime toTime = to != null ? to.get() : fromTime.plusDays(1);
138
139
return scheduleService.getAppointmentsBetween(fromTime, toTime);
140
}
141
142
@GET
143
@Path("/available")
144
public List<TimeSlot> getAvailableSlots(@QueryParam("date") LocalDateParam date,
145
@QueryParam("after") LocalTimeParam afterTime) {
146
LocalDate checkDate = date != null ? date.get() : LocalDate.now();
147
LocalTime startTime = afterTime != null ? afterTime.get() : LocalTime.of(9, 0);
148
149
return scheduleService.getAvailableSlots(checkDate, startTime);
150
}
151
}
152
153
// Example URLs:
154
// GET /schedule/daily?date=2023-12-25
155
// GET /schedule/appointments?from=2023-12-25T09:00:00&to=2023-12-25T17:00:00
156
// GET /schedule/available?date=2023-12-25&after=10:30:00
157
```
158
159
### Offset and Zoned Date/Time Parameters
160
161
Parameter converters for date/time values with timezone information.
162
163
```java { .api }
164
/**
165
* Parameter wrapper for OffsetDateTime values
166
* Parses ISO-8601 offset date-time strings (e.g., "2023-12-25T10:30:00+01:00")
167
*/
168
public class OffsetDateTimeParam extends AbstractParam<OffsetDateTime> {
169
170
/** Creates OffsetDateTimeParam from ISO-8601 offset date-time string */
171
public OffsetDateTimeParam(String input);
172
173
/** Creates OffsetDateTimeParam with custom parameter name */
174
public OffsetDateTimeParam(String input, String parameterName);
175
}
176
177
/**
178
* Parameter wrapper for ZonedDateTime values
179
* Parses ISO-8601 zoned date-time strings (e.g., "2023-12-25T10:30:00[Europe/London]")
180
*/
181
public class ZonedDateTimeParam extends AbstractParam<ZonedDateTime> {
182
183
/** Creates ZonedDateTimeParam from ISO-8601 zoned date-time string */
184
public ZonedDateTimeParam(String input);
185
186
/** Creates ZonedDateTimeParam with custom parameter name */
187
public ZonedDateTimeParam(String input, String parameterName);
188
}
189
190
/**
191
* Parameter wrapper for ZoneId values
192
* Parses zone ID strings (e.g., "Europe/London", "UTC", "+01:00")
193
*/
194
public class ZoneIdParam extends AbstractParam<ZoneId> {
195
196
/** Creates ZoneIdParam from zone ID string */
197
public ZoneIdParam(String input);
198
199
/** Creates ZoneIdParam with custom parameter name */
200
public ZoneIdParam(String input, String parameterName);
201
}
202
```
203
204
**Usage Examples:**
205
206
```java
207
import java.time.*;
208
import jakarta.ws.rs.*;
209
210
@Path("/global")
211
public class GlobalTimeResource {
212
213
@GET
214
@Path("/meetings")
215
public List<Meeting> getMeetings(@QueryParam("start") OffsetDateTimeParam start,
216
@QueryParam("timezone") ZoneIdParam timezone) {
217
218
OffsetDateTime startTime = start != null ? start.get() : OffsetDateTime.now();
219
ZoneId zone = timezone != null ? timezone.get() : ZoneId.systemDefault();
220
221
return meetingService.getMeetingsInTimezone(startTime, zone);
222
}
223
224
@GET
225
@Path("/schedule/{zone}")
226
public WorkSchedule getScheduleForZone(@PathParam("zone") ZoneIdParam zoneParam,
227
@QueryParam("date") LocalDateParam date) {
228
229
ZoneId zone = zoneParam.get();
230
LocalDate scheduleDate = date != null ? date.get() : LocalDate.now(zone);
231
232
return scheduleService.getScheduleForZone(zone, scheduleDate);
233
}
234
235
@POST
236
@Path("/events")
237
public Response createEvent(@FormParam("startTime") ZonedDateTimeParam startTime,
238
@FormParam("endTime") ZonedDateTimeParam endTime,
239
@Valid CreateEventRequest request) {
240
241
Event event = eventService.createEvent(
242
request,
243
startTime.get(),
244
endTime.get()
245
);
246
247
return Response.status(201).entity(event).build();
248
}
249
}
250
251
// Example URLs:
252
// GET /global/meetings?start=2023-12-25T10:30:00+01:00&timezone=Europe/London
253
// GET /global/schedule/America/New_York?date=2023-12-25
254
```
255
256
### Year and Month Parameters
257
258
Parameter converters for year and year-month values for date range operations.
259
260
```java { .api }
261
/**
262
* Parameter wrapper for Year values
263
* Parses year strings (e.g., "2023")
264
*/
265
public class YearParam extends AbstractParam<Year> {
266
267
/** Creates YearParam from year string */
268
public YearParam(String input);
269
270
/** Creates YearParam with custom parameter name */
271
public YearParam(String input, String parameterName);
272
}
273
274
/**
275
* Parameter wrapper for YearMonth values
276
* Parses year-month strings (e.g., "2023-12")
277
*/
278
public class YearMonthParam extends AbstractParam<YearMonth> {
279
280
/** Creates YearMonthParam from year-month string */
281
public YearMonthParam(String input);
282
283
/** Creates YearMonthParam with custom parameter name */
284
public YearMonthParam(String input, String parameterName);
285
}
286
```
287
288
**Usage Examples:**
289
290
```java
291
import java.time.*;
292
import jakarta.ws.rs.*;
293
294
@Path("/reports")
295
public class ReportsResource {
296
297
@GET
298
@Path("/monthly")
299
public MonthlyReport getMonthlyReport(@QueryParam("month") YearMonthParam month) {
300
YearMonth reportMonth = month != null ? month.get() : YearMonth.now();
301
return reportService.getMonthlyReport(reportMonth);
302
}
303
304
@GET
305
@Path("/yearly")
306
public YearlyReport getYearlyReport(@QueryParam("year") YearParam year) {
307
Year reportYear = year != null ? year.get() : Year.now();
308
return reportService.getYearlyReport(reportYear);
309
}
310
311
@GET
312
@Path("/quarterly")
313
public List<QuarterlyReport> getQuarterlyReports(@QueryParam("year") YearParam year) {
314
Year reportYear = year != null ? year.get() : Year.now();
315
316
return List.of(
317
reportService.getQuarterlyReport(reportYear, 1),
318
reportService.getQuarterlyReport(reportYear, 2),
319
reportService.getQuarterlyReport(reportYear, 3),
320
reportService.getQuarterlyReport(reportYear, 4)
321
);
322
}
323
324
@GET
325
@Path("/range")
326
public Report getReportForRange(@QueryParam("from") YearMonthParam from,
327
@QueryParam("to") YearMonthParam to) {
328
329
YearMonth fromMonth = from != null ? from.get() : YearMonth.now().minusMonths(12);
330
YearMonth toMonth = to != null ? to.get() : YearMonth.now();
331
332
return reportService.getReportForRange(fromMonth, toMonth);
333
}
334
}
335
336
// Example URLs:
337
// GET /reports/monthly?month=2023-12
338
// GET /reports/yearly?year=2023
339
// GET /reports/range?from=2023-01&to=2023-12
340
```
341
342
## Date/Time Patterns and Formats
343
344
### Supported Input Formats
345
346
```java
347
public class DateTimeFormats {
348
349
// InstantParam supports:
350
// "2023-12-25T10:30:00Z"
351
// "2023-12-25T10:30:00.123Z"
352
// "2023-12-25T10:30:00+01:00"
353
354
// InstantSecondParam supports:
355
// "1703505000" (Unix timestamp in seconds)
356
357
// LocalDateParam supports:
358
// "2023-12-25"
359
360
// LocalDateTimeParam supports:
361
// "2023-12-25T10:30:00"
362
// "2023-12-25T10:30:00.123"
363
364
// LocalTimeParam supports:
365
// "10:30:00"
366
// "10:30:00.123"
367
// "10:30"
368
369
// OffsetDateTimeParam supports:
370
// "2023-12-25T10:30:00+01:00"
371
// "2023-12-25T10:30:00-05:00"
372
// "2023-12-25T10:30:00Z"
373
374
// ZonedDateTimeParam supports:
375
// "2023-12-25T10:30:00[Europe/London]"
376
// "2023-12-25T10:30:00+01:00[Europe/London]"
377
378
// ZoneIdParam supports:
379
// "UTC"
380
// "Europe/London"
381
// "America/New_York"
382
// "+01:00"
383
// "-05:00"
384
385
// YearParam supports:
386
// "2023"
387
388
// YearMonthParam supports:
389
// "2023-12"
390
}
391
```
392
393
### Error Handling
394
395
All JSR310 parameter types provide consistent error handling:
396
397
```java
398
@Path("/dates")
399
public class DateErrorHandlingResource {
400
401
@GET
402
public Response getDataForDate(@QueryParam("date") LocalDateParam date) {
403
try {
404
LocalDate actualDate = date.get(); // May throw WebApplicationException
405
Data data = dataService.getDataForDate(actualDate);
406
return Response.ok(data).build();
407
408
} catch (WebApplicationException e) {
409
// Parameter parsing failed - returns 400 Bad Request
410
// Error message will be: "date is not a valid date format"
411
throw e;
412
}
413
}
414
}
415
416
// Invalid input examples that result in 400 errors:
417
// GET /dates?date=invalid-date
418
// GET /dates?date=2023-13-45 (invalid month/day)
419
// GET /dates?date=25-12-2023 (wrong format)
420
```
421
422
## Advanced Usage Patterns
423
424
### Date Range Queries
425
426
```java
427
@Path("/analytics")
428
public class AnalyticsResource {
429
430
@GET
431
@Path("/sales")
432
public SalesReport getSalesReport(@QueryParam("from") LocalDateParam from,
433
@QueryParam("to") LocalDateParam to,
434
@QueryParam("timezone") ZoneIdParam timezone) {
435
436
LocalDate fromDate = from != null ? from.get() : LocalDate.now().minusDays(30);
437
LocalDate toDate = to != null ? to.get() : LocalDate.now();
438
ZoneId zone = timezone != null ? timezone.get() : ZoneId.systemDefault();
439
440
// Convert to start/end of day in specified timezone
441
ZonedDateTime startTime = fromDate.atStartOfDay(zone);
442
ZonedDateTime endTime = toDate.plusDays(1).atStartOfDay(zone);
443
444
return analyticsService.getSalesReport(startTime, endTime);
445
}
446
447
@GET
448
@Path("/activity")
449
public ActivityReport getActivityReport(@QueryParam("month") YearMonthParam month,
450
@QueryParam("timezone") ZoneIdParam timezone) {
451
452
YearMonth reportMonth = month != null ? month.get() : YearMonth.now();
453
ZoneId zone = timezone != null ? timezone.get() : ZoneId.systemDefault();
454
455
// Get first and last day of month in timezone
456
LocalDate firstDay = reportMonth.atDay(1);
457
LocalDate lastDay = reportMonth.atEndOfMonth();
458
459
ZonedDateTime start = firstDay.atStartOfDay(zone);
460
ZonedDateTime end = lastDay.plusDays(1).atStartOfDay(zone);
461
462
return analyticsService.getActivityReport(start, end);
463
}
464
}
465
```
466
467
### Time-based Resource Filtering
468
469
```java
470
@Path("/logs")
471
public class LogResource {
472
473
@GET
474
public List<LogEntry> getLogs(@QueryParam("since") InstantParam since,
475
@QueryParam("level") String level,
476
@QueryParam("limit") Integer limit) {
477
478
Instant sinceTime = since != null ? since.get() : Instant.now().minus(Duration.ofHours(1));
479
int maxResults = limit != null ? limit : 100;
480
481
return logService.getLogs(sinceTime, level, maxResults);
482
}
483
484
@GET
485
@Path("/between")
486
public List<LogEntry> getLogsBetween(@QueryParam("start") InstantParam start,
487
@QueryParam("end") InstantParam end) {
488
489
if (start == null || end == null) {
490
throw new WebApplicationException("Both start and end times are required", 400);
491
}
492
493
Instant startTime = start.get();
494
Instant endTime = end.get();
495
496
if (startTime.isAfter(endTime)) {
497
throw new WebApplicationException("Start time must be before end time", 400);
498
}
499
500
return logService.getLogsBetween(startTime, endTime);
501
}
502
}
503
```
504
505
### Timezone-aware Operations
506
507
```java
508
@Path("/meetings")
509
public class MeetingResource {
510
511
@POST
512
public Response scheduleMeeting(@FormParam("startTime") ZonedDateTimeParam startTime,
513
@FormParam("duration") Integer durationMinutes,
514
@FormParam("attendeeTimezone") ZoneIdParam attendeeZone,
515
@Valid ScheduleMeetingRequest request) {
516
517
ZonedDateTime meetingStart = startTime.get();
518
Duration duration = Duration.ofMinutes(durationMinutes != null ? durationMinutes : 60);
519
ZoneId attendeeTimezone = attendeeZone != null ? attendeeZone.get() : ZoneId.systemDefault();
520
521
// Convert meeting time to attendee's timezone for confirmation
522
ZonedDateTime localStartTime = meetingStart.withZoneSameInstant(attendeeTimezone);
523
524
Meeting meeting = meetingService.scheduleMeeting(
525
request,
526
meetingStart,
527
duration,
528
attendeeTimezone
529
);
530
531
return Response.status(201)
532
.entity(meeting)
533
.header("X-Local-Start-Time", localStartTime.toString())
534
.build();
535
}
536
537
@GET
538
@Path("/availability")
539
public AvailabilityResponse getAvailability(@QueryParam("date") LocalDateParam date,
540
@QueryParam("timezone") ZoneIdParam timezone,
541
@QueryParam("duration") Integer durationMinutes) {
542
543
LocalDate checkDate = date != null ? date.get() : LocalDate.now();
544
ZoneId zone = timezone != null ? timezone.get() : ZoneId.systemDefault();
545
Duration meetingDuration = Duration.ofMinutes(durationMinutes != null ? durationMinutes : 30);
546
547
return meetingService.getAvailability(checkDate, zone, meetingDuration);
548
}
549
}
550
```
551
552
## Best Practices
553
554
### Parameter Validation
555
556
```java
557
@Path("/validation")
558
public class DateValidationResource {
559
560
@GET
561
@Path("/future-events")
562
public List<Event> getFutureEvents(@QueryParam("from") LocalDateParam from) {
563
564
LocalDate fromDate = from != null ? from.get() : LocalDate.now();
565
566
// Validate that date is not too far in the past
567
if (fromDate.isBefore(LocalDate.now().minusYears(1))) {
568
throw new WebApplicationException("Date cannot be more than 1 year in the past", 400);
569
}
570
571
// Validate that date is not too far in the future
572
if (fromDate.isAfter(LocalDate.now().plusYears(5))) {
573
throw new WebApplicationException("Date cannot be more than 5 years in the future", 400);
574
}
575
576
return eventService.getFutureEventsFrom(fromDate);
577
}
578
579
@GET
580
@Path("/business-hours")
581
public List<Appointment> getBusinessHourAppointments(@QueryParam("date") LocalDateParam date,
582
@QueryParam("startTime") LocalTimeParam startTime) {
583
584
LocalDate appointmentDate = date != null ? date.get() : LocalDate.now();
585
LocalTime start = startTime != null ? startTime.get() : LocalTime.of(9, 0);
586
587
// Validate business hours
588
if (start.isBefore(LocalTime.of(8, 0)) || start.isAfter(LocalTime.of(18, 0))) {
589
throw new WebApplicationException("Start time must be between 08:00 and 18:00", 400);
590
}
591
592
return appointmentService.getAppointments(appointmentDate, start);
593
}
594
}
595
```
596
597
### Default Values and Null Handling
598
599
```java
600
@Path("/defaults")
601
public class DefaultDateResource {
602
603
@GET
604
@Path("/recent")
605
public List<Item> getRecentItems(@QueryParam("since") InstantParam since) {
606
// Provide sensible default if parameter not provided
607
Instant sinceTime = since != null ? since.get() : Instant.now().minus(Duration.ofDays(7));
608
return itemService.getItemsSince(sinceTime);
609
}
610
611
@GET
612
@Path("/monthly/{year}")
613
public MonthlyData getMonthlyData(@PathParam("year") YearParam year,
614
@QueryParam("month") Integer month) {
615
616
Year dataYear = year.get(); // Required path parameter
617
618
// Handle optional month parameter
619
YearMonth yearMonth;
620
if (month != null && month >= 1 && month <= 12) {
621
yearMonth = YearMonth.of(dataYear.getValue(), month);
622
} else {
623
yearMonth = YearMonth.now();
624
}
625
626
return dataService.getMonthlyData(yearMonth);
627
}
628
}
629
```