使用NumberPicker展示日期时遇LocalDate日期值无效异常的求助
解决LocalDate转换时的DayOfMonth无效值异常
问题场景
使用NumberPicker展示日期,核心代码如下:
val numberPicker = binding.numberPicker val values = getArrayDates(date.toString()) //EventsBottomSheet.kt:45 numberPicker.minValue = 0 numberPicker.maxValue = values.size - 1 numberPicker.displayedValues = values.toArray(arrayOfNulls<String>(values.size)) numberPicker.wrapSelectorWheel = true numberPicker.setOnValueChangedListener { _, _, newVal -> binding.dateText.text = newVal.toString() }
getArrayDates方法实现:
private fun getArrayDates(referenceDate : String) : ArrayList<String>{ val datesList = referenceDate.split("/") val localeDate = LocalDate.of(datesList[0].toInt(), Month.of(datesList[1].toInt()), datesList[2].toInt()) //EventsBottomSheet.kt:65 (related with exception) val monthsToDisplay = listOf(localeDate.minusMonths(1), localeDate, localeDate.plusMonths(1)) val daysList = ArrayList<String>() val formatter = DateTimeFormatter.ofLocalizedDate(FormatStyle.LONG) for (month in monthsToDisplay) { val yearMonth = YearMonth.from(month) for (day in 1..yearMonth.lengthOfMonth()) { daysList.add(yearMonth.atDay(day).format(formatter)) } } return daysList }
观测到date值格式为"21/05/2023"(日/月/年),转换为LocalDate时抛出异常:
FATAL EXCEPTION: main Process: com.ahmetkaan.kediy, PID: 19391 j$.time.DateTimeException: Invalid value for DayOfMonth (valid values 1 - 28/31): 2023 at j$.time.temporal.ValueRange.checkValidValue(ValueRange.java:311) at j$.time.temporal.ChronoField.checkValidValue(ChronoField.java:713) at j$.time.LocalDate.of(LocalDate.java:250) at com.ahmetkaan.kediy.EventsBottomSheet.getArrayDates(EventsBottomSheet.kt:65) ...
已排除前导零、日期合法性问题(date在之前Fragment中由LocalDate定义)。
异常原因
异常提示DayOfMonth传入了2023,核心问题是参数顺序完全颠倒:
LocalDate.of(year, month, dayOfMonth)的参数顺序是「年、月、日」- 但拆分
"21/05/2023"后,datesList[0]是日(21)、datesList[1]是月(05)、datesList[2]是年(2023),你却把年份传给了dayOfMonth参数,触发无效值异常。
解决方案
方案1:直接修正参数顺序
调整LocalDate.of的参数,匹配日/月/年的格式:
// 修正参数顺序:年(datesList[2])、月(datesList[1])、日(datesList[0]) val localeDate = LocalDate.of(datesList[2].toInt(), Month.of(datesList[1].toInt()), datesList[0].toInt())
方案2:用DateTimeFormatter解析(更规范)
手动拆分字符串容易出错,推荐用格式化器直接解析符合格式的字符串:
private fun getArrayDates(referenceDate : String) : ArrayList<String>{ // 定义日/月/年的格式器 val formatter = DateTimeFormatter.ofPattern("dd/MM/yyyy") val localeDate = LocalDate.parse(referenceDate, formatter) val monthsToDisplay = listOf(localeDate.minusMonths(1), localeDate, localeDate.plusMonths(1)) val daysList = ArrayList<String>() val displayFormatter = DateTimeFormatter.ofLocalizedDate(FormatStyle.LONG) for (month in monthsToDisplay) { val yearMonth = YearMonth.from(month) for (day in 1..yearMonth.lengthOfMonth()) { daysList.add(yearMonth.atDay(day).format(displayFormatter)) } } return daysList }
方案3:直接传递LocalDate(最优)
既然date本身就是LocalDate类型,完全没必要转成字符串再解析,直接传LocalDate能彻底避免格式问题:
// 调用时直接传LocalDate val values = getArrayDates(date) // 修改方法参数为LocalDate private fun getArrayDates(referenceDate : LocalDate) : ArrayList<String>{ val monthsToDisplay = listOf(referenceDate.minusMonths(1), referenceDate, referenceDate.plusMonths(1)) val daysList = ArrayList<String>() val formatter = DateTimeFormatter.ofLocalizedDate(FormatStyle.LONG) for (month in monthsToDisplay) { val yearMonth = YearMonth.from(month) for (day in 1..yearMonth.lengthOfMonth()) { daysList.add(yearMonth.atDay(day).format(formatter)) } } return daysList }
内容的提问来源于stack exchange,提问作者Ahmet Kaan
相关产品推荐
相关产品推荐

