All Downloads are FREE. Search and download functionalities are using the official Maven repository.

commonMain.io.islandtime.Instant.kt Maven / Gradle / Ivy

The newest version!
@file:Suppress("FunctionName")

package io.islandtime

import dev.erikchristensen.javamath2kmp.floorDiv
import dev.erikchristensen.javamath2kmp.floorMod
import dev.erikchristensen.javamath2kmp.plusExact
import dev.erikchristensen.javamath2kmp.timesExact
import io.islandtime.base.DateTimeField
import io.islandtime.base.TimePoint
import io.islandtime.internal.*
import io.islandtime.measures.*
import io.islandtime.measures.internal.plusWithOverflow
import io.islandtime.parser.*
import io.islandtime.ranges.InstantInterval

/**
 * A platform-specific representation of an instant in time.
 */
expect class PlatformInstant

/**
 * An instant in time with nanosecond-precision.
 */
class Instant private constructor(
    override val secondOfUnixEpoch: Long,
    override val nanosecond: Int
) : TimePoint,
    Comparable {

    init {
        if (secondOfUnixEpoch !in MIN_SECOND..MAX_SECOND) {
            throw DateTimeException("'$secondOfUnixEpoch' is outside the supported second range")
        }
    }

    override val secondsSinceUnixEpoch: LongSeconds
        get() = secondOfUnixEpoch.seconds

    override val additionalNanosecondsSinceUnixEpoch: IntNanoseconds
        get() = nanosecond.nanoseconds

    override val millisecondsSinceUnixEpoch: LongMilliseconds
        get() = secondsSinceUnixEpoch + additionalNanosecondsSinceUnixEpoch.inMilliseconds

    override val millisecondOfUnixEpoch: Long
        get() = millisecondsSinceUnixEpoch.value

    operator fun plus(other: Duration): Instant {
        return when {
            other.isZero() -> this
            else -> plus(other.seconds, other.nanosecondAdjustment)
        }
    }

    operator fun plus(days: IntDays) = plus(days.toLongDays().inSecondsUnchecked, 0.nanoseconds)
    operator fun plus(days: LongDays) = plus(days.inSeconds, 0.nanoseconds)

    override operator fun plus(hours: IntHours) = plus(hours.toLongHours().inSecondsUnchecked, 0.nanoseconds)
    override operator fun plus(hours: LongHours) = plus(hours.inSeconds, 0.nanoseconds)

    override operator fun plus(minutes: IntMinutes) = plus(minutes.toLongMinutes().inSecondsUnchecked, 0.nanoseconds)
    override operator fun plus(minutes: LongMinutes) = plus(minutes.inSeconds, 0.nanoseconds)

    override operator fun plus(seconds: IntSeconds) = plus(seconds.toLongSeconds(), 0.nanoseconds)
    override operator fun plus(seconds: LongSeconds) = plus(seconds, 0.nanoseconds)

    override operator fun plus(milliseconds: IntMilliseconds) = plus(milliseconds.inNanoseconds)

    override operator fun plus(milliseconds: LongMilliseconds): Instant {
        return plus(
            milliseconds.inSeconds,
            ((milliseconds.value % MILLISECONDS_PER_SECOND).toInt() * NANOSECONDS_PER_MILLISECOND).nanoseconds
        )
    }

    override operator fun plus(microseconds: IntMicroseconds) = plus(microseconds.inNanoseconds)

    override operator fun plus(microseconds: LongMicroseconds): Instant {
        return plus(
            microseconds.inSeconds,
            ((microseconds.value % MICROSECONDS_PER_SECOND).toInt() * NANOSECONDS_PER_MICROSECOND).nanoseconds
        )
    }

    override operator fun plus(nanoseconds: IntNanoseconds) = plus(nanoseconds.toLongNanoseconds())

    override operator fun plus(nanoseconds: LongNanoseconds): Instant {
        return plus(
            nanoseconds.inSeconds,
            (nanoseconds % NANOSECONDS_PER_SECOND).toIntNanosecondsUnchecked()
        )
    }

    operator fun minus(other: Duration): Instant {
        return if (other.seconds.value == Long.MIN_VALUE) {
            plus(Long.MAX_VALUE.seconds, other.nanosecondAdjustment.negateUnchecked())
        } else {
            plus(other.seconds.negateUnchecked(), other.nanosecondAdjustment.negateUnchecked())
        }
    }

    operator fun minus(days: IntDays) = plus(days.toLongDays().negateUnchecked())

    operator fun minus(days: LongDays): Instant {
        return if (days.value == Long.MIN_VALUE) {
            this + Long.MAX_VALUE.days + 1.days
        } else {
            plus(days.negateUnchecked())
        }
    }

    override operator fun minus(hours: IntHours) = plus(hours.toLongHours().negateUnchecked())

    override operator fun minus(hours: LongHours): Instant {
        return if (hours.value == Long.MIN_VALUE) {
            this + Long.MAX_VALUE.hours + 1.hours
        } else {
            plus(-hours)
        }
    }

    override operator fun minus(minutes: IntMinutes) = plus(minutes.toLongMinutes().negateUnchecked())

    override operator fun minus(minutes: LongMinutes): Instant {
        return if (minutes.value == Long.MIN_VALUE) {
            this + Long.MAX_VALUE.minutes + 1.minutes
        } else {
            plus(minutes.negateUnchecked())
        }
    }

    override operator fun minus(seconds: IntSeconds) = plus(seconds.toLongSeconds().negateUnchecked())

    override operator fun minus(seconds: LongSeconds): Instant {
        return if (seconds.value == Long.MIN_VALUE) {
            this + Long.MAX_VALUE.seconds + 1.seconds
        } else {
            plus(seconds.negateUnchecked())
        }
    }

    override operator fun minus(milliseconds: IntMilliseconds) =
        plus(milliseconds.toLongMilliseconds().negateUnchecked())

    override operator fun minus(milliseconds: LongMilliseconds): Instant {
        return if (milliseconds.value == Long.MIN_VALUE) {
            this + Long.MAX_VALUE.milliseconds + 1.milliseconds
        } else {
            plus(milliseconds.negateUnchecked())
        }
    }

    override operator fun minus(microseconds: IntMicroseconds) =
        plus(microseconds.toLongMicroseconds().negateUnchecked())

    override operator fun minus(microseconds: LongMicroseconds): Instant {
        return if (microseconds.value == Long.MIN_VALUE) {
            this + Long.MAX_VALUE.microseconds + 1.microseconds
        } else {
            plus(microseconds.negateUnchecked())
        }
    }

    override operator fun minus(nanoseconds: IntNanoseconds) = plus(nanoseconds.toLongNanoseconds().negateUnchecked())

    override operator fun minus(nanoseconds: LongNanoseconds): Instant {
        return if (nanoseconds.value == Long.MIN_VALUE) {
            this + Long.MAX_VALUE.nanoseconds + 1.nanoseconds
        } else {
            plus(nanoseconds.negateUnchecked())
        }
    }

    operator fun rangeTo(other: Instant) = InstantInterval.withInclusiveEnd(this, other)

    override fun compareTo(other: Instant): Int {
        val secondsDiff = secondOfUnixEpoch.compareTo(other.secondOfUnixEpoch)

        return if (secondsDiff != 0) {
            secondsDiff
        } else {
            nanosecond - other.nanosecond
        }
    }

    /**
     * Converts this instant to a string in ISO-8601 extended format. For example, `2020-02-13T02:30:05.367Z`.
     */
    override fun toString() = buildString(MAX_INSTANT_STRING_LENGTH) { appendInstant(this@Instant) }

    private fun plus(secondsToAdd: LongSeconds, nanosecondsToAdd: IntNanoseconds): Instant {
        return if (secondsToAdd.value == 0L && nanosecondsToAdd.value == 0) {
            this
        } else {
            Instant(
                secondsSinceUnixEpoch + secondsToAdd,
                additionalNanosecondsSinceUnixEpoch plusWithOverflow nanosecondsToAdd
            )
        }
    }

    override fun equals(other: Any?): Boolean {
        return this === other || (other is Instant && secondOfUnixEpoch == other.secondOfUnixEpoch && nanosecond == other.nanosecond)
    }

    override fun hashCode(): Int {
        return 31 * secondOfUnixEpoch.hashCode() + nanosecond
    }

    companion object {
        private const val MIN_SECOND = -31557014167219200L
        private const val MAX_SECOND = 31556889864403199L

        /**
         * The earliest supported [Instant], which can be used as a "far past" sentinel.
         */
        val MIN = fromSecondOfUnixEpoch(MIN_SECOND)

        /**
         * The latest supported [Instant], which can be used as a "far future" sentinel.
         */
        val MAX = fromSecondOfUnixEpoch(MAX_SECOND, 999_999_999L)

        /**
         * The [Instant] representing the Unix epoch of 1970-01-01T00:00Z.
         */
        val UNIX_EPOCH = fromSecondOfUnixEpoch(0L)

        /**
         * Creates an [Instant] from the second of the Unix epoch.
         */
        fun fromSecondOfUnixEpoch(second: Long): Instant {
            return Instant(second, 0)
        }

        /**
         * Creates an [Instant] from the second of the Unix epoch.
         */
        fun fromSecondOfUnixEpoch(second: Long, nanosecond: Int): Instant {
            return fromSecondOfUnixEpoch(second, nanosecond.toLong())
        }

        /**
         * Creates an [Instant] from the second of the Unix epoch.
         */
        fun fromSecondOfUnixEpoch(second: Long, nanosecond: Long): Instant {
            val newSecond = second plusExact (nanosecond floorDiv NANOSECONDS_PER_SECOND)
            val newNanosecond = (nanosecond floorMod NANOSECONDS_PER_SECOND).toInt()

            return Instant(newSecond, newNanosecond)
        }

        /**
         * Creates an [Instant] from the millisecond of the Unix epoch.
         */
        fun fromMillisecondOfUnixEpoch(millisecond: Long): Instant {
            val second = millisecond floorDiv MILLISECONDS_PER_SECOND
            val nanosecond = (millisecond floorMod MILLISECONDS_PER_SECOND).toInt() * NANOSECONDS_PER_MILLISECOND
            return Instant(second, nanosecond)
        }

        @Deprecated(
            "Use fromSecondOfUnixEpoch() instead.",
            ReplaceWith("Instant.fromSecondOfUnixEpoch(second)"),
            DeprecationLevel.ERROR
        )
        fun fromUnixEpochSecond(second: Long): Instant {
            return fromSecondOfUnixEpoch(second)
        }

        @Deprecated(
            "Use fromSecondOfUnixEpoch() instead.",
            ReplaceWith("Instant.fromSecondOfUnixEpoch(second, nanosecondAdjustment)"),
            DeprecationLevel.ERROR
        )
        fun fromUnixEpochSecond(second: Long, nanosecondAdjustment: Int): Instant {
            return fromSecondOfUnixEpoch(second, nanosecondAdjustment)
        }

        @Deprecated(
            "Use fromSecondOfUnixEpoch() instead.",
            ReplaceWith("Instant.fromSecondOfUnixEpoch(second, nanosecondAdjustment)"),
            DeprecationLevel.ERROR
        )
        fun fromUnixEpochSecond(second: Long, nanosecondAdjustment: Long): Instant {
            return fromSecondOfUnixEpoch(second, nanosecondAdjustment)
        }

        @Deprecated(
            "Use fromMillisecondOfUnixEpoch() instead.",
            ReplaceWith("Instant.fromMillisecondOfUnixEpoch(millisecond)"),
            DeprecationLevel.ERROR
        )
        fun fromUnixEpochMillisecond(millisecond: Long): Instant {
            return fromMillisecondOfUnixEpoch(millisecond)
        }
    }
}

/**
 * Creates the [Instant] represented by a number of seconds relative to the Unix epoch of 1970-01-01T00:00Z.
 */
fun Instant(secondsSinceUnixEpoch: LongSeconds) = Instant.fromSecondOfUnixEpoch(secondsSinceUnixEpoch.value)

/**
 * Creates the [Instant] represented by a number of seconds and additional nanoseconds relative to the Unix epoch of
 * 1970-01-01T00:00Z.
 */
fun Instant(secondsSinceUnixEpoch: LongSeconds, nanosecondAdjustment: IntNanoseconds): Instant {
    return Instant.fromSecondOfUnixEpoch(secondsSinceUnixEpoch.value, nanosecondAdjustment.value)
}

/**
 * Creates the [Instant] represented by a number of seconds and additional nanoseconds relative to the Unix epoch of
 * 1970-01-01T00:00Z.
 */
fun Instant(secondsSinceUnixEpoch: LongSeconds, nanosecondAdjustment: LongNanoseconds): Instant {
    return Instant.fromSecondOfUnixEpoch(secondsSinceUnixEpoch.value, nanosecondAdjustment.value)
}

/**
 * Creates the [Instant] represented by a number of milliseconds relative to the Unix epoch of 1970-01-01T00:00Z.
 */
fun Instant(millisecondsSinceUnixEpoch: LongMilliseconds): Instant {
    return Instant.fromMillisecondOfUnixEpoch(millisecondsSinceUnixEpoch.value)
}

/**
 * Converts a string to an [Instant].
 *
 * The string is assumed to be an ISO-8601 UTC date-time representation in extended format. For example,
 * `2010-10-05T18:30Z` or `2010-10-05T18:30:00.123456789Z`. The output of [Instant.toString] can be safely parsed
 * using this method.
 *
 * @throws DateTimeParseException if parsing fails
 * @throws DateTimeException if the parsed date or time is invalid
 */
fun String.toInstant() = toInstant(DateTimeParsers.Iso.Extended.INSTANT)

/**
 * Converts a string to an [Instant] using a specific parser.
 *
 * A set of predefined parsers can be found in [DateTimeParsers].
 *
 * @throws DateTimeParseException if parsing fails
 * @throws DateTimeException if the parsed date or time is invalid
 */
fun String.toInstant(
    parser: DateTimeParser,
    settings: DateTimeParserSettings = DateTimeParserSettings.DEFAULT
): Instant {
    val result = parser.parse(this, settings)
    return result.toInstant() ?: throwParserFieldResolutionException(this)
}

private const val SECONDS_PER_10000_YEARS = 146097L * 25L * 86400L

internal fun DateTimeParseResult.toInstant(): Instant? {
    // FIXME: Require the year field here for now and make it fit within DateTime's supported range
    val parsedYear = fields[DateTimeField.YEAR] ?: return null

    fields[DateTimeField.YEAR] = parsedYear % 10_000
    val dateTime = this.toDateTime()
    val offset = this.toUtcOffset()

    // Restore the original parsed year
    fields[DateTimeField.YEAR] = parsedYear

    return if (dateTime != null && offset != null) {
        val secondOfEpoch = dateTime.secondOfUnixEpochAt(offset) +
            ((parsedYear / 10_000L) timesExact SECONDS_PER_10000_YEARS)
        Instant.fromSecondOfUnixEpoch(secondOfEpoch, dateTime.nanosecond)
    } else {
        null
    }
}

internal const val MAX_INSTANT_STRING_LENGTH = MAX_DATE_TIME_STRING_LENGTH + 1

internal fun StringBuilder.appendInstant(instant: Instant): StringBuilder {
    val secondOfUnixEpoch = instant.secondOfUnixEpoch
    val nanosecond = instant.nanosecond

    return withComponentizedSecondOfUnixEpoch(secondOfUnixEpoch) { year, monthNumber, day, hour, minute, second ->
        appendDate(year, monthNumber, day)
        append('T')
        appendTime(hour, minute, second, nanosecond)
        append('Z')
    }
}

private inline fun  withComponentizedSecondOfUnixEpoch(
    secondOfUnixEpoch: Long,
    block: (year: Int, monthNumber: Int, dayOfMonth: Int, hour: Int, minute: Int, second: Int) -> T
): T {
    val dayOfUnixEpoch = secondOfUnixEpoch floorDiv SECONDS_PER_DAY
    val secondsSinceStartOfDay = (secondOfUnixEpoch floorMod SECONDS_PER_DAY).toInt().seconds

    return withComponentizedDayOfUnixEpoch(dayOfUnixEpoch) { year, monthNumber, dayOfMonth ->
        secondsSinceStartOfDay.toComponents { hours, minutes, seconds ->
            block(year, monthNumber, dayOfMonth, hours.value, minutes.value, seconds.value)
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy