MaterialDatePicker returning wrong value

850 Views Asked by At

I am using MaterialDatePicker in android studio for the user to be able to choose the date of birth, after selecting the date, the returned value is a long with the timestamp of the selected date (milliseconds since 01/01/1970). The problem is that the result obtained is slightly wrong, example: If the user selects the date 10/05/1998 (1st of October 1998), the date returned is one day before this, that is, the calendar returns 10/04 / 1998 (October 4, 1998).

My code:

Calendar calendar = Calendar.getInstance();
MaterialDatePicker.Builder<Long> builder = 
MaterialDatePicker.Builder.datePicker();

MaterialDatePicker<Long> picker = builder.build();

picker.show(getChildFragmentManager(), picker.toString());

picker.addOnPositiveButtonClickListener(new MaterialPickerOnPositiveButtonClickListener<Long>() {
    @Override
    public void onPositiveButtonClick(Long selection) {

        calendar.setTimeInMillis(selection);
    
         SimpleDateFormat simpleDateFormat = new 
             SimpleDateFormat("dd/MM//yyyy",Locale.getDefault());
                
          txtBirthDate.setText(simpleDateFormat.format(calendar.getTime()));

    }
});

The calendar instance, now with timeInMillis has the selected date, only a day before. How can I fix this?

3

There are 3 best solutions below

1
On

【Working solution in 2022】

It turns out that the MaterialDatePicker uses UTC time format, which means it doesn't contain any Time Zone information, as the official states here.

That means we have to do some conversion work between UTC date-time and the zone date-time, the idea would be using the LocalDateTime api:

1. For showing correct date on picker, we need to convert zone date-time to UTC date-time:

Zone date-time (Long) -> zone LocalDateTime (object) -> UTC date-time (object)-> UTC date-time (Long).

2. For getting zone date-time from picker, we need to convert UTC date-time to zone date-time:

UTC date-time (Long) -> UTC LocalDateTime (object) -> zone date-time (object) -> zone date-time (Long)

  1. Create Helper Extension Functions:

    fun Long.toLocalDateTime() = LocalDateTime.ofInstant(Instant.ofEpochMilli(this), ZoneId.systemDefault())
    
    fun Long.toUTCLocalDateTime() = LocalDateTime.ofInstant(Instant.ofEpochMilli(this), ZoneId.ofOffset("UTC", ZoneOffset.UTC))
    
  2. Show MaterialDatePicker in Fragment:

    private fun showDatePicker() {
        val datePicker = MaterialDatePicker.Builder.datePicker().run {
            setTitleText("Select Date")
            setSelection(viewModel.dateLong.value.toLocalDateTime().atZone(ZoneId.ofOffset("UTC", ZoneOffset.UTC)).toInstant().toEpochMilli())
            //setSelection(MaterialDatePicker.todayInUtcMilliseconds()) //show today's date
            build()
        }
    
        datePicker.addOnPositiveButtonClickListener {
            val dateLong = datePicker.selection!!.toUTCLocalDateTime().atZone(ZoneId.systemDefault()).toInstant().toEpochMilli() 
    
            viewModel.updateDateLong(dateLong)       
        }
    
         datePicker.show(childFragmentManager, "")
     }
    

Demo: https://youtu.be/9YWr3fgQ214

0
On

This is because DatePicker indexes the months from 0 override this method

override fun onDateSet(view: DatePicker?, year: Int, month: Int, dayOfMonth: Int) {
    datePickerListener.onDateSet(view,year,month+1,dayOfMonth)
}
0
On

The only solution that worked for me was converting the desired timestamp to LocalDateTime first, then truncating to DAYS and finally converting it to UTC like following:

val selection = ZonedDateTime.now()
    .toLocalDateTime()
    .truncatedTo(ChronoUnit.DAYS)
    .toInstant(ZoneOffset.UTC)
    .toEpochMilli()

val picker = MaterialDatePicker.Builder.datePicker()
    .setSelection(selection)
    //...
    .build()

Then you can get the picked date as follows:

picker.addOnPositiveButtonClickListener { selection ->
    val selectedDate = Instant.ofEpochMilli(selection)
        .atZone(ZoneId.systemDefault())

    //...
}