- Kotlin
Kotlin 日付と時刻の扱い方 LocalDateのフォーマットと差分計算
Kotlin(JVM)で日付と時刻を扱うときは、Java 標準の java.time パッケージの LocalDate・LocalDateTime・ZonedDateTime を使います。追加のライブラリは不要で、LocalDate.now() で今日の日付、format(DateTimeFormatter.ofPattern("yyyy/MM/dd")) で文字列、ChronoUnit.DAYS.between(a, b) で日数の差が得られます。
Kotlin から使うと、a < b のような演算子での比較、date in start..end の範囲判定、date + Period.ofDays(3) の加算、拡張関数など、Java より短く読みやすい書き方ができます。この記事では、取得・フォーマット・文字列からの変換・加減算・差分・タイムゾーンまでを Kotlin の書き方で通し、すべてのコードに実行出力を付けました。yyyy と YYYY、HH と hh の取り違えで出力がずれる様子、DateTimeParseException の実際のメッセージ、月末の加算結果も再現しています。
確認環境:Ubuntu 24.04、Kotlin 2.4.20(kotlinc-jvm)、OpenJDK 21.0.10、既定のタイムゾーンは Asia/Tokyo。kotlinc a.kt -include-runtime -d a.jar でコンパイルし、java -Dstdout.encoding=UTF-8 -jar a.jar で実行しています(-Dstdout.encoding=UTF-8 は日本語の出力を UTF-8 にそろえる指定です)。
Kotlin 日付と時刻の早見表
どの型を使うか
| 型 | 持つ情報 | 例(toString()) |
使いどころ |
|---|---|---|---|
LocalDate |
年月日 | 2026-10-05 |
誕生日、締め切り日、予定日 |
LocalTime |
時分秒 | 09:07 |
開始時刻、受付時間 |
LocalDateTime |
年月日+時分秒(ゾーンなし) | 2026-10-05T09:07:03 |
1 つの地域の中だけで使う日時 |
ZonedDateTime |
日時+タイムゾーン | 2026-10-05T09:00+09:00[Asia/Tokyo] |
地域をまたぐ日時、夏時間の考慮 |
OffsetDateTime |
日時+UTC との差 | 2026-10-05T09:07:03+09:00 |
API やログの ISO 形式 |
Instant |
UTC の瞬間 | 2026-10-05T00:00:00Z |
記録時刻、エポックミリ秒との変換 |
Period |
年・月・日の長さ | P8M5D |
「8 か月と 5 日」のような暦の差 |
java.time.Duration |
秒・ナノ秒の長さ | PT57H45M30S |
経過時間、時刻の加減算 |
kotlin.time.Duration |
Kotlin の時間の長さ | 1h 30m |
90.minutes と書ける。処理時間の計測 |
やりたいこと別の書き方
| やりたいこと | 書き方 | 結果の例 |
|---|---|---|
| 今日の日付 | LocalDate.now() |
2026-10-05 |
| 現在の日時 | LocalDateTime.now() |
2026-10-05T15:57:04.163982556 |
| 値を指定して作る | LocalDate.of(2026, 10, 5) |
2026-10-05 |
| 文字列にする | dt.format(DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss")) |
2026/10/05 15:07:03 |
| 文字列から作る | LocalDate.parse("2026/10/05", DateTimeFormatter.ofPattern("yyyy/MM/dd")) |
2026-10-05 |
| 日数を足す・引く | d.plusDays(30)、d - Period.ofMonths(1) |
2026-11-04、2026-09-05 |
| 月末にする | d.with(TemporalAdjusters.lastDayOfMonth()) |
2026-10-31 |
| 前後を比べる | a < b、a == b |
true |
| 期間に入っているか | d in start..end |
true |
| 日数の差 | ChronoUnit.DAYS.between(a, b) |
247 |
| 年・月・日に分けた差 | Period.between(a, b) |
P8M5D |
| 時間の差 | Duration.between(from, to) |
PT57H45M30S |
| 別のタイムゾーンに直す | zdt.withZoneSameInstant(ZoneId.of("America/New_York")) |
同じ瞬間の現地時刻 |
| 処理時間を測る | measureTime { ... } |
kotlin.time.Duration |
本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。
(記載例)出典:株式会社RJC「Kotlin 日付と時刻の扱い方 LocalDateのフォーマットと差分計算」
現在日時を取得する・値を指定して作る
import java.time.*
fun main() {
val tokyo = ZoneId.of("Asia/Tokyo")
println(LocalDate.now(tokyo)) // 日付だけ
println(LocalTime.now(tokyo)) // 時刻だけ
println(LocalDateTime.now(tokyo)) // 日付と時刻(ゾーン情報なし)
println(ZonedDateTime.now(tokyo)) // ゾーンつき
println(OffsetDateTime.now(tokyo)) // UTC との差つき
println(Instant.now()) // UTC の瞬間
println(System.currentTimeMillis()) // エポックミリ秒
println(ZoneId.systemDefault()) // 実行環境の既定ゾーン
// 値を指定して作る
val date = LocalDate.of(2026, 10, 5)
val dateTime = LocalDateTime.of(2026, 10, 5, 9, 7, 3)
println("$date ${date.dayOfWeek} ${date.dayOfYear} ${date.lengthOfMonth()} ${date.isLeapYear}")
println("${dateTime.year}/${dateTime.monthValue}/${dateTime.dayOfMonth} ${dateTime.hour}:${dateTime.minute}:${dateTime.second}")
// テストしやすいように「現在」を固定する
val fixed = Clock.fixed(Instant.parse("2026-10-05T00:00:00Z"), tokyo)
println(LocalDateTime.now(fixed))
}
実行結果(2026 年 10 月 5 日 15 時 57 分に実行):
2026-10-05
15:57:04.163906946
2026-10-05T15:57:04.163982556
2026-10-05T15:57:04.164472425+09:00[Asia/Tokyo]
2026-10-05T15:57:04.164751825+09:00
2026-10-05T06:57:04.164854695Z
1791183424167
Asia/Tokyo
2026-10-05 MONDAY 278 31 false
2026/10/5 9:7:3
2026-10-05T09:00
now()にZoneIdを渡すと、実行するマシンの設定に左右されずに同じ地域の時刻が得られます。 引数なしのnow()はZoneId.systemDefault()のゾーンを使います。Instant.now()は UTC で表示されるので、日本時間より 9 時間前の06:57になっています。- 最後の行は、
Clock.fixedで「現在」を UTC の 0 時に固定し、東京のゾーンで読んだ結果(9 時)です。now(clock)の形にしておくと、日時に依存する処理のテストがしやすくなります。 - プロパティは
date.dayOfWeek・dateTime.monthValueのように、Java のgetDayOfWeek()・getMonthValue()をgetなしで書けます。
フォーマット:DateTimeFormatter のパターン文字早見表
日時を文字列にするには、DateTimeFormatter.ofPattern("パターン") を作って format に渡します。曜日や午前・午後のような言葉を含むパターンは、Locale を指定すると表記が決まります。
import java.time.*
import java.time.format.DateTimeFormatter
import java.util.Locale
fun main() {
val dt = ZonedDateTime.of(2026, 10, 5, 15, 7, 3, 123_456_789, ZoneId.of("Asia/Tokyo"))
val patterns = listOf(
"yyyy-MM-dd", "yyyy/MM/dd HH:mm:ss", "yyyy年M月d日(E)", "yyyy年M月d日 EEEE",
"yy/M/d", "MMM d, yyyy", "MMMM", "HH:mm", "H:m:s", "hh:mm a", "a h時mm分",
"HH:mm:ss.SSS", "HH:mm:ss.SSSSSS", "yyyyMMddHHmmss", "D", "e", "w",
"yyyy-MM-dd'T'HH:mm:ssXXX", "xxx", "XX", "Z", "VV", "z", "zzzz", "O", "G yyyy", "QQQ",
)
for (p in patterns) {
val ja = dt.format(DateTimeFormatter.ofPattern(p, Locale.JAPAN))
val en = dt.format(DateTimeFormatter.ofPattern(p, Locale.ENGLISH))
println(if (ja == en) "$p => $ja" else "$p => $ja | $en")
}
println("--- 定義済み")
println(dt.format(DateTimeFormatter.ISO_LOCAL_DATE))
println(dt.format(DateTimeFormatter.ISO_LOCAL_DATE_TIME))
println(dt.format(DateTimeFormatter.ISO_OFFSET_DATE_TIME))
println(dt.format(DateTimeFormatter.BASIC_ISO_DATE))
println(dt.toLocalDate()) // toString は ISO 形式
println(dt.toLocalDateTime())
}
実行結果(| の左が Locale.JAPAN、右が Locale.ENGLISH。同じ場合は 1 つだけ表示):
yyyy-MM-dd => 2026-10-05
yyyy/MM/dd HH:mm:ss => 2026/10/05 15:07:03
yyyy年M月d日(E) => 2026年10月5日(月) | 2026年10月5日(Mon)
yyyy年M月d日 EEEE => 2026年10月5日 月曜日 | 2026年10月5日 Monday
yy/M/d => 26/10/5
MMM d, yyyy => 10月 5, 2026 | Oct 5, 2026
MMMM => 10月 | October
HH:mm => 15:07
H:m:s => 15:7:3
hh:mm a => 03:07 午後 | 03:07 PM
a h時mm分 => 午後 3時07分 | PM 3時07分
HH:mm:ss.SSS => 15:07:03.123
HH:mm:ss.SSSSSS => 15:07:03.123456
yyyyMMddHHmmss => 20261005150703
D => 278
e => 2
w => 41
yyyy-MM-dd'T'HH:mm:ssXXX => 2026-10-05T15:07:03+09:00
xxx => +09:00
XX => +0900
Z => +0900
VV => Asia/Tokyo
z => JST
zzzz => 日本標準時 | Japan Standard Time
O => GMT+9
G yyyy => 西暦 2026 | AD 2026
QQQ => Q4
--- 定義済み
2026-10-05
2026-10-05T15:07:03.123456789
2026-10-05T15:07:03.123456789+09:00
20261005+0900
2026-10-05
2026-10-05T15:07:03.123456789
パターン文字の一覧
対象の日時は 2026-10-05 15:07:03.123456789(月曜日、Asia/Tokyo)です。出力はすべて上の実行結果から取っています。
| 文字 | 意味 | パターン例 | 出力(JAPAN) | 出力(ENGLISH) |
|---|---|---|---|---|
y |
年 | yyyy / yy |
2026 / 26 |
同じ |
M |
月 | MM / M |
10 / 10 |
同じ |
M(3 文字以上) |
月の名前 | MMM / MMMM |
10月 / 10月 |
Oct / October |
d |
日 | dd / d |
05 / 5 |
同じ |
D |
年の通算日 | D |
278 |
同じ |
E |
曜日 | E / EEEE |
月 / 月曜日 |
Mon / Monday |
e |
曜日の番号(ロケールの週の始まりから数える) | e |
2 |
2 |
w |
年の中の週番号 | w |
41 |
41 |
a |
午前・午後 | a |
午後 |
PM |
H |
時(0〜23) | HH / H |
15 / 15 |
同じ |
h |
時(1〜12) | hh / h |
03 / 3 |
同じ |
m |
分 | mm / m |
07 / 7 |
同じ |
s |
秒 | ss / s |
03 / 3 |
同じ |
S |
秒の小数部 | SSS / SSSSSS |
123 / 123456 |
同じ |
XXX |
UTC との差(0 のときは Z) | XXX / XX |
+09:00 / +0900 |
同じ |
xxx |
UTC との差(0 のときも数字) | xxx |
+09:00 |
同じ |
Z |
UTC との差 | Z |
+0900 |
同じ |
O |
GMT からの差 | O |
GMT+9 |
同じ |
VV |
タイムゾーン ID | VV |
Asia/Tokyo |
同じ |
z |
タイムゾーン名 | z / zzzz |
JST / 日本標準時 |
JST / Japan Standard Time |
G |
紀元 | G |
西暦 |
AD |
Q |
四半期 | QQQ |
Q4 |
同じ |
'...' |
文字をそのまま出す | 'T' |
T |
同じ |
文字数で桁の埋め方が変わります。 MM・dd・HH は 2 桁に 0 で埋め、M・d・H は埋めません(H:m:s の出力が 15:7:3)。T のようなアルファベットをそのまま出したいときは、'T' とシングルクォートで囲みます。年・月・日 や /・-・: などは、囲まずに書けます。
XXX・VV・z などゾーンに関する文字は、この例の ZonedDateTime のようにゾーンを持つ型で使います。
よく使う書式の組み合わせ
| 用途 | パターン | 出力 |
|---|---|---|
| ISO 形式の日付 | yyyy-MM-dd |
2026-10-05 |
| スラッシュ区切りの日時 | yyyy/MM/dd HH:mm:ss |
2026/10/05 15:07:03 |
| 日本語の日付と曜日 | yyyy年M月d日(E) |
2026年10月5日(月) |
| ファイル名用の連番 | yyyyMMddHHmmss |
20261005150703 |
| ミリ秒つきの時刻 | HH:mm:ss.SSS |
15:07:03.123 |
| 午前・午後つき | a h時mm分 |
午後 3時07分 |
| UTC との差つきの ISO 形式 | yyyy-MM-dd'T'HH:mm:ssXXX |
2026-10-05T15:07:03+09:00 |
toString() はそのまま ISO 形式(2026-10-05、2026-10-05T15:07:03.123456789)を返すので、ISO 形式でよければフォーマッタは不要です。
パターン文字の取り違えで出力がずれる例
パターン文字は大文字と小文字で意味が変わります。取り違えてもエラーにならず、ある日付・ある時刻のときだけ違う値が出るため、気づきにくいのが特徴です。
import java.time.*
import java.time.format.DateTimeFormatter
import java.util.Locale
fun main() {
val good = DateTimeFormatter.ofPattern("yyyy-MM-dd", Locale.JAPAN)
val weekYear = DateTimeFormatter.ofPattern("YYYY-MM-dd", Locale.JAPAN)
val weekYearIso = DateTimeFormatter.ofPattern("YYYY-MM-dd", Locale.UK)
println("日付 yyyy YYYY(JAPAN) YYYY(UK)")
for (s in listOf("2026-10-05", "2026-12-26", "2026-12-27", "2026-12-31", "2027-01-01", "2027-01-03", "2027-01-04")) {
val d = LocalDate.parse(s)
println("$s ${d.format(good)} ${d.format(weekYear)} ${d.format(weekYearIso)} ${d.dayOfWeek}")
}
println("--- HH と hh")
val h24 = DateTimeFormatter.ofPattern("HH:mm")
val h12 = DateTimeFormatter.ofPattern("hh:mm")
val h12a = DateTimeFormatter.ofPattern("hh:mm a", Locale.ENGLISH)
for (t in listOf(LocalTime.of(0, 5), LocalTime.of(9, 30), LocalTime.of(12, 0), LocalTime.of(15, 30), LocalTime.of(23, 59))) {
println("${t.format(h24)} ${t.format(h12)} ${t.format(h12a)}")
}
println("--- MM と mm、dd と DD")
val dt = LocalDateTime.of(2026, 10, 5, 15, 7, 3)
println(dt.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm")))
println(dt.format(DateTimeFormatter.ofPattern("yyyy-mm-dd HH:MM")))
println(dt.format(DateTimeFormatter.ofPattern("yyyy-MM-DD")))
}
日付 yyyy YYYY(JAPAN) YYYY(UK)
2026-10-05 2026-10-05 2026-10-05 2026-10-05 MONDAY
2026-12-26 2026-12-26 2026-12-26 2026-12-26 SATURDAY
2026-12-27 2026-12-27 2027-12-27 2026-12-27 SUNDAY
2026-12-31 2026-12-31 2027-12-31 2026-12-31 THURSDAY
2027-01-01 2027-01-01 2027-01-01 2026-01-01 FRIDAY
2027-01-03 2027-01-03 2027-01-03 2026-01-03 SUNDAY
2027-01-04 2027-01-04 2027-01-04 2027-01-04 MONDAY
--- HH と hh
00:05 12:05 12:05 AM
09:30 09:30 09:30 AM
12:00 12:00 12:00 PM
15:30 03:30 03:30 PM
23:59 11:59 11:59 PM
--- MM と mm、dd と DD
2026-10-05 15:07
2026-07-05 15:10
2026-10-278
yyyy と YYYY:年末年始だけ年がずれる
YYYY は「その日が属する週の年」(week-based-year)です。Oracle の DateTimeFormatter の API ドキュメントでも、y は year-of-era、Y は week-based-year と別の項目として定義されています。
出力のとおり、10 月 5 日は yyyy と YYYY が同じ値ですが、Locale.JAPAN では 2026 年 12 月 27 日(日曜日)から 31 日までが 2027 と表示されます。日曜始まりの週で数えると、この週は 2027 年の第 1 週にあたるためです。Locale.UK では逆に、2027 年 1 月 1 日〜3 日が 2026 になります。月曜始まりで数えると、この 3 日間は 2026 年の最後の週に属するためです。
| 書きたいもの | 正しい文字 | 取り違えた文字 | 起きること |
|---|---|---|---|
| 年 | yyyy |
YYYY |
年末年始の数日だけ前後の年になる |
| 月 | MM |
mm |
分が入る(2026-07-05 は 7 分のため) |
| 分 | mm |
MM |
月が入る(15:10 は 10 月のため) |
| 日 | dd |
DD |
年の通算日が入る(278) |
| 24 時間表記の時 | HH |
hh |
午後が 1〜11、0 時台が 12 になる |
HH と hh:午後と 0 時台がずれる
HH は 0〜23、hh は 1〜12 です。出力の 2 列目のとおり、hh では 15:30 が 03:30、0:05 が 12:05 になり、午前 9 時 30 分のように午前の時刻では同じ値が出ます。24 時間表記には HH、12 時間表記には hh と a(午前・午後)を組み合わせます。
文字列から日付・時刻に変換する
import java.time.*
import java.time.format.DateTimeFormatter
import java.time.format.DateTimeParseException
// 変換できないときは null を返す拡張関数
fun String.toLocalDateOrNull(pattern: String = "yyyy-MM-dd"): LocalDate? =
try {
LocalDate.parse(this, DateTimeFormatter.ofPattern(pattern))
} catch (e: DateTimeParseException) {
null
}
fun main() {
println(LocalDate.parse("2026-10-05")) // ISO 形式はそのまま
println(LocalTime.parse("09:07"))
println(LocalDateTime.parse("2026-10-05T09:07:03"))
println(ZonedDateTime.parse("2026-10-05T09:07:03+09:00[Asia/Tokyo]"))
println(OffsetDateTime.parse("2026-10-05T09:07:03+09:00"))
println(Instant.parse("2026-10-05T00:07:03Z"))
val slash = DateTimeFormatter.ofPattern("yyyy/MM/dd")
println(LocalDate.parse("2026/10/05", slash))
val loose = DateTimeFormatter.ofPattern("yyyy/M/d H:mm")
println(LocalDateTime.parse("2026/10/5 9:07", loose))
println(LocalDate.parse("20261005", DateTimeFormatter.BASIC_ISO_DATE))
val jp = DateTimeFormatter.ofPattern("yyyy年M月d日")
println(LocalDate.parse("2026年10月5日", jp))
println("2026-10-05".toLocalDateOrNull())
println("2026/10/05".toLocalDateOrNull())
println("2026/10/05".toLocalDateOrNull("yyyy/MM/dd"))
println(runCatching { LocalDate.parse("abc") }.getOrNull())
println("10/05".toLocalDateOrNull() ?: LocalDate.of(2026, 1, 1))
}
2026-10-05
09:07
2026-10-05T09:07:03
2026-10-05T09:07:03+09:00[Asia/Tokyo]
2026-10-05T09:07:03+09:00
2026-10-05T00:07:03Z
2026-10-05
2026-10-05T09:07
2026-10-05
2026-10-05
2026-10-05
null
2026-10-05
null
2026-01-01
- ISO 形式(
2026-10-05、2026-10-05T09:07:03)は、フォーマッタなしのparseで読めます。 - それ以外の形式は、文字列と同じ形のパターンを渡します。
yyyy/M/d H:mmのように 1 文字で書くと、10/5 9:07のような 0 埋めなしの入力を読めます。 - 拡張関数
String.toLocalDateOrNull()のように、変換できないときに null を返す形にしておくと、呼び出し側は?:で既定値を決められます(出力の最後の行)。短く書くならrunCatching { ... }.getOrNull()も使えます。
DateTimeParseException の実物と読み方
変換できない文字列を渡すと DateTimeParseException になります。原因別に実際のメッセージを出しました。
import java.time.*
import java.time.format.DateTimeFormatter
import java.time.format.ResolverStyle
fun show(label: String, block: () -> Any) {
try {
println("$label => ${block()}")
} catch (e: Exception) {
println("$label => ${e.javaClass.name}: ${e.message}")
}
}
fun main() {
show("1 区切りが違う") { LocalDate.parse("2026/10/05") }
show("2 桁が足りない") { LocalDate.parse("2026-10-5") }
show("3 存在しない月") { LocalDate.parse("2026-13-01") }
show("4 存在しない日") { LocalDate.parse("2026-02-30") }
show("5 時刻がない") { LocalDateTime.parse("2026-10-05") }
show("6 余分な文字") { LocalDate.parse("2026-10-05 09:00") }
show("7 時刻つきの型で日付だけの書式") {
LocalDateTime.parse("2026/10/05", DateTimeFormatter.ofPattern("yyyy/MM/dd"))
}
show("8 hh で 15 時") {
LocalTime.parse("15:30", DateTimeFormatter.ofPattern("hh:mm"))
}
show("9 パターンで 2/30") {
LocalDate.parse("2026/02/30", DateTimeFormatter.ofPattern("yyyy/MM/dd"))
}
show("10 STRICT で 2/30") {
val strict = DateTimeFormatter.ofPattern("uuuu/MM/dd").withResolverStyle(ResolverStyle.STRICT)
LocalDate.parse("2026/02/30", strict)
}
show("11 日付にない項目を書式化") {
LocalDate.of(2026, 10, 5).format(DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm"))
}
show("12 使えないパターン文字") { DateTimeFormatter.ofPattern("yyyy-MM-dd T") }
show("13 存在しない日付を作る") { LocalDate.of(2026, 2, 30) }
LocalDate.parse("2026/10/05")
}
1 区切りが違う => java.time.format.DateTimeParseException: Text '2026/10/05' could not be parsed at index 4
2 桁が足りない => java.time.format.DateTimeParseException: Text '2026-10-5' could not be parsed at index 8
3 存在しない月 => java.time.format.DateTimeParseException: Text '2026-13-01' could not be parsed: Invalid value for MonthOfYear (valid values 1 - 12): 13
4 存在しない日 => java.time.format.DateTimeParseException: Text '2026-02-30' could not be parsed: Invalid date 'FEBRUARY 30'
5 時刻がない => java.time.format.DateTimeParseException: Text '2026-10-05' could not be parsed at index 10
6 余分な文字 => java.time.format.DateTimeParseException: Text '2026-10-05 09:00' could not be parsed, unparsed text found at index 10
7 時刻つきの型で日付だけの書式 => java.time.format.DateTimeParseException: Text '2026/10/05' could not be parsed: Unable to obtain LocalDateTime from TemporalAccessor: {},ISO resolved to 2026-10-05 of type java.time.format.Parsed
8 hh で 15 時 => java.time.format.DateTimeParseException: Text '15:30' could not be parsed: Invalid value for ClockHourOfAmPm (valid values 1 - 12): 15
9 パターンで 2/30 => 2026-02-28
10 STRICT で 2/30 => java.time.format.DateTimeParseException: Text '2026/02/30' could not be parsed: Invalid date 'FEBRUARY 30'
11 日付にない項目を書式化 => java.time.temporal.UnsupportedTemporalTypeException: Unsupported field: HourOfDay
12 使えないパターン文字 => java.lang.IllegalArgumentException: Unknown pattern letter: T
13 存在しない日付を作る => java.time.DateTimeException: Invalid date 'FEBRUARY 30'
Exception in thread "main" java.time.format.DateTimeParseException: Text '2026/10/05' could not be parsed at index 4
at java.base/java.time.format.DateTimeFormatter.parseResolved0(DateTimeFormatter.java:2108)
at java.base/java.time.format.DateTimeFormatter.parse(DateTimeFormatter.java:2010)
at java.base/java.time.LocalDate.parse(LocalDate.java:435)
at java.base/java.time.LocalDate.parse(LocalDate.java:420)
at ParseerrKt.main(parseerr.kt:39)
at ParseerrKt.main(parseerr.kt)
| メッセージの形 | 意味 | 直し方 |
|---|---|---|
could not be parsed at index 4 |
先頭から 4 文字目(0 始まり)でパターンと合わなくなった | その位置の区切り文字・桁数をパターンとそろえる |
Invalid value for MonthOfYear (valid values 1 - 12): 13 |
形は合っているが値が範囲外 | 入力の値を確かめる |
Invalid date 'FEBRUARY 30' |
存在しない日付 | 入力の値を確かめる |
unparsed text found at index 10 |
10 文字目以降に読み残しがある | 時刻まで含むパターンにするか、入力を切り出す |
Unable to obtain LocalDateTime from TemporalAccessor |
読めた項目が型に足りない(日付だけで LocalDateTime は作れない) |
LocalDate.parse で読んで atStartOfDay() を使う |
Invalid value for ClockHourOfAmPm (valid values 1 - 12): 15 |
hh で 13 以上の時を読もうとした |
パターンを HH にする |
UnsupportedTemporalTypeException: Unsupported field: HourOfDay |
LocalDate に時刻の書式を使った |
日付だけのパターンにするか LocalDateTime を使う |
IllegalArgumentException: Unknown pattern letter: T |
パターンに使えない文字がある | 'T' と囲む |
at index N の N は 0 から数えた文字の位置です。1 番の 2026/10/05 は 4 文字目の / が、既定のパターンの - と合わなかったことを示しています。
捕まえなかった場合は、出力の最後のように Exception in thread "main" に続いてスタックトレースが表示され、parseerr.kt:39 のように自分のコードの行番号が含まれます。
存在しない日付を丸めずに止める
9 番と 10 番の違いに注目してください。DateTimeFormatter.ofPattern で作ったフォーマッタは、2026/02/30 を 2026-02-28 に丸めて読み込みます。Oracle の API ドキュメントによると、ofPattern が返すフォーマッタは SMART という解決スタイルを使います。
存在しない日付をエラーとして扱いたいときは、10 番のように withResolverStyle(ResolverStyle.STRICT) を付け、年のパターンを uuuu にします。
val strict = DateTimeFormatter.ofPattern("uuuu/MM/dd").withResolverStyle(ResolverStyle.STRICT)
フォーマッタなしの LocalDate.parse("2026-02-30")(4 番)は、最初から例外になります。
日付と時刻を加算・減算する
LocalDate などは不変(イミュータブル)で、plusDays などは元の値を変えずに新しい値を返します。
import java.time.*
import java.time.temporal.ChronoUnit
import java.time.temporal.TemporalAdjusters
fun main() {
val d = LocalDate.of(2026, 10, 5)
println(d.plusDays(30))
println(d.minusWeeks(1))
println(d.plusMonths(3))
println(d.plusYears(1))
println(d.plus(10, ChronoUnit.DAYS))
// Kotlin では plus / minus を演算子で書ける
println(d + Period.ofDays(3))
println(d - Period.ofMonths(1))
println(d + Period.of(1, 2, 3))
val dt = LocalDateTime.of(2026, 10, 5, 23, 30)
println(dt + Duration.ofMinutes(45))
println(dt - Duration.ofHours(24))
println(dt.plusHours(1).plusMinutes(15))
println("--- 月末の加算")
val jan31 = LocalDate.of(2026, 1, 31)
println(jan31.plusMonths(1)) // 2月に31日はない
println(jan31.plusMonths(1).plusMonths(1)) // 28日のまま進む
println(jan31.plusMonths(2)) // 元の日付から2か月
println(LocalDate.of(2026, 3, 31).minusMonths(1))
println(LocalDate.of(2028, 1, 31).plusMonths(1)) // うるう年
println(LocalDate.of(2028, 2, 29).plusYears(1))
for (n in 1..4) print("${jan31.plusMonths(n.toLong())} ")
println()
println("--- 月初・月末・曜日")
println(d.withDayOfMonth(1))
println(d.with(TemporalAdjusters.lastDayOfMonth()))
println(d.with(TemporalAdjusters.firstDayOfNextMonth()))
println(d.with(TemporalAdjusters.next(DayOfWeek.FRIDAY)))
println(d.with(TemporalAdjusters.previousOrSame(DayOfWeek.MONDAY)))
println(YearMonth.of(2026, 2).atEndOfMonth())
println(d.atStartOfDay())
println(d.atTime(18, 0))
println(dt.toLocalDate())
println(dt.truncatedTo(ChronoUnit.HOURS))
}
2026-11-04
2026-09-28
2027-01-05
2027-10-05
2026-10-15
2026-10-08
2026-09-05
2027-12-08
2026-10-06T00:15
2026-10-04T23:30
2026-10-06T00:45
--- 月末の加算
2026-02-28
2026-03-28
2026-03-31
2026-02-28
2028-02-29
2029-02-28
2026-02-28 2026-03-31 2026-04-30 2026-05-31
--- 月初・月末・曜日
2026-10-01
2026-10-31
2026-11-01
2026-10-09
2026-10-05
2026-02-28
2026-10-05T00:00
2026-10-05T18:00
2026-10-05
2026-10-05T23:00
Kotlin では + と - で書ける
LocalDate には plus(TemporalAmount) と minus(TemporalAmount) というメソッドがあり、Kotlin は plus・minus という名前のメソッドを演算子として呼べます。そのため、d + Period.ofDays(3) や dt + Duration.ofMinutes(45) と書けます。
| 足すもの | 書き方 | 対象の型 |
|---|---|---|
| 日・月・年 | d.plusDays(30)、d + Period.ofMonths(1) |
LocalDate・LocalDateTime |
| 時・分・秒 | dt.plusHours(1)、dt + Duration.ofMinutes(45) |
LocalDateTime・LocalTime |
| 年月日をまとめて | d + Period.of(1, 2, 3) |
LocalDate・LocalDateTime |
数値をそのまま足す d + 1 や、日付どうしを引く b - a は書けません。コンパイルすると次のエラーになります。
import java.time.*
fun main() {
val a = LocalDate.of(2026, 10, 5)
val b = LocalDate.of(2026, 10, 8)
for (d in a..b) println(d)
val x = a + 1
val diff = b - a
val n: LocalDate = LocalDate.parse("2026-10-05").takeIf { it > b }
}
err.kt:6:15: error: for-loop range must have an 'iterator()' method.
for (d in a..b) println(d)
^^^^
err.kt:7:17: error: argument type mismatch: actual type is 'Int', but 'TemporalAmount!' was expected.
val x = a + 1
^
err.kt:8:20: error: argument type mismatch: actual type is 'LocalDate!', but 'TemporalAmount!' was expected.
val diff = b - a
^
err.kt:9:22: error: initializer type mismatch: expected 'LocalDate', actual 'LocalDate?'.
val n: LocalDate = LocalDate.parse("2026-10-05").takeIf { it > b }
^
| エラーになる書き方 | 代わりの書き方 |
|---|---|
for (d in a..b) |
generateSequence(a) { it.plusDays(1) }.takeWhile { it <= b } |
a + 1 |
a.plusDays(1)、a + Period.ofDays(1) |
b - a |
ChronoUnit.DAYS.between(a, b)、Period.between(a, b) |
takeIf の結果を LocalDate で受ける |
型を LocalDate? にする |
月末の加算:1 月 31 日の 1 か月後
「月末の加算」の出力を表にすると、次のようになります。
| 式 | 結果 | 説明 |
|---|---|---|
2026-01-31 に plusMonths(1) |
2026-02-28 |
2 月 31 日は無いので、その月の末日に調整される |
さらに plusMonths(1) |
2026-03-28 |
28 日から 1 か月後。31 日には戻らない |
2026-01-31 に plusMonths(2) |
2026-03-31 |
元の日付から 2 か月後 |
2026-03-31 に minusMonths(1) |
2026-02-28 |
引き算でも同じ調整 |
2028-01-31 に plusMonths(1) |
2028-02-29 |
うるう年は 29 日 |
2028-02-29 に plusYears(1) |
2029-02-28 |
翌年に 29 日は無い |
毎月の月末日を順に求めるときは、1 か月ずつ足し続けるのではなく、起点の日付に plusMonths(n) を使います。 出力の 2026-02-28 2026-03-31 2026-04-30 2026-05-31 がその結果です。月末そのものが欲しい場合は、with(TemporalAdjusters.lastDayOfMonth()) や YearMonth.of(2026, 2).atEndOfMonth() が確実です。
差分を計算する:ChronoUnit・Period・Duration の違い
import java.time.*
import java.time.temporal.ChronoUnit
fun main() {
val start = LocalDate.of(2026, 1, 31)
val end = LocalDate.of(2026, 10, 5)
// ChronoUnit:1つの単位で数えた合計
println(ChronoUnit.DAYS.between(start, end))
println(ChronoUnit.WEEKS.between(start, end))
println(ChronoUnit.MONTHS.between(start, end))
println(ChronoUnit.YEARS.between(start, end))
println(start.until(end, ChronoUnit.DAYS))
// Period:年・月・日に分けた差
val p = Period.between(start, end)
println("$p -> ${p.years}年 ${p.months}か月 ${p.days}日 toTotalMonths=${p.toTotalMonths()}")
println(Period.between(end, start)) // 逆順はマイナス
// Duration:時間の長さ(秒とナノ秒)
val from = LocalDateTime.of(2026, 10, 5, 9, 0)
val to = LocalDateTime.of(2026, 10, 7, 18, 45, 30)
val dur = Duration.between(from, to)
println("$dur -> ${dur.toDays()}日 ${dur.toHoursPart()}時間 ${dur.toMinutesPart()}分 ${dur.toSecondsPart()}秒")
println("${dur.toHours()} ${dur.toMinutes()} ${dur.seconds}")
println(ChronoUnit.HOURS.between(from, to))
println(ChronoUnit.DAYS.between(from, to)) // 24時間に満たない分は切り捨て
println(ChronoUnit.DAYS.between(from.toLocalDate(), to.toLocalDate()))
// 年齢
val birth = LocalDate.of(2000, 10, 6)
println(Period.between(birth, end).years)
println(ChronoUnit.YEARS.between(birth, end.plusDays(1)))
// Duration は LocalDate どうしには使えない
try {
println(Duration.between(start, end))
} catch (e: Exception) {
println("${e.javaClass.name}: ${e.message}")
}
println(Duration.between(start.atStartOfDay(), end.atStartOfDay()).toDays())
}
247
35
8
0
247
P8M5D -> 0年 8か月 5日 toTotalMonths=8
P-8M-5D
PT57H45M30S -> 2日 9時間 45分 30秒
57 3465 207930
57
2
2
25
26
java.time.temporal.UnsupportedTemporalTypeException: Unsupported unit: Seconds
247
2026 年 1 月 31 日から 10 月 5 日までを 3 つの方法で求めた結果です。
| 方法 | 結果 | 意味 | 向いている用途 |
|---|---|---|---|
ChronoUnit.DAYS.between(a, b) |
247 |
日数の合計 | 残り日数、経過日数 |
ChronoUnit.MONTHS.between(a, b) |
8 |
満了した月数(端数は切り捨て) | 経過月数 |
Period.between(a, b) |
P8M5D |
8 か月と 5 日 | 「何年何か月何日」の表示、年齢 |
Duration.between(from, to) |
PT57H45M30S |
57 時間 45 分 30 秒 | 経過時間、所要時間 |
つまずきやすい点が 3 つあります。
Periodのdaysは合計日数ではありません。P8M5Dのdaysは「8 か月を除いた残りの 5 日」です。合計日数が欲しいときはChronoUnit.DAYS.betweenを使います。LocalDateTimeどうしのChronoUnit.DAYS.betweenは 24 時間単位で数えます。 10 月 5 日 9:00 から 10 月 7 日 18:45 は2日です。日付の差として数えたいときはtoLocalDate()で日付にしてから比べます。Duration.betweenはLocalDateどうしには使えません。 出力のとおりUnsupportedTemporalTypeException: Unsupported unit: Secondsになります。atStartOfDay()で日時にするか、ChronoUnit.DAYS.betweenを使います。
Duration を「2 日 9 時間 45 分 30 秒」のように分けて表示するには、toDays() と toHoursPart()・toMinutesPart()・toSecondsPart() を組み合わせます。toHours() は合計の時間数(57)、toHoursPart() は日を除いた残りの時間(9)です。
年齢は Period.between(誕生日, 今日).years で求められます。出力では、2000 年 10 月 6 日生まれの人は 2026 年 10 月 5 日の時点で 25、翌日に 26 になっています。
Kotlin らしい書き方:比較・範囲・kotlin.time.Duration・measureTime
import java.time.*
import java.time.temporal.ChronoUnit
import kotlin.time.Duration.Companion.days
import kotlin.time.Duration.Companion.hours
import kotlin.time.Duration.Companion.minutes
import kotlin.time.Duration.Companion.seconds
import kotlin.time.measureTime
import kotlin.time.measureTimedValue
import kotlin.time.toJavaDuration
import kotlin.time.toKotlinDuration
// 拡張プロパティ・拡張関数で読みやすくする
val LocalDate.isWeekend: Boolean
get() = dayOfWeek == DayOfWeek.SATURDAY || dayOfWeek == DayOfWeek.SUNDAY
infix fun LocalDate.daysUntil(other: LocalDate): Long = ChronoUnit.DAYS.between(this, other)
fun main() {
val a = LocalDate.of(2026, 10, 5)
val b = LocalDate.of(2026, 10, 20)
// 比較は演算子で書ける(compareTo が呼ばれる)
println("${a < b} ${a >= b} ${a == LocalDate.parse("2026-10-05")} ${a.isBefore(b)}")
// 範囲に入っているか
val campaign = a..b
println("${LocalDate.of(2026, 10, 10) in campaign} ${LocalDate.of(2026, 10, 21) in campaign}")
println(LocalDate.of(2026, 11, 1).coerceIn(a, b))
println("${maxOf(a, b)} ${minOf(a, b)}")
// 並べ替えと最小・最大
val dates = listOf(b, a, LocalDate.of(2026, 9, 30))
println(dates.sorted())
println("${dates.min()} ${dates.max()}")
// 1日ずつ進める
val week = generateSequence(a) { it.plusDays(1) }.takeWhile { it <= a.plusDays(6) }.toList()
println(week.map { it.dayOfMonth })
println(week.filter { it.isWeekend })
println(a.datesUntil(a.plusDays(3)).toList()) // 終わりの日は含まない
println(a daysUntil b)
// kotlin.time.Duration
val k = 1.hours + 30.minutes
println("$k ${k.inWholeMinutes} ${k.toIsoString()}")
println(k.toComponents { h, m, _, _ -> "${h}時間${m}分" })
val j: java.time.Duration = k.toJavaDuration() // Kotlin -> Java
println(j)
println(java.time.Duration.ofSeconds(3725).toKotlinDuration()) // Java -> Kotlin
val dt = LocalDateTime.of(2026, 10, 5, 9, 0)
println(dt + 90.minutes.toJavaDuration())
println(dt + 2.days.toJavaDuration())
println("${(36.hours).inWholeDays} ${90.seconds} ${1.5.hours} ${10.minutes > 500.seconds}")
// 処理時間を測る
val elapsed = measureTime { Thread.sleep(120) }
println(elapsed.inWholeMilliseconds >= 120)
val (value, took) = measureTimedValue { (1..1_000_000).sumOf { it.toLong() } }
println("$value ${took.inWholeMilliseconds < 5000}")
}
true false true true
true false
2026-10-20
2026-10-20 2026-10-05
[2026-09-30, 2026-10-05, 2026-10-20]
2026-09-30 2026-10-20
[5, 6, 7, 8, 9, 10, 11]
[2026-10-10, 2026-10-11]
[2026-10-05, 2026-10-06, 2026-10-07]
15
1h 30m 90 PT1H30M
1時間30分
PT1H30M
1h 2m 5s
2026-10-05T10:30
2026-10-07T09:00
1 1m 30s 1h 30m true
true
500000500000 true
比較は演算子、期間の判定は in
LocalDate・LocalDateTime・Instant などは Comparable を実装しているので、Kotlin では <・<=・>・>= が使えます。== は equals を呼ぶため、同じ日付なら true です。
| やりたいこと | Java の書き方 | Kotlin の書き方 |
|---|---|---|
| 前かどうか | a.isBefore(b) |
a < b |
| 同じか以降か | !a.isBefore(b) |
a >= b |
| 同じ日付か | a.equals(b) |
a == b |
| 期間内か(両端を含む) | !d.isBefore(s) && !d.isAfter(e) |
d in s..e |
| 期間内に収める | 条件分岐で書く | d.coerceIn(s, e) |
| 遅いほう・早いほう | a.isAfter(b) ? a : b |
maxOf(a, b)、minOf(a, b) |
| 並べ替え | Collections.sort(list) |
list.sorted() |
a..b は「範囲に入っているか」の判定には使えますが、そのまま for では回せません(前の章のエラー文のとおりです)。1 日ずつ進めるときは generateSequence か、datesUntil を使います。datesUntil は終わりの日を含まないので、出力では 10 月 5 日から 7 日までの 3 日分になっています。
java.time.Duration と kotlin.time.Duration を変換する
Kotlin の標準ライブラリには kotlin.time.Duration があり、1.hours + 30.minutes のように数値の拡張プロパティで作れます。Kotlin 公式ドキュメントの時間計測のページによると、Int・Long・Double に対して nanoseconds・microseconds・milliseconds・seconds・minutes・hours・days が用意されています。
| 観点 | java.time.Duration |
kotlin.time.Duration |
|---|---|---|
| 作り方 | Duration.ofMinutes(90) |
90.minutes、1.hours + 30.minutes |
toString() |
PT1H30M |
1h 30m |
| ISO 形式 | toString() |
toIsoString()(PT1H30M) |
| 合計を取り出す | toMinutes() |
inWholeMinutes |
| 成分に分ける | toHoursPart()・toMinutesPart() |
toComponents { h, m, s, ns -> ... } |
LocalDateTime に足す |
dt + duration |
dt + duration.toJavaDuration() |
| 相互の変換 | toKotlinDuration() |
toJavaDuration() |
LocalDateTime に足せるのは java.time 側の型なので、Kotlin の Duration は toJavaDuration() で変換してから足します。 出力の 2026-10-05T10:30 は、9 時に 90.minutes.toJavaDuration() を足した結果です。逆向きは toKotlinDuration() で、3725 秒が 1h 2m 5s と表示されています。
measureTime で処理時間を測る
measureTime { ... } はブロックの実行時間を kotlin.time.Duration で返し、measureTimedValue { ... } は結果の値と時間の両方を返します。分解宣言 val (value, took) = ... で受け取れます。得られた値は inWholeMilliseconds で数値として取り出せます。
タイムゾーンを変換する
import java.time.*
import java.time.format.DateTimeFormatter
fun main() {
val tokyo = ZoneId.of("Asia/Tokyo")
val newYork = ZoneId.of("America/New_York")
val fmt = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm xxx VV")
// ゾーンなしの日時に「どこの時刻か」を付ける
val local = LocalDateTime.of(2026, 10, 5, 9, 0)
val inTokyo = local.atZone(tokyo)
println(inTokyo.format(fmt))
// 同じ瞬間を別のゾーンで表す
println(inTokyo.withZoneSameInstant(newYork).format(fmt))
println(inTokyo.withZoneSameInstant(ZoneOffset.UTC).format(fmt))
// 時計の数字を変えずにゾーンだけ替える(別の瞬間になる)
println(inTokyo.withZoneSameLocal(newYork).format(fmt))
// Instant・エポックミリ秒との変換
val instant = inTokyo.toInstant()
println(instant)
println(instant.toEpochMilli())
println(Instant.ofEpochMilli(1791158400000).atZone(tokyo))
println(LocalDateTime.ofInstant(instant, newYork))
println(inTokyo.toOffsetDateTime())
// ISO 形式の文字列を日本時間で表示する
println(OffsetDateTime.parse("2026-10-05T00:00:00Z").atZoneSameInstant(tokyo).toLocalDateTime())
// 同じ瞬間どうしの比較
val ny = inTokyo.withZoneSameInstant(newYork)
println("${inTokyo == ny} ${inTokyo.isEqual(ny)} ${inTokyo.toInstant() == ny.toInstant()}")
// 夏時間の切り替え(ニューヨーク 2026-03-08 は 2:00 が 3:00 になる)
println(LocalDateTime.of(2026, 3, 8, 2, 30).atZone(newYork))
val before = ZonedDateTime.of(2026, 3, 7, 12, 0, 0, 0, newYork)
println(before.plusDays(1))
println(before.plus(Duration.ofHours(24)))
}
2026-10-05 09:00 +09:00 Asia/Tokyo
2026-10-04 20:00 -04:00 America/New_York
2026-10-05 00:00 +00:00 Z
2026-10-05 09:00 -04:00 America/New_York
2026-10-05T00:00:00Z
1791158400000
2026-10-05T09:00+09:00[Asia/Tokyo]
2026-10-04T20:00
2026-10-05T09:00+09:00
2026-10-05T09:00
false true true
2026-03-08T03:30-04:00[America/New_York]
2026-03-08T12:00-04:00[America/New_York]
2026-03-08T13:00-04:00[America/New_York]
| やりたいこと | 書き方 | 出力での確認 |
|---|---|---|
| ゾーンなしの日時にゾーンを付ける | local.atZone(tokyo) |
09:00 +09:00 Asia/Tokyo |
| 同じ瞬間を別のゾーンで表す | withZoneSameInstant(zone) |
2026-10-04 20:00 -04:00(ニューヨーク) |
| 時計の数字はそのままゾーンだけ替える | withZoneSameLocal(zone) |
09:00 -04:00(別の瞬間になる) |
| UTC の瞬間にする | toInstant() |
2026-10-05T00:00:00Z |
| エポックミリ秒にする・から戻す | toEpochMilli()、Instant.ofEpochMilli(ms).atZone(zone) |
1791158400000 |
| ISO 形式の文字列を日本時間で読む | OffsetDateTime.parse(s).atZoneSameInstant(tokyo) |
2026-10-05T09:00 |
- 変換には
withZoneSameInstantを使います。 日本の 10 月 5 日 9 時は、ニューヨークでは前日の 20 時です。withZoneSameLocalは「9:00」という数字を保ったままゾーンを替えるので、別の瞬間を指します。 ZonedDateTimeの==はゾーンも含めて比べます。 同じ瞬間でもゾーンが違うとfalseです。同じ瞬間かどうかはisEqualか、toInstant()どうしの==で比べます(出力はfalse true true)。- 夏時間のあるゾーンでは、存在しない時刻が自動でずれます。 ニューヨークの 2026 年 3 月 8 日 2:30 は、出力のとおり
03:30-04:00になりました。また、前日の正午にplusDays(1)を足すと翌日の 12:00、Duration.ofHours(24)を足すと 13:00 になります。「1 日後」と「24 時間後」は、夏時間の切り替えをまたぐと 1 時間ずれます。
保存や受け渡しには Instant か OffsetDateTime(UTC との差つき)を使い、画面に出す直前に ZoneId を指定して現地時刻に直すと、地域が増えても同じコードで扱えます。
よくある質問
Q. Kotlin に日付専用の標準クラスはありますか?
A. JVM 向けの Kotlin では、Java 標準の java.time(LocalDate・LocalDateTime・ZonedDateTime など)をそのまま使うのが基本です。追加の依存は不要で、この記事のコードはすべて import だけで動きます。時間の長さと計測には、Kotlin 標準ライブラリの kotlin.time.Duration・measureTime が使えます。
Q. LocalDateTime と ZonedDateTime はどう使い分けますか?
A. 1 つの地域の中だけで完結する日時は LocalDateTime、複数の地域で同じ瞬間を共有する日時は ZonedDateTime か Instant を使います。LocalDateTime は「どこの 9 時か」を持たないので、別の地域の時刻に直すときは atZone(zone) でゾーンを付けてから withZoneSameInstant を呼びます。
Q. 日付を yyyy/MM/dd で表示したら年がずれました。なぜですか?
A. パターンが YYYY/MM/dd になっていないか確かめてください。YYYY は週を基準にした年で、Locale.JAPAN では 2026 年 12 月 27 日〜31 日が 2027 と表示されます。年は小文字の yyyy で書きます。
Q. 2 つの日付の差を「何日」で出すにはどれを使いますか?
A. ChronoUnit.DAYS.between(a, b) です。Period.between(a, b).days は年と月を除いた残りの日数なので、1 か月を超える差では合計日数になりません。
まとめ
- Kotlin(JVM)の日付と時刻は
java.timeを使う。日付はLocalDate、日時はLocalDateTime、地域をまたぐならZonedDateTime・Instant - フォーマットは
DateTimeFormatter.ofPattern。年はyyyy、月はMM、日はdd、24 時間表記の時はHH、分はmm。YYYY・hh・DDは別の意味で、特定の日時だけ出力がずれる - 文字列からの変換は
parse。DateTimeParseExceptionのat index Nは合わなくなった位置。null を返す拡張関数にすると?:で扱える 2026/02/30を止めたいときはuuuuとResolverStyle.STRICT。既定では2026-02-28に丸められる- 加減算は
plusDaysなどのほか、d + Period.ofDays(3)と演算子で書ける。1 月 31 日の 1 か月後は 2 月 28 日で、続けて足すと 3 月 28 日になる - 差分は目的で選ぶ:合計日数は
ChronoUnit.DAYS.between、年月日に分けるならPeriod、時間の長さはDuration - Kotlin では
a < b・d in s..e・maxOfで比較でき、90.minutes.toJavaDuration()でkotlin.time.Durationをjava.timeに渡せる
参考・出典
- Oracle「DateTimeFormatter(Java SE 21 API)」 https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/time/format/DateTimeFormatter.html (パターン文字の定義、
ofPatternの解決スタイル) - Kotlin 公式ドキュメント「Time measurement」 https://kotlinlang.org/docs/time-measurement.html (
kotlin.time.Durationの作り方、文字列表現、toComponents、measureTime・measureTimedValue)
本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。
(記載例)出典:株式会社RJC「Kotlin 日付と時刻の扱い方 LocalDateのフォーマットと差分計算」
株式会社RJC ― SI事業・SES事業・AI駆動開発。RJCは一緒に成長を楽しめる会社です。
WE ARE HIRING
RJCで一緒に開発しながら、
成長を楽しみませんか?
RJCは、Web・モバイル・AIを活用した開発プロジェクトで、テックリードやPM・PMOも活躍するシステム開発会社です。会社を知る、待遇を確かめる、話を聞いてみる。気になるところ見てみてください!
- 127日年間休日
- 12時間平均残業時間
- 毎日ガチャ遊びココロも大切にする福利厚生。アマギフなどの賞品ラインナップ!
ほかにも、チケットレストラン、書籍読み放題、2年ごとの慰労報奨(休暇 or 金一封)、11期連続の黒字決算。