🕘
Kotlinで時間経過と時差を考慮して日時表示するテクニック
Kotlinで日時表示を出し分けたくて、その方法を調べました。
やりたいこと
1. 投稿日の年月を条件次第で省略表示する
あるポストの投稿日時が 2024/1/1 9:00 だとして、
-
閲覧日時が 2024/1/1 10:00 なら
- 同日なので、投稿日時を 9:00 と出す
-
閲覧日時が 2024/1/2 9:00 なら
- 日をまたいだので、投稿日時を 1/1 9:00 と出す
-
閲覧日時が 2025/1/1 9:00 なら
- 年をまたいだので、投稿日時を 2024/1/1 9:00 と出す
SNSとかでよくあるやつです。
2. 投稿と閲覧が別の地域で行われたら、時差を考慮する
あるポストの投稿場所が東京、投稿日時が 2024/1/1 9:00 だとして、
-
閲覧場所がワシントン、閲覧日時が 2024/1/1 10:00 なら
- -17時間の時差で違う年になってしまうので、投稿日時を 2023/12/31 17:00 と出す
この複雑なロジックをKotlinで実装してみます。
やりかた
実装
2つのInstant
をZonedDateTime
に変換して計算します。
fun formatPostedTime(
postedTime: Instant,
browseTime: Instant,
locale: Locale,
zoneId: String,
): String {
val zone = ZoneId.of(zoneId, ZoneId.SHORT_IDS)
val postedTimeZoned = ZonedDateTime.ofInstant(postedTime, zone)
val browseTimeZoned = ZonedDateTime.ofInstant(browseTime, zone)
val sameDay = postedTimeZoned.toLocalDate() == browseTimeZoned.toLocalDate()
val sameYear = postedTimeZoned.year == browseTimeZoned.year
val pattern = if (sameDay) {
"H:mm"
} else if (sameYear) {
"M/d H:mm"
} else {
"yyyy/M/d H:mm"
}
return postedTimeZoned.format(DateTimeFormatter.ofPattern(pattern, locale))
}
テスト
パラメタライズドテストで、いろんな条件をテストします。サマータイムの切り替えの境目も考慮します。
@RunWith(Parameterized::class)
class FormatPostedTimeTest(
private val testCase: String,
private val postedUTCTime: String,
private val browseUTCTime: String,
private val locale: Locale,
private val zoneId: String,
private val expected: String,
) {
private companion object {
@JvmStatic
@Parameterized.Parameters(name = "{0}")
fun testParameters(): List<Array<Any>> {
return listOf(
arrayOf(
"東京、同年、同日",
"2024-01-01T00:00:00Z",
"2024-01-01T01:00:00Z",
Locale.JAPAN,
"JST",
"9:00"
),
arrayOf(
"東京、同年、別日",
"2024-01-01T00:00:00Z",
"2024-01-02T00:00:00Z",
Locale.JAPAN,
"JST",
"1/1 9:00"
),
arrayOf(
"東京、別年",
"2024-01-01T00:00:00Z",
"2025-01-01T00:00:00Z",
Locale.JAPAN,
"JST",
"2024/1/1 9:00"
),
arrayOf(
"ワシントン、同年、同日",
"2024-01-01T08:00:00Z",
"2024-01-01T09:00:00Z",
Locale.US,
"PST",
"0:00"
),
arrayOf(
"ワシントン、同年、別日",
"2024-01-01T08:00:00Z",
"2024-01-02T08:00:00Z",
Locale.US,
"PST",
"1/1 0:00"
),
arrayOf(
"ワシントン、別年",
"2024-01-01T08:00:00Z",
"2025-01-01T08:00:00Z",
Locale.US,
"PST",
"2024/1/1 0:00"
),
arrayOf(
"ワシントン、別年、サマータイム開始直前",
"2024-03-10T09:59:59Z",
"2025-03-10T09:59:59Z",
Locale.US,
"PST",
"2024/3/10 1:59"
),
arrayOf(
"ワシントン、別年、サマータイム開始直後",
"2024-03-10T10:00:00Z",
"2025-03-10T10:00:00Z",
Locale.US,
"PST",
"2024/3/10 3:00"
),
arrayOf(
"ワシントン、別年、サマータイム終了直前",
"2024-11-03T08:59:59Z",
"2025-11-03T08:59:59Z",
Locale.US,
"PST",
"2024/11/3 1:59"
),
arrayOf(
"ワシントン、別年、サマータイム終了直後",
"2024-11-03T09:00:00Z",
"2025-11-03T09:00:00Z",
Locale.US,
"PST",
"2024/11/3 1:00"
)
)
}
}
@Test
fun testFormatPostedTime() {
val actual = formatPostedTime(
postedTime = Instant.parse(postedUTCTime),
browseTime = Instant.parse(browseUTCTime),
locale = locale,
zoneId = zoneId
)
assertEquals(expected, actual)
}
}
テスト結果
補足
Androidで実装するなら、getBestDateTimePattern
を使って地域に応じた時刻表示(ワシントンならJanuary 2, 2024, 10:00
)に変換、いわゆる多言語対応も簡単にできます!
以上、普段の開発で気づいたことの共有でした!
宣伝
#CybozuSumerBlogFes2024 にも、アジャイルのふりかえりに関する記事を投稿します。お楽しみに!
Discussion