diff --git a/AGENTS.md b/AGENTS.md index a4c7624d..942859bc 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -132,6 +132,6 @@ Order matters - first match wins: 3. ObjectResolver, EnumResolver, SealedClassResolver, ValueClassResolver 4. Primitive resolvers (String, Int, Long, etc.) 5. **Kotlin native types FIRST** (KotlinUuidResolver, KotlinInstantResolver, KotlinDurationResolver) -6. **Java types SECOND** (JavaUuidResolver, JavaInstantResolver, JavaDurationResolver) +6. **Java types SECOND** (JavaUuidResolver, JavaInstantResolver, JavaDurationResolver, JavaZonedDateTimeResolver) 7. Collection resolvers (List, Set, Map, Array) 8. ClassResolver (fallback for classes with constructors) \ No newline at end of file diff --git a/docs/supported-types.md b/docs/supported-types.md index 99ed5ecb..4c031d02 100644 --- a/docs/supported-types.md +++ b/docs/supported-types.md @@ -32,6 +32,7 @@ For types not listed here, register a [custom factory](custom-factories.md). | `java.math.BigInteger` | `some()` | | | `java.time.LocalDate` | `some()` | | | `java.time.LocalDateTime` | `some()` | | +| `java.time.ZonedDateTime` | `some()` | | | **Collections** | | | | `List` | `some>()` | See [CollectionStrategy](configuration/collection-strategy.md) | | `MutableList` | `some>()` | | diff --git a/epochs.sh.kt b/epochs.sh.kt new file mode 100644 index 00000000..ec2d5edc --- /dev/null +++ b/epochs.sh.kt @@ -0,0 +1,9 @@ +import java.time.LocalDateTime +import java.time.ZoneOffset + +fun main() { + val start = LocalDateTime.of(1970, 1, 1, 0, 0, 0).toEpochSecond(ZoneOffset.UTC) + val end = LocalDateTime.of(2100, 12, 31, 23, 59, 59).toEpochSecond(ZoneOffset.UTC) + println("START_EPOCH=$start") + println("END_EPOCH=$end") +} diff --git a/src/main/kotlin/dev/appoutlet/some/config/SomeConfig.kt b/src/main/kotlin/dev/appoutlet/some/config/SomeConfig.kt index 2ac3da2e..afb9310c 100644 --- a/src/main/kotlin/dev/appoutlet/some/config/SomeConfig.kt +++ b/src/main/kotlin/dev/appoutlet/some/config/SomeConfig.kt @@ -18,6 +18,7 @@ import dev.appoutlet.some.resolver.IntResolver import dev.appoutlet.some.resolver.JavaDurationResolver import dev.appoutlet.some.resolver.JavaInstantResolver import dev.appoutlet.some.resolver.JavaUuidResolver +import dev.appoutlet.some.resolver.JavaZonedDateTimeResolver import dev.appoutlet.some.resolver.KotlinDurationResolver import dev.appoutlet.some.resolver.KotlinInstantResolver import dev.appoutlet.some.resolver.KotlinUuidResolver @@ -114,6 +115,7 @@ data class SomeConfig( JavaUuidResolver(), JavaInstantResolver(random), JavaDurationResolver(random), + JavaZonedDateTimeResolver(random), BigDecimalResolver(random), BigIntegerResolver(random), LocalDateResolver(random), diff --git a/src/main/kotlin/dev/appoutlet/some/resolver/JavaZonedDateTimeResolver.kt b/src/main/kotlin/dev/appoutlet/some/resolver/JavaZonedDateTimeResolver.kt new file mode 100644 index 00000000..d2454764 --- /dev/null +++ b/src/main/kotlin/dev/appoutlet/some/resolver/JavaZonedDateTimeResolver.kt @@ -0,0 +1,32 @@ +package dev.appoutlet.some.resolver + +import dev.appoutlet.some.core.ResolverChain +import dev.appoutlet.some.core.TypeResolver +import java.time.Instant +import java.time.ZoneId +import java.time.ZonedDateTime +import kotlin.random.Random +import kotlin.reflect.KType +import kotlin.reflect.typeOf + +/** + * 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. + * + * @param random The random source used for generation. + */ +class JavaZonedDateTimeResolver(private val random: Random) : TypeResolver { + private val zoneIds by lazy { ZoneId.getAvailableZoneIds().toList() } + + override fun canResolve(type: KType): Boolean { + return type == typeOf() + } + + 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)) + } +} diff --git a/src/test/kotlin/dev/appoutlet/some/resolver/JavaZonedDateTimeResolverTest.kt b/src/test/kotlin/dev/appoutlet/some/resolver/JavaZonedDateTimeResolverTest.kt new file mode 100644 index 00000000..cbe301e4 --- /dev/null +++ b/src/test/kotlin/dev/appoutlet/some/resolver/JavaZonedDateTimeResolverTest.kt @@ -0,0 +1,35 @@ +package dev.appoutlet.some.resolver + +import dev.appoutlet.some.test.defaultTestChain +import java.time.Instant +import java.time.ZonedDateTime +import kotlin.random.Random +import kotlin.reflect.typeOf +import kotlin.test.Test +import kotlin.test.assertFalse +import kotlin.test.assertIs +import kotlin.test.assertTrue + +class JavaZonedDateTimeResolverTest { + @Test + fun `JavaZonedDateTimeResolver generates ZonedDateTime values`() { + val resolver = JavaZonedDateTimeResolver(Random.Default) + + val result = resolver.resolve(typeOf(), defaultTestChain) + assertIs(result) + } + + @Test + fun `JavaZonedDateTimeResolver canResolve detects ZonedDateTime type`() { + val resolver = JavaZonedDateTimeResolver(Random.Default) + assertTrue(resolver.canResolve(typeOf())) + } + + @Test + fun `JavaZonedDateTimeResolver rejects other types`() { + val resolver = JavaZonedDateTimeResolver(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") + } +}