Search code examples
javaandroidkotlintimepickerandroid-timepicker

TimePicker incorrectly choosing 12 hour version of the same time when clicking on its 24 hour version


Very weird. I know. This is the executing code.

(Code in the EDIT.)

Once I select a time and a date, and do the same again, the previous output text differs from this output text.

Note that the user setting is 24 hour format.

First time doing it:

Selecting datetime the first time!

Here's how it looks after doing the first time (ignore the highlighted "person", that is an implementation of a search mechanism for contacts):

Looks fine for the first time, as expected

THEN, the same thing again (I swear this is the second time I'm doing this, it might look the same):

As expected for the second time

But what is this monstrosity?!

Don't know what is happening here

Toasts confirm that both the time I'm entering the 24 hour if-clause, so I think that choosing the right format for date is fine. (Also note that if it were 12 hour format, an AM/PM would have been there as suggested by the code but isn't there in the output).

I don't know how this could be happening. The SimpleDateFormat is instantiated right before usage, so messing with it anywhere else is not possible, so I don't think it's coming from datePickerDialog.

Any clues/suggestions/ideas?

EDIT: As requested, here's the Adapter code. I'm new so please go easy on me :) The relevant code is in fun setDateTime()


class ContactAdapter(
    private val context: Context,
    private val contact_name: String?,
    private val items: List<Contact>
) :
    RecyclerView.Adapter<ContactAdapter.ViewHolder>() {
    private val c = Calendar.getInstance()
    fun createDatePicker(onSetDate: (DatePicker, Int, Int, Int) -> Unit) = DatePickerDialog(
        context,
        onSetDate,
        c[Calendar.YEAR],
        c[Calendar.MONTH],
        c[Calendar.DAY_OF_MONTH]
    )

    fun createTimePicker(onSetTime: (TimePicker, Int, Int) -> Unit) = TimePickerDialog(
        context,
        onSetTime,
        c[Calendar.HOUR],
        c[Calendar.MINUTE],
        DateFormat.is24HourFormat(context)
    )

    inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {

        init {
            setupAddButton()
            setupGreetingTimeText()
        }

        private fun setDateTime() {
            val datePicker = createDatePicker { _, year, month, date ->
                c[Calendar.YEAR] = year
                c[Calendar.MONTH] = month
                c[Calendar.DAY_OF_MONTH] = date

                val timePicker = createTimePicker { _, hour, minute ->
                    c[Calendar.HOUR] = hour
                    c[Calendar.MINUTE] = minute
                    itemView.txtGreetingTime.text = if (DateFormat.is24HourFormat(context)) {
                        Toast.makeText(context, "Chose 24-hour clause", Toast.LENGTH_SHORT).show()
                        SimpleDateFormat(
                            "dd mmm, yyyy 'at' HH:mm",
                            Locale.getDefault()
                        ).format(c.time)
                    } else {
                        Toast.makeText(context, "Chose 12-hour clause", Toast.LENGTH_SHORT).show()
                        SimpleDateFormat(
                            "dd mmm, yyyy 'at' hh:mm aa",
                            Locale.getDefault()
                        ).format(c.time)
                    }
                }

                // Do time picking stuff
                timePicker.show()

            }
            // Do date picking stuff
            datePicker.show()

        }
        private fun setupAddButton() {
            itemView.addButton.setOnClickListener {
                println("Attempted call to ${itemView.contactPhone.text}")
                itemView.layoutTimingDetails.visibility =
                    if (itemView.layoutTimingDetails.visibility == View.VISIBLE) View.GONE else View.VISIBLE

            }
        }

        private fun setupGreetingTimeText() {
            itemView.txtGreetingTime.setOnClickListener {
                it.txtGreetingTime.text = dateTimeToString()
            }
        }

        fun setData(item: Contact, contact_name: String?) {
            itemView.contactName.text = item.name
            itemView.contactPhone.text = item.phone
            itemView.contactEmail.text = item.email

            if (contact_name != null)
                itemView.contactName.highlightBackground(contact_name, Color.YELLOW)
        }
    }

    override fun getItemCount(): Int = items.size
    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder =
        ViewHolder(
            LayoutInflater.from(parent.context).inflate(R.layout.card_contact, parent, false)
        )

    override fun onBindViewHolder(holder: ViewHolder, position: Int) =
        holder.setData(items[position], contact_name)
}

EDIT 2: Earlier I thought that SimpleDateFormat was at fault here. But it is actually the TimePicker. It's somehow wrongly interpreting a click on 24 hour buttons and instead registering their counterpart 12 hour buttons as input.

Example if I chose 18:30 as the time the first time, then if I chose 6:30 as the time the second time, the time generated is still 18:30. However, if for the third time is changed to 18:30, now the registered time is 6:30.


Solution

  • I created a DateTimePickerSample to help you. Original issue was wrong use of HOUR from java.util.Calendar. Using HOUR_OF_DAY returns correct results.

    From Java Documenation

    HOUR: Field number for get and set indicating the hour of the morning or afternoon. HOUR_OF_DAY: Field number for get and set indicating the hour of the day.

    Usage:

    val picker = DateTimePickerSample(this)
    picker.pickDateTime(
        usePreviousCalendar = true,
        callback = object: DateTimePickerSample.Callback {
    
            override fun onDateTimeSetSet(dateTimeStr: String, is24HourFormat: Boolean) {
                Log.d("Duh", "date=$dateTimeStr, is24HourFormat=$is24HourFormat")
            }
        }
    )
    

    DateTimePickerSample.kt

    import android.app.DatePickerDialog
    import android.app.TimePickerDialog
    import android.content.Context
    import android.text.format.DateFormat
    import android.widget.DatePicker
    import android.widget.TimePicker
    import java.text.SimpleDateFormat
    import java.util.*
    
    class DateTimePickerSample(
        private val context: Context
    ) : DatePickerDialog.OnDateSetListener,
        TimePickerDialog.OnTimeSetListener {
    
        interface Callback {
            fun onDateTimeSetSet(dateTimeStr: String, is24HourFormat: Boolean)
        }
    
        private var callback: Callback? = null
        private var calendar = Calendar.getInstance()
    
        private val is24HourFormat = DateFormat.is24HourFormat(context)
    
        fun pickDateTime(usePreviousCalendar: Boolean, callback: Callback) {
            this.callback = callback
            if (!usePreviousCalendar) {
                this.calendar = Calendar.getInstance()
            }
    
            val datePickerDialog = createDatePicker()
            datePickerDialog.show()
        }
    
        private fun createDatePicker(): DatePickerDialog {
            return DatePickerDialog(
                context,
                this,
                calendar[Calendar.YEAR],
                calendar[Calendar.MONTH],
                calendar[Calendar.DAY_OF_MONTH]
            )
        }
    
        private fun createTimePicker(): TimePickerDialog {
            return TimePickerDialog(
                context,
                this,
                calendar[Calendar.HOUR_OF_DAY],
                calendar[Calendar.MINUTE],
                is24HourFormat
            )
        }
    
        /*
         * DatePickerDialog.OnDateSetListener
         */
    
        override fun onDateSet(view: DatePicker?, year: Int, month: Int, dayOfMonth: Int) {
            calendar[Calendar.YEAR] = year
            calendar[Calendar.MONTH] = month
            calendar[Calendar.DAY_OF_MONTH] = dayOfMonth
    
            val timePickerDialog = createTimePicker()
            timePickerDialog.show()
        }
    
        /*
         * TimePickerDialog.OnTimeSetListener
         */
    
        override fun onTimeSet(view: TimePicker?, hourOfDay: Int, minute: Int) {
            calendar[Calendar.HOUR_OF_DAY] = hourOfDay
            calendar[Calendar.MINUTE] = minute
    
            val dateFormatter = if (is24HourFormat) {
                SimpleDateFormat("dd mmm, yyyy 'at' HH:mm", Locale.getDefault())
            } else {
                SimpleDateFormat("dd mmm, yyyy 'at' hh:mm aa", Locale.getDefault())
            }
    
            val dateTimeStr = dateFormatter.format(calendar.time)
            callback?.onDateTimeSetSet(dateTimeStr, is24HourFormat)
        }
    }