Skip to content

時間測量

Kotlin 標準函式庫提供您用於計算和測量不同時間單位的功能。 精確的時間測量對於以下活動很重要:

  • 管理執行緒或處理程序
  • 收集統計資料
  • 偵測逾時
  • 偵錯

依預設,時間是使用單調時間來源測量的,但也可以設定其他時間來源。 有關更多資訊,請參閱建立時間來源

計算時長

為了表示一段時間,標準函式庫提供了 Duration 類別。Duration 可以用來自 DurationUnit 列舉類別的以下單位來表示:

  • NANOSECONDS (奈秒)
  • MICROSECONDS (微秒)
  • MILLISECONDS (毫秒)
  • SECONDS (秒)
  • MINUTES (分)
  • HOURS (小時)
  • DAYS (天)

Duration 可以是正數、負數、零、正無限大或負無限大。

建立時長

若要建立 Duration,請使用適用於 IntLongDouble 類型的擴充屬性nanosecondsmicrosecondsmillisecondssecondsminuteshoursdays

TIP

天數指的是 24 小時的期間。它們不是日曆上的日期。

例如:

kotlin
import kotlin.time.*
import kotlin.time.Duration.Companion.nanoseconds
import kotlin.time.Duration.Companion.milliseconds
import kotlin.time.Duration.Companion.seconds
import kotlin.time.Duration.Companion.minutes
import kotlin.time.Duration.Companion.days

fun main() {
    val fiveHundredMilliseconds: Duration = 500.milliseconds
    val zeroSeconds: Duration = 0.seconds
    val tenMinutes: Duration = 10.minutes
    val negativeNanosecond: Duration = (-1).nanoseconds
    val infiniteDays: Duration = Double.POSITIVE_INFINITY.days
    val negativeInfiniteDays: Duration = Double.NEGATIVE_INFINITY.days

    println(fiveHundredMilliseconds) // 500ms
    println(zeroSeconds)             // 0s
    println(tenMinutes)              // 10m
    println(negativeNanosecond)      // -1ns
    println(infiniteDays)            // Infinity
    println(negativeInfiniteDays)    // -Infinity
}

您還可以對 Duration 物件執行基本算術運算:

kotlin
import kotlin.time.*
import kotlin.time.Duration.Companion.seconds

fun main() {
    val fiveSeconds: Duration = 5.seconds
    val thirtySeconds: Duration = 30.seconds

    println(fiveSeconds + thirtySeconds)
    // 35s
    println(thirtySeconds - fiveSeconds)
    // 25s
    println(fiveSeconds * 2)
    // 10s
    println(thirtySeconds / 2)
    // 15s
    println(thirtySeconds / fiveSeconds)
    // 6.0
    println(-thirtySeconds)
    // -30s
    println((-thirtySeconds).absoluteValue)
    // 30s
}

取得字串表示

擁有 Duration 的字串表示形式會很有用,這樣您就可以列印、序列化、傳輸或儲存它。

若要取得字串表示,請使用 .toString() 函數。依預設,時間會使用每個存在的單位來報告。例如:1h 0m 45.677s-(6d 5h 5m 28.284s)

若要配置輸出,請使用 .toString() 函數,並將所需的 DurationUnit 和小數位數作為函數參數:

kotlin
import kotlin.time.Duration
import kotlin.time.Duration.Companion.milliseconds
import kotlin.time.DurationUnit

fun main() {
    // Print in seconds with 2 decimal places
    println(5887.milliseconds.toString(DurationUnit.SECONDS, 2))
    // 5.89s
}

若要取得 ISO-8601 相容的字串,請使用 toIsoString() 函數:

kotlin
import kotlin.time.Duration.Companion.seconds

fun main() {
    println(86420.seconds.toIsoString()) // PT24H0M20S
}

轉換時長

若要將 Duration 轉換為不同的 DurationUnit,請使用以下屬性:

  • inWholeNanoseconds
  • inWholeMicroseconds
  • inWholeSeconds
  • inWholeMinutes
  • inWholeHours
  • inWholeDays

例如:

kotlin
import kotlin.time.Duration
import kotlin.time.Duration.Companion.minutes

fun main() {
    val thirtyMinutes: Duration = 30.minutes
    println(thirtyMinutes.inWholeSeconds)
    // 1800
}

或者,您可以將所需的 DurationUnit 作為函數參數,用於以下擴充函數:

  • .toInt()
  • .toDouble()
  • .toLong()

例如:

kotlin
import kotlin.time.Duration.Companion.seconds
import kotlin.time.DurationUnit

fun main() {
    println(270.seconds.toDouble(DurationUnit.MINUTES))
    // 4.5
}

比較時長

若要檢查 Duration 物件是否相等,請使用相等運算子 (==):

kotlin
import kotlin.time.Duration
import kotlin.time.Duration.Companion.hours
import kotlin.time.Duration.Companion.minutes

fun main() {
    val thirtyMinutes: Duration = 30.minutes
    val halfHour: Duration = 0.5.hours
    println(thirtyMinutes == halfHour)
    // true
}

若要比較 Duration 物件,請使用比較運算子 (<, >):

kotlin
import kotlin.time.Duration.Companion.microseconds
import kotlin.time.Duration.Companion.nanoseconds

fun main() {
    println(3000.microseconds < 25000.nanoseconds)
    // false
}

將時長分解為元件

若要將 Duration 分解為其時間元件並執行進一步操作,請使用 toComponents() 函數的重載版本。將您想要的操作作為函數或 Lambda 表達式添加到函數參數中。

例如:

kotlin
import kotlin.time.Duration
import kotlin.time.Duration.Companion.minutes

fun main() {
    val thirtyMinutes: Duration = 30.minutes
    println(thirtyMinutes.toComponents { hours, minutes, _, _ -> "${hours}h:${minutes}m" })
    // 0h:30m
}

在此範例中,Lambda 表達式將 hoursminutes 作為函數參數,並使用底線 (_) 表示未使用的 secondsnanoseconds 參數。該表達式使用字串模板返回一個串聯的字串,以獲取 hoursminutes 的所需輸出格式。

測量時間

為了追蹤時間的流逝,標準函式庫提供了工具,讓您可以輕鬆地:

  • 使用所需的時間單位測量執行某些程式碼所需的時間。
  • 標記時間中的一個時刻。
  • 比較和減去時間中的兩個時刻。
  • 檢查自特定時刻以來過了多少時間。
  • 檢查當前時間是否已超過特定時刻。

測量程式碼執行時間

若要測量執行程式碼區塊所需的時間,請使用 measureTime 內聯函數:

kotlin
import kotlin.time.measureTime

fun main() {
    val timeTaken = measureTime {
        Thread.sleep(100)
    }
    println(timeTaken) // e.g. 103 ms
}

若要測量執行程式碼區塊所需的時間返回該程式碼區塊的值,請使用內聯函數 measureTimedValue

例如:

kotlin
import kotlin.time.measureTimedValue

fun main() {
    val (value, timeTaken) = measureTimedValue {
        Thread.sleep(100)
        42
    }
    println(value)     // 42
    println(timeTaken) // e.g. 103 ms
}

依預設,這兩個函數都使用單調時間來源。

標記時間中的時刻

若要標記時間中的特定時刻,請使用 TimeSource 介面和 markNow() 函數來建立一個 TimeMark

kotlin
import kotlin.time.*

fun main() {
   val timeSource = TimeSource.Monotonic
   val mark = timeSource.markNow()
}

測量時間差異

若要測量來自同一時間來源的 TimeMark 物件之間的差異,請使用減法運算子 (-)。

若要比較來自同一時間來源的 TimeMark 物件,請使用比較運算子 (<>)。

例如:

kotlin
import kotlin.time.*

fun main() {
   val timeSource = TimeSource.Monotonic
   val mark1 = timeSource.markNow()
   Thread.sleep(500) // Sleep 0.5 seconds.
   val mark2 = timeSource.markNow()

   repeat(4) { n ->
       val mark3 = timeSource.markNow()
       val elapsed1 = mark3 - mark1
       val elapsed2 = mark3 - mark2

       println("Measurement 1.${n + 1}: elapsed1=$elapsed1, elapsed2=$elapsed2, diff=${elapsed1 - elapsed2}")
   }

   println(mark2 > mark1) // This is true, as mark2 was captured later than mark1.
   // true
}

若要檢查截止日期是否已過或是否已達到逾時,請使用 hasPassedNow()hasNotPassedNow() 擴充函數:

kotlin
import kotlin.time.*
import kotlin.time.Duration.Companion.seconds

fun main() {
   val timeSource = TimeSource.Monotonic
   val mark1 = timeSource.markNow()
   val fiveSeconds: Duration = 5.seconds
   val mark2 = mark1 + fiveSeconds

   // It hasn't been 5 seconds yet
   println(mark2.hasPassedNow())
   // false

   // Wait six seconds
   Thread.sleep(6000)
   println(mark2.hasPassedNow())
   // true

}

時間來源

依預設,時間是使用單調時間來源測量的。單調時間來源只會向前移動,不受時區等變化影響。單調時間的替代方案是實際經過時間 (elapsed real time),也稱為牆上時鐘時間 (wall-clock time)。實際經過時間是相對於時間中的另一個點來測量的。

每個平台的預設時間來源

下表說明每個平台的預設單調時間來源:

平台來源
Kotlin/JVMSystem.nanoTime()
Kotlin/JS (Node.js)process.hrtime()
Kotlin/JS (browser)window.performance.now() or Date.now()
Kotlin/Nativestd::chrono::high_resolution_clock or std::chrono::steady_clock

建立時間來源

在某些情況下,您可能想要使用不同的時間來源。例如,在 Android 中,System.nanoTime() 只計算裝置處於活動狀態時的時間。當裝置進入深度睡眠時,它會失去對時間的追蹤。為了在裝置處於深度睡眠時仍能追蹤時間,您可以建立一個使用 SystemClock.elapsedRealtimeNanos() 的時間來源:

kotlin
object RealtimeMonotonicTimeSource : AbstractLongTimeSource(DurationUnit.NANOSECONDS) {
    override fun read(): Long = SystemClock.elapsedRealtimeNanos()
}

然後您可以使用您的時間來源進行時間測量:

kotlin
fun main() {
    val elapsed: Duration = RealtimeMonotonicTimeSource.measureTime {
        Thread.sleep(100)
    }
    println(elapsed) // e.g. 103 ms
}

有關 kotlin.time 套件的更多資訊,請參閱我們的標準函式庫 API 參考