@@ -56,7 +56,7 @@ private val instantParser: Parser<Instant>
56
56
}
57
57
58
58
// never fails: 9_999 years are always supported
59
- val localDate = dateVal.withYear(dateVal.year % 10000 ).plus(days, CalendarUnit .DAY )
59
+ val localDate = dateVal.withYear(dateVal.year % 10000 ).plus(days, DateTimeUnit .DAY )
60
60
val localTime = LocalTime .of(hours, min, seconds, 0 )
61
61
val secDelta: Long = try {
62
62
safeMultiply((dateVal.year / 10000 ).toLong(), SECONDS_PER_10000_YEARS )
@@ -285,10 +285,9 @@ private fun Instant.check(zone: TimeZone): Instant =
[email protected] {
285
285
actual fun Instant.plus (period : DateTimePeriod , zone : TimeZone ): Instant = try {
286
286
with (period) {
287
287
val withDate = toZonedLocalDateTimeFailing(zone)
288
- .run { if (years != 0 && months == 0 ) plusYears(years.toLong()) else this }
289
- .run { if (months != 0 ) plusMonths(years * 12L + months.toLong()) else this }
290
- .run { if (days != 0 ) plusDays(days.toLong()) else this }
291
- // See [Instant.plus(Instant, long, CalendarUnit, TimeZone)] for an explanation of time inside day being special
288
+ .run { if (years != 0 && months == 0 ) plus(years.toLong(), DateTimeUnit .YEAR ) else this }
289
+ .run { if (months != 0 ) plus(years * 12L + months.toLong(), DateTimeUnit .MONTH ) else this }
290
+ .run { if (days != 0 ) plus(days.toLong(), DateTimeUnit .DAY ) else this }
292
291
val secondsToAdd = safeAdd(seconds,
293
292
safeAdd(minutes.toLong() * SECONDS_PER_MINUTE , hours.toLong() * SECONDS_PER_HOUR ))
294
293
withDate.toInstant().plus(secondsToAdd, period.nanoseconds)
@@ -299,33 +298,15 @@ actual fun Instant.plus(period: DateTimePeriod, zone: TimeZone): Instant = try {
299
298
throw DateTimeArithmeticException (" Boundaries of Instant exceeded when adding CalendarPeriod" , e)
300
299
}
301
300
302
- internal actual fun Instant.plus (value : Long , unit : CalendarUnit , zone : TimeZone ): Instant = try {
301
+ internal actual fun Instant.plus (value : Long , unit : CalendarUnit , zone : TimeZone ): Instant =
302
+ plusDateTimeUnit(value, unit.dateTimeUnit, zone)
303
+
304
+ internal fun Instant.plusDateTimeUnit (value : Long , unit : DateTimeUnit , zone : TimeZone ): Instant = try {
303
305
when (unit) {
304
- CalendarUnit .YEAR -> toZonedLocalDateTimeFailing(zone).plusYears(value).toInstant()
305
- CalendarUnit .MONTH -> toZonedLocalDateTimeFailing(zone).plusMonths(value).toInstant()
306
- CalendarUnit .DAY -> toZonedLocalDateTimeFailing(zone).plusDays(value).toInstant()
307
- /* From org.threeten.bp.ZonedDateTime#plusHours: the time is added to the raw LocalDateTime,
308
- then org.threeten.bp.ZonedDateTime#create is called on the absolute instant
309
- (gotten from org.threeten.bp.chrono.ChronoLocalDateTime#toEpochSecond). This, in turn,
310
- finds an applicable offset and builds the local representation of the zoned datetime.
311
- If we then feed the result to org.threeten.bp.chrono.ChronoZonedDateTime#toInstant, it simply
312
- builds the instant with org.threeten.bp.chrono.ChronoZonedDateTime#toEpochSecond, which, once
313
- more, finds the absolute instant. Thus, we can summarize the composition of `atZone`, `plusHours`
314
- and `toInstant` like this:
315
- 1. An absolute instant is converted to a zoned datetime by adding an offset;
316
- 2. Time is added to a datetime, ignoring the offset;
317
- 3. Using the know offset, it is converted to an absolute instant;
318
- 4. The instant is adapted to a zoned datetime representation;
319
- 5. The zoned datetime is converted to an absolute instant.
320
- 4 and 5 are invertible by their form: their composition adds and subtracts the offset to and from
321
- the unix epoch. 1-3 can then be simplified to just adding the time to the instant directly.
322
- */
323
- CalendarUnit .HOUR -> plus(safeMultiply(value, SECONDS_PER_HOUR .toLong()), 0 ).check(zone)
324
- CalendarUnit .MINUTE -> plus(safeMultiply(value, SECONDS_PER_MINUTE .toLong()), 0 ).check(zone)
325
- CalendarUnit .SECOND -> plus(value, 0 ).check(zone)
326
- CalendarUnit .MILLISECOND -> plus(value / MILLIS_PER_ONE , (value % MILLIS_PER_ONE ) * NANOS_PER_MILLI ).check(zone)
327
- CalendarUnit .MICROSECOND -> plus(value / MICROS_PER_ONE , (value % MICROS_PER_ONE ) * NANOS_PER_MICRO ).check(zone)
328
- CalendarUnit .NANOSECOND -> plus(0 , value).check(zone)
306
+ is DateTimeUnit .DateBased -> toZonedLocalDateTimeFailing(zone).plus(value, unit).toInstant()
307
+ is DateTimeUnit .TimeBased -> multiplyAndDivide(value, unit.nanoseconds, NANOS_PER_ONE .toLong()).let {
308
+ (seconds, nanoseconds) -> check(zone).plus(seconds, nanoseconds).check(zone)
309
+ }
329
310
}
330
311
} catch (e: ArithmeticException ) {
331
312
throw DateTimeArithmeticException (" Arithmetic overflow when adding to an Instant" , e)
@@ -338,21 +319,30 @@ actual fun Instant.periodUntil(other: Instant, zone: TimeZone): DateTimePeriod {
338
319
var thisLdt = toZonedLocalDateTimeFailing(zone)
339
320
val otherLdt = other.toZonedLocalDateTimeFailing(zone)
340
321
341
- val months = thisLdt.until(otherLdt, CalendarUnit .MONTH ) // `until` on dates never fails
342
- thisLdt = thisLdt.plusMonths (months) // won't throw: thisLdt + months <= otherLdt, which is known to be valid
343
- val days = thisLdt.until(otherLdt, CalendarUnit .DAY ) // `until` on dates never fails
344
- thisLdt = thisLdt.plusDays (days) // won't throw: thisLdt + days <= otherLdt
345
- val time = thisLdt.until(otherLdt, CalendarUnit .NANOSECOND ).nanoseconds // |otherLdt - thisLdt| < 24h
322
+ val months = thisLdt.until(otherLdt, DateTimeUnit .MONTH ) // `until` on dates never fails
323
+ thisLdt = thisLdt.plus (months, DateTimeUnit . MONTH ) // won't throw: thisLdt + months <= otherLdt, which is known to be valid
324
+ val days = thisLdt.until(otherLdt, DateTimeUnit .DAY ) // `until` on dates never fails
325
+ thisLdt = thisLdt.plus (days, DateTimeUnit . DAY ) // won't throw: thisLdt + days <= otherLdt
326
+ val time = thisLdt.until(otherLdt, DateTimeUnit .NANOSECOND ).nanoseconds // |otherLdt - thisLdt| < 24h
346
327
347
328
time.toComponents { hours, minutes, seconds, nanoseconds ->
348
329
return DateTimePeriod ((months / 12 ).toInt(), (months % 12 ).toInt(), days.toInt(), hours, minutes, seconds.toLong(), nanoseconds.toLong())
349
330
}
350
331
}
351
332
352
333
public actual fun Instant.until (other : Instant , unit : DateTimeUnit , zone : TimeZone ): Long =
353
- try {
354
- // TODO: inline 'until' here and simplify operation for time-based units
355
- toZonedLocalDateTimeFailing(zone).until(other.toZonedLocalDateTimeFailing(zone), unit.calendarUnit) / unit.calendarScale
356
- } catch (e: ArithmeticException ) {
357
- if (this < other) Long .MAX_VALUE else Long .MIN_VALUE
334
+ when (unit) {
335
+ is DateTimeUnit .DateBased ->
336
+ toZonedLocalDateTimeFailing(zone).dateTime.until(other.toZonedLocalDateTimeFailing(zone).dateTime, unit)
337
+ is DateTimeUnit .TimeBased -> {
338
+ check(zone); other.check(zone)
339
+ try {
340
+ multiplyAddAndDivide(other.epochSeconds - epochSeconds,
341
+ NANOS_PER_ONE .toLong(),
342
+ (other.nanosecondsOfSecond - nanosecondsOfSecond).toLong(),
343
+ unit.nanoseconds)
344
+ } catch (e: ArithmeticException ) {
345
+ if (this < other) Long .MAX_VALUE else Long .MIN_VALUE
346
+ }
347
+ }
358
348
}
0 commit comments