@@ -11,11 +11,7 @@ import kotlinx.datetime.internal.JSJoda.ZoneId
1111import kotlin.math.roundToInt
1212import kotlin.math.roundToLong
1313
14- private val tzdb: Result <TimeZoneDatabase > = runCatching { parseTzdb() }
15-
16- internal actual val systemTzdb: TimeZoneDatabase get() = tzdb.getOrThrow()
17-
18- private fun parseTzdb (): TimeZoneDatabase {
14+ private val tzdb: Result <TimeZoneDatabase ?> = runCatching {
1915 /* *
2016 * References:
2117 * - https://github.com/js-joda/js-joda/blob/8c1a7448db92ca014417346049fb64b55f7b1ac1/packages/timezone/src/MomentZoneRulesProvider.js#L78-L94
@@ -71,7 +67,7 @@ private fun parseTzdb(): TimeZoneDatabase {
7167 fun List<Long>.partialSums (): List <Long > = scanWithoutInitial(0 , Long ::plus)
7268
7369 val zones = mutableMapOf<String , TimeZoneRules >()
74- val (zonesPacked, linksPacked) = readTzdb() ? : return EmptyTimeZoneDatabase
70+ val (zonesPacked, linksPacked) = readTzdb() ? : return @runCatching null
7571 for (zone in zonesPacked) {
7672 val components = zone.split(' |' )
7773 val offsets = components[2 ].split(' ' ).map { unpackBase60(it) }
@@ -92,32 +88,65 @@ private fun parseTzdb(): TimeZoneDatabase {
9288 zones[components[1 ]] = rules
9389 }
9490 }
95- return object : TimeZoneDatabase {
91+ object : TimeZoneDatabase {
9692 override fun rulesForId (id : String ): TimeZoneRules =
9793 zones[id] ? : throw IllegalTimeZoneException (" Unknown time zone: $id " )
9894
9995 override fun availableTimeZoneIds (): Set <String > = zones.keys
10096 }
10197}
10298
103- private object EmptyTimeZoneDatabase : TimeZoneDatabase {
104- override fun rulesForId (id : String ): TimeZoneRules = when (id) {
105- " SYSTEM" -> TimeZoneRules (
106- transitionEpochSeconds = emptyList(),
107- offsets = listOf (UtcOffset .ZERO ),
108- recurringZoneRules = null
109- ) // TODO: that's not correct, we need to use `Date()`'s offset
110- else -> throw IllegalTimeZoneException (" JSJoda timezone database is not available" )
99+ private object SystemTimeZone: TimeZone() {
100+ override val id: String get() = " SYSTEM"
101+
102+ /* https://github.com/js-joda/js-joda/blob/8c1a7448db92ca014417346049fb64b55f7b1ac1/packages/core/src/LocalDate.js#L1404-L1416 +
103+ * https://github.com/js-joda/js-joda/blob/8c1a7448db92ca014417346049fb64b55f7b1ac1/packages/core/src/zone/SystemDefaultZoneRules.js#L69-L71 */
104+ override fun atStartOfDay (date : LocalDate ): Instant = atZone(date.atTime(LocalTime .MIN )).toInstant()
105+
106+ /* https://github.com/js-joda/js-joda/blob/8c1a7448db92ca014417346049fb64b55f7b1ac1/packages/core/src/zone/SystemDefaultZoneRules.js#L21-L24 */
107+ override fun offsetAtImpl (instant : Instant ): UtcOffset =
108+ UtcOffset (minutes = - Date (instant.toEpochMilliseconds().toDouble()).getTimezoneOffset().toInt())
109+
110+ /* https://github.com/js-joda/js-joda/blob/8c1a7448db92ca014417346049fb64b55f7b1ac1/packages/core/src/zone/SystemDefaultZoneRules.js#L49-L55 */
111+ override fun atZone (dateTime : LocalDateTime , preferred : UtcOffset ? ): ZonedDateTime {
112+ val epochMilli = dateTime.toInstant(UTC ).toEpochMilliseconds()
113+ val offsetInMinutesBeforePossibleTransition = Date (epochMilli.toDouble()).getTimezoneOffset().toInt()
114+ val epochMilliSystemZone = epochMilli +
115+ offsetInMinutesBeforePossibleTransition * SECONDS_PER_MINUTE * MILLIS_PER_ONE
116+ val offsetInMinutesAfterPossibleTransition = Date (epochMilliSystemZone.toDouble()).getTimezoneOffset().toInt()
117+ val offset = UtcOffset (minutes = - offsetInMinutesAfterPossibleTransition)
118+ return ZonedDateTime (dateTime, this , offset)
111119 }
112120
113- override fun availableTimeZoneIds (): Set <String > = emptySet()
121+ override fun equals (other : Any? ): Boolean = other == = this
122+
123+ override fun hashCode (): Int = id.hashCode()
124+ }
125+
126+ internal actual fun currentSystemDefaultZone (): Pair <String , TimeZone ?> {
127+ val id = ZoneId .systemDefault().id()
128+ return if (id == " SYSTEM" ) id to SystemTimeZone
129+ else id to null
130+ }
131+
132+ internal actual fun timeZoneById (zoneId : String ): TimeZone {
133+ val id = if (zoneId == " SYSTEM" ) {
134+ val (name, zone) = currentSystemDefaultZone()
135+ if (zone != null ) return zone
136+ name
137+ } else zoneId
138+ val rules = tzdb.getOrThrow()?.rulesForId(id)
139+ if (rules != null ) return RegionTimeZone (rules, id)
140+ throw IllegalTimeZoneException (" js-joda timezone database is not available" )
114141}
115142
116- internal actual fun currentSystemDefaultZone (): Pair <String , TimeZoneRules ? > =
117- ZoneId .systemDefault().id () to null
143+ internal actual fun getAvailableZoneIds (): Set <String > =
144+ tzdb.getOrThrow()?.availableTimeZoneIds () ? : setOf ( " UTC " )
118145
119146internal actual fun currentTime (): Instant = Instant .fromEpochMilliseconds(Date ().getTime().toLong())
120147
121148internal external class Date () {
149+ constructor (milliseconds: Double )
122150 fun getTime (): Double
151+ fun getTimezoneOffset (): Double
123152}
0 commit comments