Creating Date and Time Pickers in an Android App
Date and time fields are common in Android applications that manage appointments, reminders, bookings, delivery windows, or event schedules. A DatePickerDialog lets a user choose a calendar date, while a TimePickerDialog provides a familiar clock-based interface for selecting hours and minutes.
Using these components is usually better than asking users to type values manually. It reduces formatting errors and works well for Australian users, who commonly expect dates in day-month-year order, such as 25/04/2026, rather than the US month-day-year format.
This tutorial uses Kotlin and Android Views. The example creates a simple appointment selector, displays the chosen value in a TextView, and shows how to format, validate, and store the result safely.
Preparing the Layout And Activity
Start with a layout containing two buttons and a text view. One button opens the calendar, and the other opens the clock. The text view shows the selected appointment time.
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="24dp">
<Button
android:id="@+id/dateButton"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Choose date" />
<Button
android:id="@+id/timeButton"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Choose time" />
<TextView
android:id="@+id/appointmentText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:text="No appointment selected"
android:textSize="18sp" />
</LinearLayout>
In MainActivity, keep the selected values in a Calendar object. Initialising it with the current date and time gives the dialogs sensible defaults. A person booking a tradie visit in Sydney or a school pickup in Melbourne can then adjust only the part of the appointment they need.
private val appointment = Calendar.getInstance()
Opening The Date Picker Dialog
A date picker dialog receives the current year, month, and day. Android’s Calendar.MONTH value starts at zero, so January is 0 and December is 11. The dialog handles that internal convention for you, while the displayed result can use the more familiar Australian format.
val dateButton = findViewById<Button>(R.id.dateButton)
val appointmentText = findViewById<TextView>(R.id.appointmentText)
dateButton.setOnClickListener {
val dialog = DatePickerDialog(
this,
{ _, year, month, dayOfMonth ->
appointment.set(Calendar.YEAR, year)
appointment.set(Calendar.MONTH, month)
appointment.set(Calendar.DAY_OF_MONTH, dayOfMonth)
updateAppointmentText(appointmentText)
},
appointment.get(Calendar.YEAR),
appointment.get(Calendar.MONTH),
appointment.get(Calendar.DAY_OF_MONTH)
)
dialog.show()
}
The callback runs after the user taps the positive button. The selected values are copied into the shared Calendar instance, so a later time selection keeps the same date. This also makes the code suitable for a basic booking screen where the date and time are chosen separately.
Adding The Time Picker
The time picker follows the same pattern. Its callback returns an hour and minute, which are stored in the existing appointment object. Passing false as the final constructor argument displays the 24-hour clock, a useful choice for Australian users who may enter work shifts, public transport times, or medical appointments.
val timeButton = findViewById<Button>(R.id.timeButton)
timeButton.setOnClickListener {
val dialog = TimePickerDialog(
this,
{ _, hourOfDay, minute ->
appointment.set(Calendar.HOUR_OF_DAY, hourOfDay)
appointment.set(Calendar.MINUTE, minute)
appointment.set(Calendar.SECOND, 0)
updateAppointmentText(appointmentText)
},
appointment.get(Calendar.HOUR_OF_DAY),
appointment.get(Calendar.MINUTE),
true
)
dialog.show()
}
Create a formatter to show the result in a readable form. SimpleDateFormat uses the device locale by default, but specifying Locale Australia makes the example’s intended regional behaviour explicit. The pattern below produces values such as 25/04/2026 14:30.
private fun updateAppointmentText(view: TextView) {
val formatter = SimpleDateFormat(
"dd/MM/yyyy HH:mm",
Locale("en", "AU")
)
view.text = formatter.format(appointment.time)
}
Formatting Dates For Australian Users
Date formatting should be consistent throughout the application. Australian users generally expect dd/MM/yyyy, while a 24-hour clock avoids ambiguity between morning and evening appointments. Labels should still be clear, especially when the app is used by older people or people with limited digital experience.
| Purpose | Recommended format | Example |
|---|---|---|
| Short date | dd/MM/yyyy |
25/04/2026 |
| Date and time | dd/MM/yyyy HH:mm |
25/04/2026 14:30 |
| Friendly date | EEE, d MMM |
Sat, 25 Apr |
| Storage value | ISO 8601 | 2026-04-25T14:30:00 |
The device time zone also matters. Sydney and Melbourne observe daylight saving, while Brisbane generally does not. If an appointment is tied to a physical location, use the venue’s time zone rather than assuming every Australian user follows the same clock rules. For a simple single-device app, Calendar.getInstance() is usually appropriate.
Avoid storing a display string as the primary value. Save a timestamp or a structured date and time, then format it when presenting the screen. This prevents sorting errors and makes future changes to the user’s regional settings easier to support.
Validating And Saving A Selection
Many applications need a valid future appointment rather than any calendar value. Compare the selected Calendar object with the current moment before saving. If the date is in the past, show an error beside the controls instead of silently accepting it.
private fun isFutureAppointment(): Boolean {
return appointment.after(Calendar.getInstance())
}
You can save the value in SharedPreferences for a small local example. For a larger booking application, SQLite or Room is more suitable because appointments may need searching, editing, and sorting.
val preferences = getSharedPreferences("appointments", MODE_PRIVATE)
if (isFutureAppointment()) {
preferences.edit()
.putLong("appointment_time", appointment.timeInMillis)
.apply()
} else {
appointmentText.text = "Choose a future date and time"
}
If the app stores names, addresses, health information, or booking details, consider the Australian Privacy Act 1988 and the Australian Privacy Principles. Store only what the feature needs, explain why it is collected, and protect it appropriately. An app that includes contact information can keep it behind a protected email address rather than exposing it directly in a public screen.
Improving Accessibility And Reliability
Give each control a meaningful label and ensure the selected value is visible without relying on colour alone. Buttons should have adequate touch targets, and the result text should be announced clearly by TalkBack. A short label such as “Appointment: Saturday 25 April at 2:30 pm” is easier to understand than a raw timestamp.
Configuration changes can recreate an activity, which may otherwise reset the selected values. For production code, use a ViewModel or onSaveInstanceState to preserve the appointment. Also test rotations, process recreation, and screens with large font settings.
Test dates around midnight and daylight-saving transitions. A booking made in Melbourne can behave differently from one made in Brisbane when clocks change. Test invalid past appointments, leap years, device locale changes, and both 12-hour and 24-hour display preferences before releasing the app to the Australian market.