-
Notifications
You must be signed in to change notification settings - Fork 109
/
Copy pathInstant.kt
185 lines (157 loc) · 7.31 KB
/
Instant.kt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
/*
* Copyright 2019-2020 JetBrains s.r.o.
* Use of this source code is governed by the Apache 2.0 License that can be found in the LICENSE.txt file.
*/
/* Based on the ThreeTenBp project.
* Copyright (c) 2007-present, Stephen Colebourne & Michael Nascimento Santos
*/
package kotlinx.datetime
import kotlinx.datetime.format.*
import kotlinx.datetime.internal.*
import kotlinx.datetime.serializers.InstantIso8601Serializer
import kotlinx.serialization.Serializable
import kotlin.time.*
import kotlin.time.Duration.Companion.nanoseconds
import kotlin.time.Duration.Companion.seconds
public actual enum class DayOfWeek {
MONDAY,
TUESDAY,
WEDNESDAY,
THURSDAY,
FRIDAY,
SATURDAY,
SUNDAY;
}
/**
* The minimum supported epoch second.
*/
private const val MIN_SECOND = -31557014167219200L // -1000000000-01-01T00:00:00Z
/**
* The maximum supported epoch second.
*/
private const val MAX_SECOND = 31556889864403199L // +1000000000-12-31T23:59:59
@Serializable(with = InstantIso8601Serializer::class)
public actual class Instant internal constructor(public actual val epochSeconds: Long, public actual val nanosecondsOfSecond: Int) : Comparable<Instant> {
init {
require(epochSeconds in MIN_SECOND..MAX_SECOND) { "Instant exceeds minimum or maximum instant" }
}
// org.threeten.bp.Instant#toEpochMilli
public actual fun toEpochMilliseconds(): Long = try {
if (epochSeconds >= 0) {
val millis = safeMultiply(epochSeconds, MILLIS_PER_ONE.toLong())
safeAdd(millis, (nanosecondsOfSecond / NANOS_PER_MILLI).toLong())
} else {
// prevent an overflow in seconds * 1000
// instead of going form the second farther away from 0
// going toward 0
// we go from the second closer to 0 away from 0
// that way we always stay in the valid long range
// seconds + 1 can not overflow because it is negative
val millis = safeMultiply(epochSeconds + 1, MILLIS_PER_ONE.toLong())
safeAdd(millis, (nanosecondsOfSecond / NANOS_PER_MILLI - MILLIS_PER_ONE).toLong())
}
} catch (_: ArithmeticException) {
if (epochSeconds > 0) Long.MAX_VALUE else Long.MIN_VALUE
}
public actual operator fun plus(duration: Duration): Instant = duration.toComponents { secondsToAdd, nanosecondsToAdd ->
try {
plus(secondsToAdd, nanosecondsToAdd.toLong())
} catch (_: IllegalArgumentException) {
if (duration.isPositive()) MAX else MIN
} catch (_: ArithmeticException) {
if (duration.isPositive()) MAX else MIN
}
}
public actual operator fun minus(duration: Duration): Instant = plus(-duration)
public actual operator fun minus(other: Instant): Duration =
(this.epochSeconds - other.epochSeconds).seconds + // won't overflow given the instant bounds
(this.nanosecondsOfSecond - other.nanosecondsOfSecond).nanoseconds
actual override fun compareTo(other: Instant): Int {
val s = epochSeconds.compareTo(other.epochSeconds)
if (s != 0) {
return s
}
return nanosecondsOfSecond.compareTo(other.nanosecondsOfSecond)
}
override fun equals(other: Any?): Boolean =
this === other || other is Instant && this.epochSeconds == other.epochSeconds && this.nanosecondsOfSecond == other.nanosecondsOfSecond
// org.threeten.bp.Instant#hashCode
override fun hashCode(): Int =
(epochSeconds xor (epochSeconds ushr 32)).toInt() + 51 * nanosecondsOfSecond
// org.threeten.bp.format.DateTimeFormatterBuilder.InstantPrinterParser#print
actual override fun toString(): String = format(ISO_DATE_TIME_OFFSET_WITH_TRAILING_ZEROS)
public actual companion object {
internal actual val MIN = Instant(MIN_SECOND, 0)
internal actual val MAX = Instant(MAX_SECOND, 999_999_999)
@Deprecated("Use Clock.System.now() instead", ReplaceWith("Clock.System.now()", "kotlinx.datetime.Clock"), level = DeprecationLevel.ERROR)
public actual fun now(): Instant = currentTime()
// org.threeten.bp.Instant#ofEpochMilli
public actual fun fromEpochMilliseconds(epochMilliseconds: Long): Instant {
val epochSeconds = epochMilliseconds.floorDiv(MILLIS_PER_ONE.toLong())
val nanosecondsOfSecond = (epochMilliseconds.mod(MILLIS_PER_ONE.toLong()) * NANOS_PER_MILLI).toInt()
return fromEpochSeconds(epochSeconds, nanosecondsOfSecond)
}
/**
* @throws ArithmeticException if arithmetic overflow occurs
* @throws IllegalArgumentException if the boundaries of Instant are overflown
*/
internal fun fromEpochSecondsThrowing(epochSeconds: Long, nanosecondAdjustment: Long): Instant {
val secs = safeAdd(epochSeconds, nanosecondAdjustment.floorDiv(NANOS_PER_ONE.toLong()))
val nos = nanosecondAdjustment.mod(NANOS_PER_ONE.toLong()).toInt()
return Instant(secs, nos)
}
// org.threeten.bp.Instant#ofEpochSecond(long, long)
public actual fun fromEpochSeconds(epochSeconds: Long, nanosecondAdjustment: Long): Instant =
try {
fromEpochSecondsThrowing(epochSeconds, nanosecondAdjustment)
} catch (_: ArithmeticException) {
if (epochSeconds > 0) MAX else MIN
} catch (_: IllegalArgumentException) {
if (epochSeconds > 0) MAX else MIN
}
public actual fun fromEpochSeconds(epochSeconds: Long, nanosecondAdjustment: Int): Instant =
fromEpochSeconds(epochSeconds, nanosecondAdjustment.toLong())
public actual fun parse(input: CharSequence, format: DateTimeFormat<DateTimeComponents>): Instant = try {
format.parse(input).toInstantUsingOffset()
} catch (e: IllegalArgumentException) {
throw DateTimeFormatException("Failed to parse an instant from '$input'", e)
}
@Deprecated("This overload is only kept for binary compatibility", level = DeprecationLevel.HIDDEN)
public fun parse(isoString: String): Instant = parse(input = isoString)
public actual val DISTANT_PAST: Instant = fromEpochSeconds(DISTANT_PAST_SECONDS, 999_999_999)
public actual val DISTANT_FUTURE: Instant = fromEpochSeconds(DISTANT_FUTURE_SECONDS, 0)
}
}
private val ISO_DATE_TIME_OFFSET_WITH_TRAILING_ZEROS = DateTimeComponents.Format {
date(ISO_DATE)
alternativeParsing({
char('t')
}) {
char('T')
}
hour()
char(':')
minute()
char(':')
second()
optional {
char('.')
secondFractionInternal(1, 9, FractionalSecondDirective.GROUP_BY_THREE)
}
isoOffset(
zOnZero = true,
useSeparator = true,
outputMinute = WhenToOutput.IF_NONZERO,
outputSecond = WhenToOutput.IF_NONZERO
)
}
// org.threeten.bp.Instant#plus(long, long)
internal actual fun Instant.plus(secondsToAdd: Long, nanosToAdd: Long): Instant {
if ((secondsToAdd or nanosToAdd) == 0L) {
return this
}
val newEpochSeconds: Long = safeAdd(safeAdd(epochSeconds, secondsToAdd), (nanosToAdd / NANOS_PER_ONE))
val newNanosToAdd = nanosToAdd % NANOS_PER_ONE
val nanoAdjustment = (nanosecondsOfSecond + newNanosToAdd) // safe int+NANOS_PER_ONE
return Instant.fromEpochSecondsThrowing(newEpochSeconds, nanoAdjustment)
}