JetpackComposeで日時を設定する

（育休にてAndroidを久しぶりに触ったので、JetpackComposeを改めて勉強中）

# 概要
JetpackCompose Material3にて日時設定をしようと思ったら、いまだにDatePickerとTimePickerは別々になっていて面倒だったので、両方を呼び出してDateを取得できるようにする。
(2023/11/27時点だとTimePickerDialogも開発中でサンプルコードがあるのみ)

# 実装
## 日付を設定する
```kotlin
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun DatePickerDialogComponent(
    showPicker: MutableState<Boolean>,
    onCanceled: () -> Unit,
    onSelected: (dateMillis: Long) -> Unit
) {
    if (!showPicker.value) {
        return
    }

    val datePickerState = rememberDatePickerState(
        initialSelectedDateMillis = Date().time
    )

    DatePickerDialog(
        onDismissRequest = {
            showPicker.value = false
            onCanceled()
        },
        confirmButton = {
            TextButton(
                onClick = {
                    val selectedDateMillis = datePickerState.selectedDateMillis
                    datePickerState.setSelection(selectedDateMillis)
                    showPicker.value = false
                    if (selectedDateMillis != null) {
                        onSelected(selectedDateMillis)
                    } else {
                        onCanceled()
                    }
                }
            ) {
                Text(text = "OK")
            }
        },
        dismissButton = {
            TextButton(
                onClick = {
                    showPicker.value = false
                    onCanceled()
                }
            ) {
                Text(text = "CANCEL")
            }
        }
    ) {
        DatePicker(state = datePickerState)
    }
}
```

## 時刻を設定する
```kotlin
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun TimePickerDialogComponent(
    showPicker: MutableState<Boolean>,
    onCanceled: () -> Unit,
    onSelected: (hour: Int, minute: Int) -> Unit
) {
    if (!showPicker.value) {
        return
    }

    val cal = Calendar.getInstance()
    val hour = cal.get(Calendar.HOUR_OF_DAY)
    val minute = cal.get(Calendar.MINUTE)
    val timePickerState = rememberTimePickerState(
        initialHour = hour,
        initialMinute = minute,
        is24Hour = true
    )

    TimePickerDialog(
        onCancel = {
            showPicker.value = false
            onCanceled()
        },
        onConfirm = {
            showPicker.value = false
            onSelected(timePickerState.hour, timePickerState.minute)
        }
    ) {
        TimePicker(state = timePickerState)
    }
}
```

## 日付と時刻を両方を交互に呼び出す
```kotlin
@Composable
fun DateTimePickerComponent(
    showPicker: MutableState<Boolean>,
    onSelected: (date: Date) -> Unit
) {

    if (!showPicker.value) {
        return
    }

    val showDatePicker = remember { mutableStateOf(true) }
    val showTimePicker = remember { mutableStateOf(false) }
    val dateMillisState = remember { mutableStateOf<Long?>(null) }

    DatePickerDialogComponent(
        showPicker = showDatePicker,
        onCanceled = {
            showPicker.value = false
        }
    ) { dateMillis ->
        dateMillisState.value = dateMillis
        showTimePicker.value = true
    }

    TimePickerDialogComponent(
        showPicker = showTimePicker,
        onCanceled = {
            showPicker.value = false
        }
    ) { hour, minute ->
        val dateMillis = dateMillisState.value
        if (dateMillis != null) {
            val cal = Calendar.getInstance()
            cal.timeInMillis = dateMillis
            cal.set(Calendar.HOUR_OF_DAY, hour)
            cal.set(Calendar.MINUTE, minute)
            onSelected(cal.time)
        }
        showPicker.value = false
    }
}
```

## 使う側
```kotlin
val showPicker = remember { mutableStateOf(false) }

Button(
    modifier = onClick = {
        showPicker.value = true
    }) {
    Text(text = "日時指定で記録")
}

DateTimePickerComponent(showPicker = showPicker) {
    viewModel.save(it)
}
```

ひとまずはこれで動きました。

もしかしたらDateではなくInstantなどの新しいAPIを使った方が良さそうですかね。
ただJetpackComposeでのダイアログの操作って、昔のAndroidに慣れてると違和感というか面倒だと思ってしまいます…

# 追記
DatePickerが以下の初期値だと-9時間されてしまってるのに気づきました。。。
```kotlin
initialSelectedDateMillis = Date().time
```

ドキュメント見るとUTCで作らないといけなさそうですね
> initialSelectedDateMillis - timestamp in UTC milliseconds from the epoch that represents an initial selection of a date. Provide a null to indicate no selection. Note that the state's selectedDateMillis will provide a timestamp that represents the start of the day, which may be different than the provided initialSelectedDateMillis.

こちらで作り直したらイケました。
```
initialSelectedDateMillis = LocalDate.now()
    .atStartOfDay()
    .toInstant(ZoneOffset.UTC)
    .toEpochMilli()
```


# 参考
- [Material Design 3 for Compose gets new components and features](https://material.io/blog/material-3-compose-1-1)
- [material3 samples](https://cs.android.com/androidx/platform/tools/dokka-devsite-plugin/+/master:testData/compose/samples/material3/samples/TimePickerSamples.kt;l=230;drc=03ca30d22e6ee3483142f2e4048db459cb5afb79)