Kotlinで日時を扱う場合、単に「今の日時を取得する」だけでは不十分です。
タイムゾーン、DB保存、API通信、表示フォーマットなど、用途ごとに適切な型を選ばなければ、後から修正困難な不具合を生みます。
本記事では、Kotlinにおける日時処理を「どの型を、どの目的で使うべきか」 という観点から、体系的に解説します。
Kotlinでの日時処理:結論から
2026年現在、Kotlinでの日時処理は以下の方針がベストプラクティスです。
- 基本は
java.timeAPI - Kotlin Multiplatform(KMP)では
kotlinx-datetime java.util.Date/Calendarは互換用途に限定
この前提を理解しておくと、設計で迷うことがほぼなくなります。
なぜ java.util.Date は推奨されないのか
val date = Date()
一見シンプルですが、java.util.Date には以下の問題があります。
- 可変オブジェクトである(スレッドセーフでない)
- API設計が直感的でない
- タイムゾーンの扱いが曖昧
- Java 8以降、公式には
java.timeへの移行が推奨されている
重要なのは、「使えない」わけではないという点です。
外部ライブラリや古いAPIとの互換性のために Date を扱う場面は今でも存在します。
ただし、新規設計や内部ロジックでは、
Dateを直接扱わず、Instantに変換してから処理する
というスタンスが安全です。
Kotlinで最も標準的な日時API:java.time
Java 8以降、日時処理の中心は java.time パッケージです。
KotlinはJavaと完全互換なので、そのまま利用できます。
主なクラスと役割
| クラス | 役割 |
|---|---|
LocalDate | 日付のみ(誕生日・締日) |
LocalTime | 時刻のみ(営業時間) |
LocalDateTime | 日付+時刻(タイムゾーンなし) |
ZonedDateTime | タイムゾーン付き日時 |
Instant | UTC基準の瞬間 |
日付のみを扱う:LocalDate
import java.time.LocalDate
val today = LocalDate.now()
val specificDate = LocalDate.of(2026, 2, 17)
- 誕生日
- 締日
- カレンダー上の日付
といった 「時刻やタイムゾーンが不要な情報」 に最適です。
時刻のみを扱う:LocalTime
import java.time.LocalTime
val now = LocalTime.now()
val openTime = LocalTime.of(9, 0)
- 営業開始時刻
- アラーム時刻
など、日付と独立した時間情報に使います。
日付+時刻:LocalDateTime(注意点あり)
import java.time.LocalDateTime
val now = LocalDateTime.now()
LocalDateTime は タイムゾーンを持ちません。
そのため、
- DBに「その地域のローカル日時」を保存したい場合
- 日本国内限定サービス
などでは有効ですが、
- 多地域展開
- サマータイム対応
が将来的に必要になる場合は、設計時点で慎重に検討する必要があります。
タイムゾーンを含める:ZonedDateTime
import java.time.ZonedDateTime
import java.time.ZoneId
val tokyoTime = ZonedDateTime.now(ZoneId.of("Asia/Tokyo"))
val utcTime = ZonedDateTime.now(ZoneId.of("UTC"))
- 国際サービス
- 予約・イベント管理
- 表示用の「現地時刻」
では必須となるクラスです。
内部基準の最適解:Instant
import java.time.Instant
val now = Instant.now()
Instant は「UTC基準の一点」を表します。
Instant が強い理由
- タイムゾーン差異を完全に排除できる
- API通信・ログ・イベント時刻に最適
Dateと相互変換しやすい
多くのシステムでは、
内部処理・保存:
Instant
表示・業務処理:ZonedDateTime/LocalDateTime
という役割分担が安定します。
フォーマット・パース(文字列変換)
日時 → 文字列
import java.time.format.DateTimeFormatter
val formatter = DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm")
val text = LocalDateTime.now().format(formatter)
文字列 → 日時
val parsed = LocalDateTime.parse(
"2026/02/17 16:00",
formatter
)
日本向けUIでは yyyy/MM/dd 形式が一般的です。
日付計算・差分処理(重要)
日付の加減算
val tomorrow = LocalDate.now().plusDays(1)
val lastMonth = LocalDate.now().minusMonths(1)
日付差分(総日数)
Period.days は「日数部分」なので、総日数を求める用途には向きません。
import java.time.temporal.ChronoUnit
val start = LocalDate.of(2026, 1, 1)
val end = LocalDate.of(2026, 2, 17)
val daysBetween = ChronoUnit.DAYS.between(start, end)
Kotlin Multiplatform向け:kotlinx-datetime
KMPでは java.time が使えないため、kotlinx-datetime が標準選択肢になります。
import kotlinx.datetime.*
val now = Clock.System.now()
val local = now.toLocalDateTime(TimeZone.currentSystemDefault())
注意点
- フォーマットAPIが最小限
- 表示用は別ライブラリや自前処理が必要な場合が多い
実務での代表的な設計パターン
| 用途 | 推奨型 |
|---|---|
| 内部基準・ログ | Instant |
| DB保存(瞬間) | Instant |
| DB保存(地域日時) | LocalDateTime |
| API通信 | ISO-8601 + Instant |
| 画面表示 | ZonedDateTime |
| 誕生日・締日 | LocalDate |
Androidでの注意点
AndroidではAPIレベルによって java.time がそのまま使えない場合がありますが、Core Library Desugaring を有効にすれば、多くの環境で問題なく利用可能です。
まとめ
- 新規開発では
java.timeを第一選択 - 内部基準は
Instant - タイムゾーンを扱う設計を避けない
Dateは互換用途に限定- KMPでは
kotlinx-datetime
この設計を守れば、「後から壊れない日時処理」 を実現できます。
以上、KotlinでDateやTimeを扱う方法についてでした。
最後までお読みいただき、ありがとうございました。










