@@ -11,11 +11,7 @@ import kotlinx.datetime.internal.JSJoda.ZoneId
11
11
import kotlin.math.roundToInt
12
12
import kotlin.math.roundToLong
13
13
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 {
19
15
/* *
20
16
* References:
21
17
* - https://github.com/js-joda/js-joda/blob/8c1a7448db92ca014417346049fb64b55f7b1ac1/packages/timezone/src/MomentZoneRulesProvider.js#L78-L94
@@ -71,7 +67,7 @@ private fun parseTzdb(): TimeZoneDatabase {
71
67
fun List<Long>.partialSums (): List <Long > = scanWithoutInitial(0 , Long ::plus)
72
68
73
69
val zones = mutableMapOf<String , TimeZoneRules >()
74
- val (zonesPacked, linksPacked) = readTzdb() ? : return EmptyTimeZoneDatabase
70
+ val (zonesPacked, linksPacked) = readTzdb() ? : return @runCatching null
75
71
for (zone in zonesPacked) {
76
72
val components = zone.split(' |' )
77
73
val offsets = components[2 ].split(' ' ).map { unpackBase60(it) }
@@ -92,32 +88,65 @@ private fun parseTzdb(): TimeZoneDatabase {
92
88
zones[components[1 ]] = rules
93
89
}
94
90
}
95
- return object : TimeZoneDatabase {
91
+ object : TimeZoneDatabase {
96
92
override fun rulesForId (id : String ): TimeZoneRules =
97
93
zones[id] ? : throw IllegalTimeZoneException (" Unknown time zone: $id " )
98
94
99
95
override fun availableTimeZoneIds (): Set <String > = zones.keys
100
96
}
101
97
}
102
98
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)
111
119
}
112
120
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" )
114
141
}
115
142
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 " )
118
145
119
146
internal actual fun currentTime (): Instant = Instant .fromEpochMilliseconds(Date ().getTime().toLong())
120
147
121
148
internal external class Date () {
149
+ constructor (milliseconds: Double )
122
150
fun getTime (): Double
151
+ fun getTimezoneOffset (): Double
123
152
}
0 commit comments