@@ -8,8 +8,6 @@ package kotlinx.datetime.internal
8
8
import kotlinx.datetime.*
9
9
import kotlinx.datetime.UtcOffset
10
10
import kotlinx.datetime.internal.JSJoda.ZoneId
11
- import kotlin.math.roundToInt
12
- import kotlin.math.roundToLong
13
11
14
12
private val tzdb: Result <TimeZoneDatabase ?> = runCatching {
15
13
/* *
@@ -25,49 +23,56 @@ private val tzdb: Result<TimeZoneDatabase?> = runCatching {
25
23
in ' A' .. ' X' -> char - ' A' + 36
26
24
else -> throw IllegalArgumentException (" Invalid character: $char " )
27
25
}
28
- fun unpackBase60 (string : String ): Double {
26
+ /* * converts a base60 number of minutes to a whole number of seconds */
27
+ fun base60MinutesInSeconds (string : String ): Long {
29
28
var i = 0
30
29
var parts = string.split(' .' )
31
- val whole = parts[0 ]
32
- var multiplier = 1.0
33
- var out = 0.0
34
- var sign = 1
35
30
36
31
// handle negative numbers
37
- if (string.startsWith(' -' )) {
32
+ val sign = if (string.startsWith(' -' )) {
38
33
i = 1
39
- sign = - 1
34
+ - 1
35
+ } else {
36
+ 1
40
37
}
41
38
42
- // handle digits before the decimal
39
+ var wholeMinutes: Long = 0
40
+ val whole = parts[0 ]
41
+ // handle digits before the decimal (whole minutes)
43
42
for (ix in i.. whole.lastIndex) {
44
- out = 60 * out + charCodeToInt(whole[ix])
43
+ wholeMinutes = 60 * wholeMinutes + charCodeToInt(whole[ix])
45
44
}
46
45
47
- // handle digits after the decimal
48
- parts.getOrNull(1 )?.let { fractional ->
49
- for (c in fractional) {
50
- multiplier = multiplier / 60.0
51
- out + = charCodeToInt(c) * multiplier
46
+ // handle digits after the decimal (seconds and less)
47
+ val seconds = parts.getOrNull(1 )?.let { fractional ->
48
+ when (fractional.length) {
49
+ 1 -> charCodeToInt(fractional[0 ]) // single digit, representing seconds
50
+ 0 -> 0 // actually no fractional part
51
+ else -> {
52
+ charCodeToInt(fractional[0 ]) + charCodeToInt(fractional[1 ]).let {
53
+ if (it >= 30 ) 1 else 0 // rounding the seconds digit
54
+ }
55
+ }
52
56
}
53
- }
57
+ } ? : 0
54
58
55
- return out * sign
59
+ return (wholeMinutes * SECONDS_PER_MINUTE + seconds) * sign
56
60
}
57
61
58
62
val zones = mutableMapOf<String , TimeZoneRules >()
59
63
val (zonesPacked, linksPacked) = readTzdb() ? : return @runCatching null
60
64
for (zone in zonesPacked) {
61
65
val components = zone.split(' |' )
62
- val offsets = components[2 ].split(' ' ).map { unpackBase60(it) }
63
- val indices = components[3 ].map { charCodeToInt(it) }
64
- val lengthsOfPeriodsWithOffsets = components[4 ].split(' ' ).map {
65
- (unpackBase60(it) * SECONDS_PER_MINUTE * MILLIS_PER_ONE ).roundToLong() / // minutes to milliseconds
66
- MILLIS_PER_ONE // but we only need seconds
66
+ val offsets = components[2 ].split(' ' ).map {
67
+ UtcOffset (null , null , - base60MinutesInSeconds(it).toInt())
67
68
}
69
+ val indices = components[3 ].map { charCodeToInt(it) }
70
+ val lengthsOfPeriodsWithOffsets = components[4 ].split(' ' ).map(::base60MinutesInSeconds)
68
71
zones[components[0 ]] = TimeZoneRules (
69
- transitionEpochSeconds = lengthsOfPeriodsWithOffsets.runningReduce(Long ::plus).take<Long >(indices.size - 1 ),
70
- offsets = indices.map { UtcOffset (null , null , - (offsets[it] * 60 ).roundToInt()) },
72
+ transitionEpochSeconds = lengthsOfPeriodsWithOffsets.runningReduce(Long ::plus).let {
73
+ if (it.size == indices.size - 1 ) it else it.take<Long >(indices.size - 1 )
74
+ },
75
+ offsets = indices.map { offsets[it] },
71
76
recurringZoneRules = null
72
77
)
73
78
}
0 commit comments