diff --git a/android/build.gradle.kts b/android/build.gradle.kts index c4dda1b8..ddc09a27 100644 --- a/android/build.gradle.kts +++ b/android/build.gradle.kts @@ -18,7 +18,7 @@ android { } defaultConfig { - minSdk = 24 + minSdk = 26 testInstrumentationRunner = "android.support.test.runner.AndroidJUnitRunner" } diff --git a/core/src/main/kotlin/dev/appoutlet/some/config/SomeConfig.kt b/core/src/main/kotlin/dev/appoutlet/some/config/SomeConfig.kt index 152670e0..54b1f181 100644 --- a/core/src/main/kotlin/dev/appoutlet/some/config/SomeConfig.kt +++ b/core/src/main/kotlin/dev/appoutlet/some/config/SomeConfig.kt @@ -128,7 +128,7 @@ data class SomeConfig( JavaUuidResolver(), JavaInstantResolver(random), JavaDurationResolver(random), - JavaZonedDateTimeResolver(random), + JavaZonedDateTimeResolver(strategyProvider, random), OptionalResolver(strategyProvider, random), BigDecimalResolver(random), BigIntegerResolver(random), diff --git a/core/src/main/kotlin/dev/appoutlet/some/config/ZonedDateTimeStrategy.kt b/core/src/main/kotlin/dev/appoutlet/some/config/ZonedDateTimeStrategy.kt new file mode 100644 index 00000000..015d6126 --- /dev/null +++ b/core/src/main/kotlin/dev/appoutlet/some/config/ZonedDateTimeStrategy.kt @@ -0,0 +1,97 @@ +package dev.appoutlet.some.config + +import java.time.Instant +import java.time.ZoneId + +/** + * Strategy for controlling how [java.time.ZonedDateTime] values are generated. + * + * The strategy selects the time window for generated instants and, for [Range], + * an optional fixed [ZoneId]. All non-[Range] variants use a random available JVM [ZoneId]. + * + * ## Available Strategies + * + * - [Default] – (Default) Generates instants across the full [Instant] range with a random [ZoneId]. + * - [NearPast] – Generates instants from 10 years before now until now. + * - [NearFuture] – Generates instants from now until 10 years after now. + * - [DistantPast] – Generates instants from [Instant.MIN] until now. + * - [DistantFuture] – Generates instants from now until [Instant.MAX]. + * - [Range] – Generates instants within user-defined bounds, optionally pinned to a specific [ZoneId]. + * + * ## Example Usage + * + * ```kotlin + * // Default full-range behavior + * some { strategy(ZonedDateTimeStrategy.Default) } + * + * // Values from the last 10 years + * some { strategy(ZonedDateTimeStrategy.NearPast) } + * + * // Custom range with a random ZoneId + * some { strategy(ZonedDateTimeStrategy.Range(min, max)) } + * + * // Custom range pinned to a specific ZoneId + * some { strategy(ZonedDateTimeStrategy.Range(min, max, ZoneId.of("America/Sao_Paulo"))) } + * ``` + */ +sealed interface ZonedDateTimeStrategy : Strategy { + override val key get() = ZonedDateTimeStrategy::class + + /** + * Generates instants across the full [Instant] range with a random [ZoneId]. + * + * This is the default strategy and matches the historical behavior before + * [ZonedDateTimeStrategy] was introduced. + */ + data object Default : ZonedDateTimeStrategy + + /** + * Generates instants from 10 years before now until now. + */ + data object NearPast : ZonedDateTimeStrategy + + /** + * Generates instants from now until 10 years after now. + */ + data object NearFuture : ZonedDateTimeStrategy + + /** + * Generates instants from [Instant.MIN] until now. + */ + data object DistantPast : ZonedDateTimeStrategy + + /** + * Generates instants from now until [Instant.MAX]. + */ + data object DistantFuture : ZonedDateTimeStrategy + + /** + * Generates instants within the inclusive range [[min], [max]], optionally pinned to [zoneId]. + * + * When [zoneId] is `null`, a random available JVM [ZoneId] is used. + * + * @property min The inclusive lower bound of generated instants. + * @property max The inclusive upper bound of generated instants. + * @property zoneId The fixed zone to use, or `null` for a random zone. + * + * @throws IllegalArgumentException if [min] is greater than [max]. + */ + data class Range( + val min: Instant, + val max: Instant, + val zoneId: ZoneId? = null, + ) : ZonedDateTimeStrategy { + init { + require(min <= max) { + "ZonedDateTimeStrategy.Range requires min <= max, but got min=$min and max=$max" + } + } + } + + companion object { + /** + * The default [ZonedDateTimeStrategy]. + */ + val default: ZonedDateTimeStrategy get() = Default + } +} diff --git a/core/src/main/kotlin/dev/appoutlet/some/resolver/JavaZonedDateTimeResolver.kt b/core/src/main/kotlin/dev/appoutlet/some/resolver/JavaZonedDateTimeResolver.kt index 1192b16f..4bd1cacc 100644 --- a/core/src/main/kotlin/dev/appoutlet/some/resolver/JavaZonedDateTimeResolver.kt +++ b/core/src/main/kotlin/dev/appoutlet/some/resolver/JavaZonedDateTimeResolver.kt @@ -1,23 +1,34 @@ package dev.appoutlet.some.resolver +import dev.appoutlet.some.config.ZonedDateTimeStrategy import dev.appoutlet.some.core.Resolver import dev.appoutlet.some.core.ResolverChain +import dev.appoutlet.some.core.StrategyProvider +import dev.appoutlet.some.core.get import java.time.Instant import java.time.ZoneId +import java.time.ZoneOffset import java.time.ZonedDateTime import kotlin.random.Random import kotlin.reflect.KType import kotlin.reflect.typeOf +private const val TEN_YEARS = 10L + /** * Resolves [ZonedDateTime] instances. * - * Generated values fall within the range 1970-01-01 to 2100-12-31. - * Each instance uses a randomly selected [ZoneId] from the available set on the JVM. + * Generated values are controlled by the active [ZonedDateTimeStrategy]. By default, values are + * spread across the full [Instant] range and use a randomly selected [ZoneId] from the available + * set on the JVM. * + * @param strategyProvider Provider of configured strategies. * @param random The random source used for generation. */ -class JavaZonedDateTimeResolver(private val random: Random) : Resolver { +class JavaZonedDateTimeResolver( + private val strategyProvider: StrategyProvider, + private val random: Random, +) : Resolver { private val zoneIds by lazy { ZoneId.getAvailableZoneIds().toList() } override fun canResolve(type: KType): Boolean { @@ -25,8 +36,35 @@ class JavaZonedDateTimeResolver(private val random: Random) : Resolver { } override fun resolve(type: KType, chain: ResolverChain): Any { - val epochSecond = random.nextLong(Instant.MIN.epochSecond, Instant.MAX.epochSecond) - val zoneId = zoneIds[random.nextInt(zoneIds.size)] - return ZonedDateTime.ofInstant(Instant.ofEpochSecond(epochSecond), ZoneId.of(zoneId)) + val strategy = strategyProvider.get() ?: ZonedDateTimeStrategy.default + val now = Instant.now() + + val (minInstant, maxInstant) = when (strategy) { + ZonedDateTimeStrategy.Default -> Instant.MIN to Instant.MAX + ZonedDateTimeStrategy.NearPast -> now.minusYears(TEN_YEARS) to now + ZonedDateTimeStrategy.NearFuture -> now to now.plusYears(TEN_YEARS) + ZonedDateTimeStrategy.DistantPast -> Instant.MIN to now + ZonedDateTimeStrategy.DistantFuture -> now to Instant.MAX + is ZonedDateTimeStrategy.Range -> strategy.min to strategy.max + } + + val epochSecond = randomEpochSecond(minInstant, maxInstant) + + val zoneId = when (strategy) { + is ZonedDateTimeStrategy.Range -> strategy.zoneId ?: ZoneId.of(zoneIds.random()) + else -> ZoneId.of(zoneIds.random()) + } + + return ZonedDateTime.ofInstant(Instant.ofEpochSecond(epochSecond), zoneId) } + + private fun randomEpochSecond(min: Instant, max: Instant): Long { + return random.nextLong(min.epochSecond, max.epochSecond) + } + + private fun Instant.minusYears(years: Long): Instant = + this.atZone(ZoneOffset.UTC).minusYears(years).toInstant() + + private fun Instant.plusYears(years: Long): Instant = + this.atZone(ZoneOffset.UTC).plusYears(years).toInstant() } diff --git a/core/src/test/kotlin/dev/appoutlet/some/config/ZonedDateTimeStrategyTest.kt b/core/src/test/kotlin/dev/appoutlet/some/config/ZonedDateTimeStrategyTest.kt new file mode 100644 index 00000000..18f69f17 --- /dev/null +++ b/core/src/test/kotlin/dev/appoutlet/some/config/ZonedDateTimeStrategyTest.kt @@ -0,0 +1,73 @@ +package dev.appoutlet.some.config + +import java.time.Instant +import java.time.ZoneId +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertFailsWith +import kotlin.test.assertSame + +class ZonedDateTimeStrategyTest { + @Test + fun `default strategy is Default`() { + assertSame(ZonedDateTimeStrategy.Default, ZonedDateTimeStrategy.default) + } + + @Test + fun `Range accepts valid min and max`() { + val min = Instant.parse("2020-01-01T00:00:00Z") + val max = Instant.parse("2025-01-01T00:00:00Z") + + val strategy = ZonedDateTimeStrategy.Range(min, max) + + assertEquals(min, strategy.min) + assertEquals(max, strategy.max) + assertEquals(null, strategy.zoneId) + } + + @Test + fun `Range accepts equal min and max`() { + val instant = Instant.parse("2020-01-01T00:00:00Z") + + val strategy = ZonedDateTimeStrategy.Range(instant, instant) + + assertEquals(instant, strategy.min) + assertEquals(instant, strategy.max) + } + + @Test + fun `Range accepts custom zoneId`() { + val min = Instant.parse("2020-01-01T00:00:00Z") + val max = Instant.parse("2025-01-01T00:00:00Z") + val zoneId = ZoneId.of("America/Sao_Paulo") + + val strategy = ZonedDateTimeStrategy.Range(min, max, zoneId) + + assertEquals(zoneId, strategy.zoneId) + } + + @Test + fun `Range rejects inverted min and max`() { + val min = Instant.parse("2025-01-01T00:00:00Z") + val max = Instant.parse("2020-01-01T00:00:00Z") + + val error = assertFailsWith { + ZonedDateTimeStrategy.Range(min, max) + } + + assertEquals( + "ZonedDateTimeStrategy.Range requires min <= max, but got min=$min and max=$max", + error.message, + ) + } + + @Test + fun `key is ZonedDateTimeStrategy class`() { + assertSame(ZonedDateTimeStrategy::class, ZonedDateTimeStrategy.Default.key) + assertSame(ZonedDateTimeStrategy::class, ZonedDateTimeStrategy.NearPast.key) + assertSame( + ZonedDateTimeStrategy::class, + ZonedDateTimeStrategy.Range(Instant.MIN, Instant.MAX).key, + ) + } +} diff --git a/core/src/test/kotlin/dev/appoutlet/some/resolver/JavaZonedDateTimeResolverTest.kt b/core/src/test/kotlin/dev/appoutlet/some/resolver/JavaZonedDateTimeResolverTest.kt index cbe301e4..bd483051 100644 --- a/core/src/test/kotlin/dev/appoutlet/some/resolver/JavaZonedDateTimeResolverTest.kt +++ b/core/src/test/kotlin/dev/appoutlet/some/resolver/JavaZonedDateTimeResolverTest.kt @@ -1,11 +1,19 @@ package dev.appoutlet.some.resolver +import dev.appoutlet.some.config.DefaultStrategyProvider +import dev.appoutlet.some.config.NullableStrategy +import dev.appoutlet.some.config.ZonedDateTimeStrategy +import dev.appoutlet.some.config.buildSomeConfig +import dev.appoutlet.some.core.ResolverChain import dev.appoutlet.some.test.defaultTestChain import java.time.Instant +import java.time.ZoneId +import java.time.ZoneOffset import java.time.ZonedDateTime import kotlin.random.Random import kotlin.reflect.typeOf import kotlin.test.Test +import kotlin.test.assertEquals import kotlin.test.assertFalse import kotlin.test.assertIs import kotlin.test.assertTrue @@ -13,7 +21,7 @@ import kotlin.test.assertTrue class JavaZonedDateTimeResolverTest { @Test fun `JavaZonedDateTimeResolver generates ZonedDateTime values`() { - val resolver = JavaZonedDateTimeResolver(Random.Default) + val resolver = JavaZonedDateTimeResolver(DefaultStrategyProvider(), Random.Default) val result = resolver.resolve(typeOf(), defaultTestChain) assertIs(result) @@ -21,15 +29,166 @@ class JavaZonedDateTimeResolverTest { @Test fun `JavaZonedDateTimeResolver canResolve detects ZonedDateTime type`() { - val resolver = JavaZonedDateTimeResolver(Random.Default) + val resolver = JavaZonedDateTimeResolver(DefaultStrategyProvider(), Random.Default) assertTrue(resolver.canResolve(typeOf())) } @Test fun `JavaZonedDateTimeResolver rejects other types`() { - val resolver = JavaZonedDateTimeResolver(Random.Default) + val resolver = JavaZonedDateTimeResolver(DefaultStrategyProvider(), Random.Default) assertFalse(resolver.canResolve(typeOf()), "Should not resolve String") assertFalse(resolver.canResolve(typeOf()), "Should not resolve Int") assertFalse(resolver.canResolve(typeOf()), "Should not resolve java.time.Instant") } + + @Test + fun `Default strategy generates full range values with random zone`() { + val resolver = JavaZonedDateTimeResolver(DefaultStrategyProvider(), Random.Default) + + repeat(50) { + val result = resolver.resolve(typeOf(), defaultTestChain) as ZonedDateTime + assertTrue(result.toInstant().epochSecond >= Instant.MIN.epochSecond) + assertTrue(result.toInstant().epochSecond < Instant.MAX.epochSecond) + assertTrue(result.zone.id in ZoneId.getAvailableZoneIds()) + } + } + + @Test + fun `NearPast strategy generates values within ten years before now`() { + val resolver = JavaZonedDateTimeResolver( + DefaultStrategyProvider(mapOf(ZonedDateTimeStrategy::class to ZonedDateTimeStrategy.NearPast)), + Random.Default, + ) + + repeat(100) { + val before = Instant.now() + val tenYearsBeforeBefore = before.atZone(ZoneOffset.UTC).minusYears(10).toInstant() + val result = resolver.resolve(typeOf(), defaultTestChain) as ZonedDateTime + val after = Instant.now() + + assertTrue( + !result.toInstant().isBefore(tenYearsBeforeBefore), + "Expected result after or at $tenYearsBeforeBefore, got ${result.toInstant()}", + ) + assertTrue( + !result.toInstant().isAfter(after), + "Expected result before or at $after, got ${result.toInstant()}", + ) + } + } + + @Test + fun `NearFuture strategy generates values within ten years after now`() { + val resolver = JavaZonedDateTimeResolver( + DefaultStrategyProvider(mapOf(ZonedDateTimeStrategy::class to ZonedDateTimeStrategy.NearFuture)), + Random.Default, + ) + + repeat(100) { + val before = Instant.now() + val result = resolver.resolve(typeOf(), defaultTestChain) as ZonedDateTime + val after = Instant.now() + val tenYearsAfterAfter = after.atZone(ZoneOffset.UTC).plusYears(10).toInstant() + + assertTrue( + !result.toInstant().isBefore(before), + "Expected result after or at $before, got ${result.toInstant()}", + ) + assertTrue( + !result.toInstant().isAfter(tenYearsAfterAfter), + "Expected result before or at $tenYearsAfterAfter, got ${result.toInstant()}", + ) + } + } + + @Test + fun `DistantPast strategy generates values from Instant MIN until now`() { + val resolver = JavaZonedDateTimeResolver( + DefaultStrategyProvider(mapOf(ZonedDateTimeStrategy::class to ZonedDateTimeStrategy.DistantPast)), + Random.Default, + ) + + repeat(100) { + val result = resolver.resolve(typeOf(), defaultTestChain) as ZonedDateTime + assertTrue(!result.toInstant().isBefore(Instant.MIN)) + assertTrue(!result.toInstant().isAfter(Instant.now())) + } + } + + @Test + fun `DistantFuture strategy generates values from now until Instant MAX`() { + val resolver = JavaZonedDateTimeResolver( + DefaultStrategyProvider(mapOf(ZonedDateTimeStrategy::class to ZonedDateTimeStrategy.DistantFuture)), + Random.Default, + ) + + repeat(100) { + val before = Instant.now() + val result = resolver.resolve(typeOf(), defaultTestChain) as ZonedDateTime + assertTrue(!result.toInstant().isBefore(before)) + assertTrue(!result.toInstant().isAfter(Instant.MAX)) + } + } + + @Test + fun `Range strategy generates values within inclusive bounds and random zone`() { + val min = Instant.parse("2020-01-01T00:00:00Z") + val max = Instant.parse("2025-01-01T00:00:00Z") + val resolver = JavaZonedDateTimeResolver( + DefaultStrategyProvider(mapOf(ZonedDateTimeStrategy::class to ZonedDateTimeStrategy.Range(min, max))), + Random.Default, + ) + + repeat(100) { + val result = resolver.resolve(typeOf(), defaultTestChain) as ZonedDateTime + assertTrue(result.zone.id in ZoneId.getAvailableZoneIds()) + assertTrue( + !result.toInstant().isBefore(min), + "Expected result after or at $min, got ${result.toInstant()}", + ) + assertTrue( + !result.toInstant().isAfter(max), + "Expected result before or at $max, got ${result.toInstant()}", + ) + } + } + + @Test + fun `Range strategy with zoneId uses supplied zone`() { + val min = Instant.parse("2020-01-01T00:00:00Z") + val max = Instant.parse("2025-01-01T00:00:00Z") + val zoneId = ZoneId.of("America/Sao_Paulo") + val resolver = JavaZonedDateTimeResolver( + DefaultStrategyProvider( + mapOf(ZonedDateTimeStrategy::class to ZonedDateTimeStrategy.Range(min, max, zoneId)) + ), + Random.Default, + ) + + repeat(50) { + val result = resolver.resolve(typeOf(), defaultTestChain) as ZonedDateTime + assertEquals(zoneId, result.zone) + assertTrue(!result.toInstant().isBefore(min)) + assertTrue(!result.toInstant().isAfter(max)) + } + } + + @Test + fun `ZonedDateTimeStrategy integrates with SomeConfig`() { + val min = Instant.parse("2020-01-01T00:00:00Z") + val max = Instant.parse("2025-01-01T00:00:00Z") + val zoneId = ZoneId.of("Europe/London") + val config = buildSomeConfig { + strategy(ZonedDateTimeStrategy.Range(min, max, zoneId)) + } + val resolvers = config.buildResolvers() + val chain = ResolverChain(resolvers, config[NullableStrategy::class]) + + repeat(50) { + val result = chain.resolve(typeOf()) as ZonedDateTime + assertEquals(zoneId, result.zone) + assertTrue(!result.toInstant().isBefore(min)) + assertTrue(!result.toInstant().isAfter(max)) + } + } } diff --git a/docs/strategies/index.md b/docs/strategies/index.md index 63ba8f41..7c5412b5 100644 --- a/docs/strategies/index.md +++ b/docs/strategies/index.md @@ -24,6 +24,7 @@ val user = some { | `CollectionStrategy` | `CollectionStrategy()` | Collections with 1 to 5 elements | | `FloatStrategy` | `FloatStrategy()` | Floats in the range `0.0f..1.0f` | | `DefaultValueStrategy` | `DefaultValueStrategy.UseDefault` | Uses Kotlin defaults for optional constructor parameters | +| `ZonedDateTimeStrategy` | `ZonedDateTimeStrategy.Default` | `ZonedDateTime` values across the full `Instant` range with a random `ZoneId` | ## Available strategies @@ -32,3 +33,4 @@ val user = some { - [Collection Strategy](collection-strategy.md) - [Float Strategy](float-strategy.md) - [Default Value Strategy](default-value-strategy.md) +- [ZonedDateTime Strategy](zoned-date-time-strategy.md) diff --git a/docs/strategies/zoned-date-time-strategy.md b/docs/strategies/zoned-date-time-strategy.md new file mode 100644 index 00000000..6866f98f --- /dev/null +++ b/docs/strategies/zoned-date-time-strategy.md @@ -0,0 +1,183 @@ +# ZonedDateTime Strategy + +Controls how `java.time.ZonedDateTime` values are generated during fixture generation. + +This strategy is part of the shared API available through both `some-core` and `some-android`. + +Use it when your tests care about **which part of time** a value comes from, or whether the generated `ZonedDateTime` should use a **specific `ZoneId`**. + +## Variants + +### `Default` + +Generates values across the full `Instant` range with a random JVM `ZoneId`. + +```kotlin +some { + strategy(ZonedDateTimeStrategy.Default) +} +``` + +This is the default strategy. + +Use it when the exact date range does not matter and you only need a valid `ZonedDateTime`. + +### `NearPast` + +Generates values from the last 10 years. + +```kotlin +some { + strategy(ZonedDateTimeStrategy.NearPast) +} +``` + +Use it for features that should behave like recent historical data: imported records, audit events, or recently completed jobs. + +### `NearFuture` + +Generates values from now until 10 years in the future. + +```kotlin +some { + strategy(ZonedDateTimeStrategy.NearFuture) +} +``` + +Use it for bookings, scheduled notifications, expirations, or other forward-looking dates. + +### `DistantPast` + +Generates values from `Instant.MIN` until now. + +```kotlin +some { + strategy(ZonedDateTimeStrategy.DistantPast) +} +``` + +Use it when you want older dates to be possible, not just values from the last few years. + +### `DistantFuture` + +Generates values from now until `Instant.MAX`. + +```kotlin +some { + strategy(ZonedDateTimeStrategy.DistantFuture) +} +``` + +Use it when your domain supports far-future timestamps and a 10-year window would be too narrow. + +### `Range` + +Generates values between a custom `min` and `max` `Instant`, with an optional fixed `ZoneId`. + +```kotlin +val min = Instant.parse("2024-01-01T00:00:00Z") +val max = Instant.parse("2024-12-31T23:59:59Z") + +some { + strategy(ZonedDateTimeStrategy.Range(min, max)) +} +``` + +When `zoneId` is not provided, Some uses a random available JVM zone. + +```kotlin +val min = Instant.parse("2024-01-01T00:00:00Z") +val max = Instant.parse("2024-12-31T23:59:59Z") + +some { + strategy( + ZonedDateTimeStrategy.Range( + min = min, + max = max, + zoneId = ZoneId.of("America/Sao_Paulo"), + ) + ) +} +``` + +Use `Range` when your test should stay inside a business window such as a quarter, a release cycle, or a reporting period. + +## Choosing the right variant + +| Variant | Best when you need | +|---------|--------------------| +| `Default` | Any valid `ZonedDateTime` | +| `NearPast` | Recent historical values | +| `NearFuture` | Upcoming values | +| `DistantPast` | Potentially very old values | +| `DistantFuture` | Potentially very far future values | +| `Range` | A specific time window, optionally with a fixed zone | + +## Zone behavior + +- `Default`, `NearPast`, `NearFuture`, `DistantPast`, and `DistantFuture` use a **random available JVM `ZoneId`**. +- `Range(min, max)` also uses a random available JVM `ZoneId`. +- `Range(min, max, zoneId)` pins every generated value to the supplied `ZoneId`. + +If your assertions depend on a specific zone, prefer `Range(..., zoneId = ...)`. + +## Reusing the strategy + +When several tests need the same time window, configure it once with `someSetup`: + +```kotlin +val fixtures = someSetup { + strategy( + ZonedDateTimeStrategy.Range( + min = Instant.parse("2024-01-01T00:00:00Z"), + max = Instant.parse("2024-12-31T23:59:59Z"), + zoneId = ZoneId.of("Europe/London"), + ) + ) +} + +val startedAt: ZonedDateTime = fixtures() +val finishedAt: ZonedDateTime = fixtures() +``` + +## Validation and caveats + +### `Range` requires `min <= max` + +`ZonedDateTimeStrategy.Range` throws `IllegalArgumentException` when `min` is after `max`. + +```kotlin +ZonedDateTimeStrategy.Range( + min = Instant.parse("2025-01-01T00:00:00Z"), + max = Instant.parse("2024-01-01T00:00:00Z"), +) // IllegalArgumentException +``` + +### Prefer ranges that span at least one second + +!!! warning "Current resolver behavior" + + `JavaZonedDateTimeResolver` currently randomizes using epoch seconds. + In practice, this means `Range` works best when `min` and `max` span at least one second. + +If you need a stable single timestamp, register a [custom factory](../custom-factories.md) for `ZonedDateTime` in that test. + +```kotlin +val fixedTime = ZonedDateTime.parse("2024-06-01T10:15:30+01:00[Europe/London]") + +val fixture = some { + factory(ZonedDateTime::class) { fixedTime } +} +``` + +## Summary table + +| Strategy | Behavior | +|----------|----------| +| `Default` | Full `Instant` range with a random `ZoneId` (default) | +| `NearPast` | From 10 years before now until now | +| `NearFuture` | From now until 10 years after now | +| `DistantPast` | From `Instant.MIN` until now | +| `DistantFuture` | From now until `Instant.MAX` | +| `Range(min, max)` | Custom range with a random `ZoneId` | +| `Range(min, max, zoneId)` | Custom range pinned to a specific `ZoneId` | diff --git a/docs/supported-types.md b/docs/supported-types.md index d53d5fc0..7bd373f3 100644 --- a/docs/supported-types.md +++ b/docs/supported-types.md @@ -38,7 +38,7 @@ For types not listed here, register a [custom factory](custom-factories.md) or s | `java.math.BigInteger` | `some()` | | | `java.time.LocalDate` | `some()` | | | `java.time.LocalDateTime` | `some()` | | -| `java.time.ZonedDateTime` | `some()` | | +| `java.time.ZonedDateTime` | `some()` | See [ZonedDateTime Strategy](strategies/zoned-date-time-strategy.md) | | **Collections** | | | | `List` | `some>()` | See [Collection Strategy](strategies/collection-strategy.md) | | `MutableList` | `some>()` | | diff --git a/zensical.toml b/zensical.toml index 2ee65879..9ff93647 100644 --- a/zensical.toml +++ b/zensical.toml @@ -20,7 +20,8 @@ nav = [ "strategies/string-strategy.md", "strategies/collection-strategy.md", "strategies/float-strategy.md", - "strategies/default-value-strategy.md" + "strategies/default-value-strategy.md", + "strategies/zoned-date-time-strategy.md" ] }, "supported-types.md", { "Android" = [