Skip to content

Commit 30ab530

Browse files
authored
Wasm/WASI target implementation (#366)
1 parent 9ea4c3e commit 30ab530

File tree

647 files changed

+406
-9
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

647 files changed

+406
-9
lines changed

README.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -478,6 +478,24 @@ external object JsJodaTimeZoneModule
478478
private val jsJodaTz = JsJodaTimeZoneModule
479479
```
480480

481+
#### Note about time zones in Wasm/WASI
482+
483+
By default, there's only one time zone available in Kotlin/Wasm WASI: the `UTC` time zone with a fixed offset.
484+
485+
If you want to use all time zones in Kotlin/Wasm WASI platform, you need to add the following dependency:
486+
487+
```kotlin
488+
kotlin {
489+
sourceSets {
490+
val wasmWasiMain by getting {
491+
dependencies {
492+
implementation("kotlinx-datetime-zoneinfo", "2024a-spi.0.6.0-RC.2")
493+
}
494+
}
495+
}
496+
}
497+
```
498+
481499
### Maven
482500

483501
Add a dependency to the `<dependencies>` element. Note that you need to use the platform-specific `-jvm` artifact in Maven.
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
import java.io.File
2+
3+
/*
4+
* Copyright 2019-2024 JetBrains s.r.o. and contributors.
5+
* Use of this source code is governed by the Apache 2.0 License that can be found in the LICENSE.txt file.
6+
*/
7+
8+
private val pkg = "package kotlinx.datetime.timezones.tzData"
9+
10+
private fun generateByteArrayProperty(tzData: TzData, header: String, propertyName: String): String = buildString {
11+
append(header)
12+
appendLine()
13+
appendLine()
14+
appendLine("/* ${tzData.fullTzNames.joinToString(", ")} */")
15+
append("internal val $propertyName get() = byteArrayOf(")
16+
for (chunk in tzData.data.toList().chunked(16)) {
17+
appendLine()
18+
append(" ")
19+
val chunkText = chunk.joinToString {
20+
it.toString().padStart(4, ' ')
21+
} + ","
22+
append(chunkText)
23+
}
24+
appendLine()
25+
append(")")
26+
}
27+
28+
private class TzData(val data: ByteArray, val fullTzNames: MutableList<String>)
29+
private fun loadTzBinaries(
30+
zoneInfo: File,
31+
currentName: String,
32+
result: MutableList<TzData>
33+
) {
34+
val zoneName = if (currentName.isEmpty()) zoneInfo.name else "$currentName/${zoneInfo.name}"
35+
if (zoneInfo.isDirectory) {
36+
zoneInfo.listFiles()?.forEach {
37+
loadTzBinaries(it, zoneName, result)
38+
}
39+
} else {
40+
val bytes = zoneInfo.readBytes()
41+
val foundTzData = result.firstOrNull { it.data.contentEquals(bytes) }
42+
val tzData: TzData
43+
if (foundTzData != null) {
44+
tzData = foundTzData
45+
} else {
46+
tzData = TzData(bytes, mutableListOf())
47+
result.add(tzData)
48+
}
49+
50+
tzData.fullTzNames.add(zoneName)
51+
}
52+
}
53+
54+
fun generateZoneInfosResources(zoneInfoDir: File, outputDir: File, version: String) {
55+
val header = buildString {
56+
appendLine()
57+
append("/* AUTOGENERATED FROM ZONE INFO DATABASE v.$version */")
58+
appendLine()
59+
appendLine()
60+
append(pkg)
61+
}
62+
63+
val loadedZones = mutableListOf<TzData>()
64+
zoneInfoDir.listFiles()?.forEach { file ->
65+
loadTzBinaries(file, "", loadedZones)
66+
}
67+
68+
val zoneDataByNameBody = StringBuilder()
69+
val getTimeZonesBody = StringBuilder()
70+
loadedZones.forEachIndexed { id, tzData ->
71+
val tzDataName = "tzData$id"
72+
val data = generateByteArrayProperty(tzData, header, tzDataName)
73+
File(outputDir, "$tzDataName.kt").writeText(data)
74+
tzData.fullTzNames.forEach { name ->
75+
zoneDataByNameBody.appendLine(" \"$name\" -> $tzDataName")
76+
getTimeZonesBody.appendLine(" \"$name\",")
77+
}
78+
}
79+
80+
val content = buildString {
81+
append(header)
82+
appendLine()
83+
appendLine()
84+
appendLine("internal fun zoneDataByName(name: String): ByteArray = when(name) {")
85+
append(zoneDataByNameBody)
86+
appendLine()
87+
append(" else -> throw kotlinx.datetime.IllegalTimeZoneException(\"Invalid timezone name\")")
88+
appendLine()
89+
append("}")
90+
appendLine()
91+
appendLine()
92+
append("internal val timeZones: Set<String> by lazy { setOf(")
93+
appendLine()
94+
append(getTimeZonesBody)
95+
appendLine()
96+
append(")")
97+
append("}")
98+
}
99+
100+
File(outputDir, "tzData.kt").writeText(content)
101+
}

core/build.gradle.kts

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,10 @@ kotlin {
127127
}
128128
}
129129

130+
wasmWasi {
131+
nodejs()
132+
}
133+
130134
@OptIn(ExperimentalKotlinGradlePluginApi::class)
131135
compilerOptions {
132136
freeCompilerArgs.add("-Xexpect-actual-classes")
@@ -207,14 +211,34 @@ kotlin {
207211
dependsOn(commonJsTest)
208212
}
209213

210-
val nativeMain by getting {
214+
val commonKotlinMain by creating {
211215
dependsOn(commonMain.get())
212216
dependencies {
213217
api("org.jetbrains.kotlinx:kotlinx-serialization-core:$serializationVersion")
214218
}
215219
}
216220

221+
val commonKotlinTest by creating {
222+
dependsOn(commonTest.get())
223+
}
224+
225+
val nativeMain by getting {
226+
dependsOn(commonKotlinMain)
227+
}
228+
217229
val nativeTest by getting {
230+
dependsOn(commonKotlinTest)
231+
}
232+
233+
val wasmWasiMain by getting {
234+
dependsOn(commonKotlinMain)
235+
}
236+
237+
val wasmWasiTest by getting {
238+
dependsOn(commonKotlinTest)
239+
dependencies {
240+
runtimeOnly(project(":kotlinx-datetime-zoneinfo"))
241+
}
218242
}
219243

220244
val darwinMain by getting {

core/common/src/TimeZone.kt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,8 @@ public expect open class TimeZone {
6666
* On Linux, this function queries the `/etc/localtime` symbolic link. If the link is missing, [UTC] is used.
6767
* If the link points to an invalid location, [IllegalTimeZoneException] is thrown.
6868
*
69+
* Always returns the `UTC` timezone on the Wasm WASI platform due to the lack of support for retrieving system timezone information.
70+
*
6971
* @sample kotlinx.datetime.test.samples.TimeZoneSamples.currentSystemDefault
7072
*/
7173
public fun currentSystemDefault(): TimeZone
@@ -95,6 +97,10 @@ public expect open class TimeZone {
9597
*
9698
* @throws IllegalTimeZoneException if [zoneId] has an invalid format or a time-zone with the name [zoneId]
9799
* is not found.
100+
*
101+
* @throws IllegalTimeZoneException on the Wasm WASI platform for non-fixed-offset time zones,
102+
* unless a dependency on the `kotlinx-datetime-zoneinfo` artifact is added.
103+
*
98104
* @sample kotlinx.datetime.test.samples.TimeZoneSamples.constructorFunction
99105
*/
100106
public fun of(zoneId: String): TimeZone
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.

0 commit comments

Comments
 (0)