6
6
* Copyright (c) 2007-present, Stephen Colebourne & Michael Nascimento Santos
7
7
*/
8
8
9
+ @file:OptIn(ExperimentalForeignApi ::class )
10
+
9
11
package kotlinx.datetime
10
12
13
+ import kotlinx.cinterop.ExperimentalForeignApi
14
+ import kotlinx.cinterop.memScoped
11
15
import kotlinx.datetime.internal.*
12
16
import kotlinx.datetime.serializers.*
13
17
import kotlinx.serialization.Serializable
@@ -19,7 +23,7 @@ public actual open class TimeZone internal constructor() {
19
23
20
24
public actual fun currentSystemDefault (): TimeZone =
21
25
// TODO: probably check if currentSystemDefault name is parseable as FixedOffsetTimeZone?
22
- RegionTimeZone .currentSystemDefault ()
26
+ currentSystemDefaultZone ()
23
27
24
28
public actual val UTC : FixedOffsetTimeZone = UtcOffset .ZERO .asTimeZone()
25
29
@@ -59,11 +63,15 @@ public actual open class TimeZone internal constructor() {
59
63
} catch (e: DateTimeFormatException ) {
60
64
throw IllegalTimeZoneException (e)
61
65
}
62
- return RegionTimeZone .of(zoneId)
66
+ return try {
67
+ RegionTimeZone (systemTzdb.rulesForId(zoneId), zoneId)
68
+ } catch (e: Exception ) {
69
+ throw IllegalTimeZoneException (" Invalid zone ID: $zoneId " , e)
70
+ }
63
71
}
64
72
65
73
public actual val availableZoneIds: Set <String >
66
- get() = RegionTimeZone .availableZoneIds
74
+ get() = systemTzdb.availableTimeZoneIds()
67
75
}
68
76
69
77
public actual open val id: String
@@ -95,17 +103,45 @@ public actual open class TimeZone internal constructor() {
95
103
override fun toString (): String = id
96
104
}
97
105
98
- internal expect class RegionTimeZone : TimeZone {
99
- override val id: String
100
- override fun atStartOfDay (date : LocalDate ): Instant
101
- override fun offsetAtImpl (instant : Instant ): UtcOffset
102
- override fun atZone (dateTime : LocalDateTime , preferred : UtcOffset ? ): ZonedDateTime
106
+ internal interface TimezoneDatabase {
107
+ fun rulesForId (id : String ): TimeZoneRules
108
+ fun availableTimeZoneIds (): Set <String >
109
+ }
110
+
111
+ internal expect val systemTzdb: TimezoneDatabase
103
112
104
- companion object {
105
- fun of (zoneId : String ): RegionTimeZone
106
- fun currentSystemDefault (): RegionTimeZone
107
- val availableZoneIds: Set <String >
113
+ internal expect fun currentSystemDefaultZone (): RegionTimeZone
114
+
115
+ internal class RegionTimeZone (private val tzid : TimeZoneRules , override val id : String ) : TimeZone() {
116
+
117
+ override fun atStartOfDay (date : LocalDate ): Instant = memScoped {
118
+ val ldt = LocalDateTime (date, LocalTime .MIN )
119
+ when (val info = tzid.infoAtDatetime(ldt)) {
120
+ is OffsetInfo .Regular -> ldt.toInstant(info.offset)
121
+ is OffsetInfo .Gap -> info.start
122
+ is OffsetInfo .Overlap -> ldt.toInstant(info.offsetBefore)
123
+ }
108
124
}
125
+
126
+ override fun atZone (dateTime : LocalDateTime , preferred : UtcOffset ? ): ZonedDateTime =
127
+ when (val info = tzid.infoAtDatetime(dateTime)) {
128
+ is OffsetInfo .Regular -> ZonedDateTime (dateTime, this , info.offset)
129
+ is OffsetInfo .Gap -> {
130
+ try {
131
+ ZonedDateTime (dateTime.plusSeconds(info.transitionDurationSeconds), this , info.offsetAfter)
132
+ } catch (e: IllegalArgumentException ) {
133
+ throw DateTimeArithmeticException (
134
+ " Overflow whet correcting the date-time to not be in the transition gap" ,
135
+ e
136
+ )
137
+ }
138
+ }
139
+
140
+ is OffsetInfo .Overlap -> ZonedDateTime (dateTime, this ,
141
+ if (info.offsetAfter == preferred) info.offsetAfter else info.offsetBefore)
142
+ }
143
+
144
+ override fun offsetAtImpl (instant : Instant ): UtcOffset = tzid.infoAtInstant(instant)
109
145
}
110
146
111
147
0 commit comments