androidkotlinandroid-jetpack-composejava-timeandroid-timepicker

How do you display AM or PM using a time picker in jetpack compose?


I've developed a simple time picker app in jetpack compose. It consists of a text composable and a time picker. The text displays the selected time. However it doesn't display whether it is AM or PM.

Here is a breakdown of the application. It consists of a column that contains a text composable and a time picker. There are three variables. One holds the current time using the built in calendar, one holds the time picker state, and the final one formats the time from the time picker.

Now here is the code:

// App Layout
Column(
    modifier = Modifier.fillMaxSize(),
    horizontalAlignment = Alignment.CenterHorizontally,
    verticalArrangement = Arrangement.Center,
) {
    // Variable That Stores The Current Time
    val currentTime = Calendar.getInstance()

    // Variable That Stores Time Picker State
    val timePickerState =
        rememberTimePickerState(
            initialHour = currentTime[Calendar.HOUR_OF_DAY],
            initialMinute = currentTime[Calendar.MINUTE],
            is24Hour = false,
        )

    // Variable That Stores The Formatted Time
    val formattedTime =
        "%02d:%02d".format(
            timePickerState.hour,
            timePickerState.minute,
        )

    // Text That Displays The Selected Time
    Text(text = formattedTime)

    // Time Picker
    TimePicker(
        state = timePickerState,
    )
}

As stated above I can't figure out how to add AM or PM to the text composable or even save the user's preference of AM or PM. There is a parameter for the timePickerState called is24Hour, however it is a boolean and it will cause a crash when added to a string.


Solution

  • First off, you are still using Java's old date api that became obsolete 10 years ago. If you want to stick to a Java API instead of the Kotlin wrapper (kotlinx.datetime.*), use the Time API from Java 8 that is located in the java.time.* packages:

    // Variable That Stores The Current Time
    val currentTime = LocalTime.now()
    
    // Variable That Stores Time Picker State
    val timePickerState =
        rememberTimePickerState(
            initialHour = currentTime.hour,
            initialMinute = currentTime.minute,
            is24Hour = false,
        )
    

    Now, with this out of the way, the TimePicker can be toggled between a 24-hour selection mode and an AM/PM selection mode with is24Hour. The result is not affected by it, though, this is just for the user's convenience. The resulting timePickerState.hour will always be in the range of 0 to 23 so you can easily work with it, like doing time arithmetics and so on.

    If you want to display this time with an AM/PM marker at the end, this is solely a formatting issue. One that can be easily solved, since we are now already using the Java Time API:

    // Variable That Stores The Formatted Time
    val localTime = LocalTime.of(timePickerState.hour, timePickerState.minute)
    val pattern = if (timePickerState.is24hour) "HH:mm" else "hh:mm a"
    val formattedTime = localTime.format(DateTimeFormatter.ofPattern(pattern))
    

    First a LocalTime is created from the TimePicker's state. It is then formatted with a DateTimeFormatter according to pattern. Now, this pattern is the important part: "hh:mm a" formats the time as an AM/PM format with hours in the range 1-12 and a trailing AM/PM marker. "HH:mm" on the other hand formats this as a simple time with hours in the range 0-23.

    In the code above I decided to switch between these two patterns depending on how the TimePickerState was initialized (timePickerState.is24hour). In your code that is always false, so you will never see the 0-23 hour range. But if you do not set a flat false or true for is24hour and make it dependent on a variabe you could let the user choose which one they prefer. It will then affect the TimePicker and the formatted time.