-
Notifications
You must be signed in to change notification settings - Fork 108
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Wasm/WASI target implementation #366
Conversation
sourceSets { | ||
commonMain { | ||
dependencies { | ||
compileOnly(project(":kotlinx-datetime")) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Applying timezones without datetime lead to broken compilation (or exception on usage times zone when PL is enabled). This helps us to avoid version collisions on gradle side and circullar dependency while testing.
|
||
@OptIn(InternalDateTimeApi::class) | ||
private fun zoneDataByName(name: String): ByteArray = | ||
timeZonesProvider?.zoneDataByName(name) ?: error("TimeZones are not supported") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We need to decide how to lead user to use timezones dependency in this case
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this should be an IllegalTimeZoneException
(which is documented in the KDoc of TimeZone.of
), with the text like "WASI does not support time zones automatically; please add a dependency on kotlinx-datetime-timezones-full"
Also, there should be a clear difference between a zone not being found because it doesn't exist and it not being found because the dependency wasn't provided, though with the current pure-Kotlin TimeZone.kt
, it won't work, so we'll need to reorganize the code a bit.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You would like to add it in common TimeZone.of
KDoc or in make a separate KDoc for platform-specific TimeZone.of
?
*/ | ||
@InternalDateTimeApi | ||
public fun initializeTimeZonesProvider(provider: TimeZonesProvider) { | ||
check(timeZonesProvider != provider) { "TimeZone database redeclaration" } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We need to decide what to do if more that one timezone library were added
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But this issue can't occur currently, when there's only one artifact with the time zones, right? If we decide to publish partial timezone data, we could do it in a way that resolves conflicts on the build system level. For example, kotlinx-datetime-timezone-basic
+ kotlinx-datetime-timezones-full
, with full
depending on basic
. So I'm not sure if we will ever encounter such conflicts.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have no idea how to to resolve such conflicts for different flavors of timezones libraries without a special gradle plugin. We, with gradle plugin team, do not see the way for now, how to get one dependency with gradle resolution, the only way I see now is to allow redeclaration in runtime with a special "priority" level. But how to manage this level's needs a consideration.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here's a scheme:
kotlinx-datetime-timezones-full -> kotlinx-datetime-timezones-basic -> kotlinx-datetime
A -> B
means "A depends on B". kotlinx-datetime-timezones-full
only provides new data, the bulk of the data is in kotlinx-datetime-timezones-basic
. Then, kotlinx-datetime-timezones-basic
could have another thing like initializeFullTimeZonesProvider
, which kotlinx-datetime-timezones-full
would call. Do you see any problems with this approach?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, for now we are not decided yet what could -basic
be but a first thoughts was about to remove some information from timezones (like make only future
implementation or +-5 years
). But not amount timezones. So the information in timezones
then will be not additive.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In any case, we'll be able to work around this somehow. This is a technical thing invisible to the users, so we can safely do anything we want here.
The build fails because of none of TZ binaries in the PR. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also, not to forget: when we decide on the versions, artifact names, etc., we'll need to update the README.
} | ||
|
||
internal actual fun currentSystemDefaultZone(): Pair<String, TimeZoneRules?> = | ||
throw UnsupportedOperationException("WASI platform does not support system timezone obtaining") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since we expect this exception to realistically occur, we must document it in TimeZone.currentSystemDefault()
's KDoc.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You would like to add it in common TimeZone
KDoc or in make a separate KDoc for platform-specific currentSystemDefaultZone
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The common one, because when writing TimeZone.currentSystemDefault()
in common code, you should still be aware that this may fail.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
After an internal discussion, we decided that it makes more sense to return the UTC time zone by default. The reason is, because TimeZone.currentSystemDefault()
is not easily mockable (see #17), sometimes, there are calls to currentSystemDefault()
deep inside the existing code, and to have them throw exceptions now would be problematic. If/when we replace currentSystemDefault()
with a version that supports dependency injection better, that version will throw on WASI.
This should still be documented, and it should be in the common code.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So, you suggest to set currentSystemDefault
into the UTC
and document it, right?
Should we also need to put UTC
into availableTimeZoneIds
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes.
org.gradle.java.installations.fromEnv=JDK_8 | ||
|
||
group=org.jetbrains.kotlinx | ||
version=0.6.0-RC.2 | ||
versionSuffix=SNAPSHOT | ||
|
||
timezonesMajorVersion=1.0 | ||
tzdbVersion=2024a |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note (we'll have to discuss this internally): SemVer doesn't allow letters in the initial components, so 1.0.2024a
is an invalid version number. We could stick to this anyway, or we could use the less natural 1.0.2024-a
(note the dash).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In the end, this isn't a SemVer anyway, so dashes aren't needed, 2024a
is ok.
import org.jetbrains.kotlin.gradle.targets.js.npm.NpmResolverPlugin | ||
|
||
plugins { | ||
kotlin("multiplatform") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's a good call to have timezonesMajorVersion
so we could describe the compatibility between kotlinx-datetime
and the timezones package. It's easy to imagine people sticking to stable versions of the library but still upgrading the time zones.
But the Kotlin version is also part of the compatibility: there are no compatibility guarantees when someone uses the library compiled with the compiler version newer than the one used by the project itself, so to upgrade the timezone database correctly, you will need to upgrade Kotlin, which I think is strange.
I propose this scheme: kotlinx-datetime-timezones-full
is compiled with the oldest compiler that works, and when some incompatibility arises between the compiler used for kotlinx-datetime
and the one for kotlinx-datetime-timezones-full
, only then to upgrade the compiler here, while simultaneously upgrading the timezonesMajorVersion
.
Thoughts?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The only thing here is that we have no possibility (in current approach) to stick a range of major versions of timezones
to specific range of datetime
versions in gradle. So for now it could be only documented as compatibility table.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
After a discussion, here's the plan we arrived at:
- Initially, while WASI is allowed to remain binary-incompatible and the library itself is unstable, we want to version our timezone database artifacts like
2024a-spi.0.6.0
, where2024a
is the timezone database version and0.6.0
is the library version. The compatibility table isn't needed, because it's self-evident if the artifacts are compatible. - When both the library and WASI reach stability, we'll have
2028c-spi.1
,2029d-spi.2
, etc., where1
and2
is the version of the protocol by which the timezone artifact registers itself in the datetime library. Yes, these versions will need to be described in a compatibility table. - When we are completely satisfied with the protocol, we may drop the versioning entirely and only have things like
2036f
as our timezone database versions.
org.gradle.java.installations.fromEnv=JDK_8 | ||
|
||
group=org.jetbrains.kotlinx | ||
version=0.6.0-RC.2 | ||
versionSuffix=SNAPSHOT | ||
|
||
timezonesMajorVersion=1.0 | ||
tzdbVersion=2024a |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This may be out of this PR's scope, but it's possible to automatically detect that a new version of tzdb was published: https://github.com/ThreeTen/threetenbp/blob/main/.github/workflows/tzdbupdate.yml If you feel like implementing this, it would be nice; if not, we'll just do it later ourselves.
I have rebased onto the master and merged all the AMEND commits by @dkhalanskyjb request. |
sourceSets { | ||
val wasmWasiMain by getting { | ||
dependencies { | ||
implementation("kotlinx-datetime-zoneinfo", "2024a-spi.0.6.0-RC.2") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
implementation("kotlinx-datetime-zoneinfo", "2024a-spi.0.6.0-RC.2") | |
implementation("kotlinx-datetime-zoneinfo", "2024a-spi.0.6.0") |
core/common/test/InstantTest.kt
Outdated
val diff = instant1.periodUntil(instant2, TimeZone.currentSystemDefault()) | ||
val instant3 = instant1.plus(diff, TimeZone.currentSystemDefault()) | ||
val diff = instant1.periodUntil(instant2, TimeZone.UTC) | ||
val instant3 = instant1.plus(diff, TimeZone.UTC) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No longer needed, right?
This is internal API which is not intended to use on user-side. | ||
*/ | ||
@InternalDateTimeApi | ||
public interface TimeZonesProvider { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Suppressing such errors is not advisable by the compiler team and can stop working in K2
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We'll wait for 0.6.1 to be released from master before merging this
4425dba
to
f4fcea0
Compare
I sqashed commits so PR is ready to merge. All tests passed. Thank you a lot for the review! |
If this is good to go, could you rebase it on top of |
Co-authored-by: Dmitry Khalanskiy <[email protected]>
It is done. |
Great job, thanks! |
This is Wasm/WASI target implementation. It based on native version with hardcoded tzdb.
Before building timezones project you should run generateWasmWasiZoneInfo task to download and convert timezones db (nodejs and npm should be installed in the system). We decided to put the result of this task into the repository but for review it is not generated yet.