diff --git a/README.md b/README.md index d8aa966..5daf0f3 100644 --- a/README.md +++ b/README.md @@ -111,6 +111,66 @@ You can include optional type hints in placeholders to ensure JSON values are re If parsing fails (e.g. `abc` for an `int`), APImposter **falls back to the original string**. +### Relative Date/Time Placeholders + +APImposter supports dynamic date and time placeholders that are calculated relative to the current time. This is perfect for testing calendar applications, scheduling systems, or any time-sensitive mock data. + +#### Supported formats + +| Format | Example | Description | +|--------|---------|-------------| +| `{datetime+/-offset}` | `{datetime+2d4h30m}` | DateTime with flexible time offset | +| `{datetime+/-Nd@HH:MM}` | `{datetime+1d@13:30}` | DateTime N days from now at specific time | +| `{date+/-Nd}` | `{date-7d}` | Date N days from now | + +#### Format specifiers (optional) + +Add a format specifier after a colon to control the output format: + +| Specifier | Description | Example Output | +|-----------|-------------|----------------| +| `iso` (default) | ISO standard format | `2025-09-03T15:30:00` | +| `rfc3339` | RFC 3339 with UTC timezone | `2025-09-03T15:30:00Z` | +| Custom pattern | Any valid DateTimeFormatter pattern | `Sep 03, 2025` | + +#### Examples + +```yaml +# Basic relative dates (ISO format) +- method: GET + path: "/events" + response: + body: + todayMeeting: "{datetime+0d@08:00}" # Today at 8:00 AM + tomorrowLunch: "{datetime+1d@13:00}" # Tomorrow at 1:00 PM + nextWeek: "{date+7d}" # Date 7 days from now + pastEvent: "{datetime-2d4h30m}" # 2 days, 4h, 30m ago + yesterday: "{date-1d}" # Yesterday's date + +# Different output formats +- method: GET + path: "/calendar" + response: + body: + # RFC3339 format + eventTimeRfc: "{datetime+1d@15:30:rfc3339}" # "2025-09-03T15:30:00Z" + + # Custom formats + eventDate: "{date+1d:MMM dd, yyyy}" # "Sep 03, 2025" + timeOnly: "{datetime+0d@14:30:HH:mm}" # "14:30" + shortDate: "{date+7d:MM/dd/yy}" # "09/09/25" +``` + +#### Time components for offset format + +- `d` = days +- `h` = hours +- `m` = minutes + +All components are optional and can be combined: `{datetime+1d2h}`, `{datetime+30m}`, `{datetime-4h15m}` + +**Note:** If a custom format is invalid, the system automatically falls back to ISO format to prevent errors. + --- ## Example Requests and Responses diff --git a/src/main/java/dsv/su/apimposter/service/MockService.java b/src/main/java/dsv/su/apimposter/service/MockService.java index cec8528..3b5a66b 100644 --- a/src/main/java/dsv/su/apimposter/service/MockService.java +++ b/src/main/java/dsv/su/apimposter/service/MockService.java @@ -1,5 +1,10 @@ package dsv.su.apimposter.service; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.time.ZoneOffset; +import java.time.format.DateTimeFormatter; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; @@ -30,7 +35,20 @@ public class MockService { private final MockFileLoader loader; private final AntPathMatcher pathMatcher = new AntPathMatcher(); - private static final Pattern PLACEHOLDER_PATTERN = Pattern.compile("\\{([\\w\\.]+)(?::(int|float|bool))?}"); + // Patterns using named capture groups (Java 21+) + private static final Pattern PLACEHOLDER_PATTERN = + Pattern.compile("\\{(?[\\w\\.]+)(?::(?int|float|bool))?}"); + + // Patterns for relative date/time placeholders with optional format specifier + private static final Pattern DATETIME_OFFSET_PATTERN = + Pattern.compile("\\{datetime(?[+-])(?\\d+d)?(?\\d+h)?" + + "(?\\d+m)?(?::(?\\w+|[^}]+))?}"); + private static final Pattern DATETIME_AT_TIME_PATTERN = + Pattern.compile("\\{datetime(?[+-])(?\\d+)d@(?