Skip to content

時間測定

Kotlin の標準ライブラリは、さまざまな単位で時間を計算および測定するためのツールを提供します。 正確な時間測定は、次のような活動にとって重要です:

  • スレッドやプロセスの管理
  • 統計の収集
  • タイムアウトの検出
  • デバッグ

デフォルトでは、時間は単調な時間ソースを使用して測定されますが、他の時間ソースも設定できます。 詳細については、「時間ソースを作成する」を参照してください。

期間を計算する

時間の量を表すために、標準ライブラリは Duration クラスを提供します。Duration は、DurationUnit enum クラスの次の単位で表現できます:

  • 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)

出力を設定するには、目的の DurationUnit と小数点以下の桁数を関数パラメーターとして使用して、.toString() 関数を使用します:

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() 関数のオーバーロードを使用します。目的のアクションを関数またはラムダ式として関数パラメーターに追加します。

例:

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
}

この例では、ラムダ式は hoursminutes を関数パラメーターとして持ち、未使用の secondsnanoseconds パラメーターにはアンダースコア (_) を使用しています。この式は、文字列テンプレートを使用して連結された文字列を返し、hoursminutes の目的の出力形式を取得します。

時間を測定する

時間の経過を追跡するために、標準ライブラリは、簡単に次のことができるツールを提供します:

  • 目的の時間単位でコードの実行にかかった時間を測定する。
  • ある時点をマークする。
  • 2つの時点を比較し、減算する。
  • 特定の時点からどれくらいの時間が経過したかを確認する。
  • 現在時刻が特定の時点を過ぎたかどうかを確認する。

コード実行時間を測定する

コードブロックの実行にかかる時間を測定するには、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) // 0.5秒スリープ。
   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) // mark2がmark1より後にキャプチャされたため、これはtrueです。
   // 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

   // まだ5秒経過していません
   println(mark2.hasPassedNow())
   // false
  
   // 6秒待機する
   Thread.sleep(6000)
   println(mark2.hasPassedNow())
   // true

}

時間ソース

デフォルトでは、時間は単調な時間ソースを使用して測定されます。単調な時間ソースは前方のみに進み、タイムゾーンのような変動の影響を受けません。単調な時間に対する代替手段は、経過実時間(ウォールクロック時間とも呼ばれる)です。経過実時間は、別の時点を基準として測定されます。

プラットフォームごとのデフォルトの時間ソース

この表は、各プラットフォームにおける単調な時間のデフォルトソースを説明します:

プラットフォームソース
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リファレンスをご覧ください。