基于Java 8 java.time按Locale设置周首日的日历打印问题咨询
疑问1:当前硬编码方案是否合理
不合理,存在明显缺陷:
- 仅适配周首日为周日的Locale,一旦切换到周首日为周一的地区(如中国大部分地区、欧洲各国),日历展示逻辑完全失效
- 自定义
DaysOfWeek枚举属于重复造轮子,java.time自带的DayOfWeek枚举已经覆盖所有星期场景,还支持原生本地化能力 - 补全前后月日期的逻辑中硬编码了
DayOfWeek.SATURDAY、DayOfWeek.SUNDAY作为边界判断条件,同样不支持多Locale适配
疑问2:如何通过java.time API原生实现需求
核心是正确使用WeekFields的两个能力:获取当前Locale的周首日、按本地化规则排序星期序列,具体改造步骤如下:
步骤1:删除自定义枚举,生成本地化星期排序序列
直接基于WeekFields生成符合当前Locale的星期顺序列表,无需硬编码:
val weekFields = WeekFields.of(Locale.getDefault()) // 生成本地化排序的星期列表,比如Locale.US是[周日,周一...周六],Locale.CHINA是[周一,周二...周日] val sortedDaysOfWeek = generateSequence(weekFields.firstDayOfWeek) { it.plus(1) }.take(7).toList()
步骤2:改造getDaysOfMonth的边界判断逻辑
移除硬编码的星期边界,全部用weekFields的规则替换:
fun getDaysOfMonth(year: Int, month: Month): Map<DayOfWeek, MutableList<LocalDate>> { val weekFields = WeekFields.of(Locale.getDefault()) val firstDayOfWeek = weekFields.firstDayOfWeek val lastDayOfWeek = firstDayOfWeek.minus(1) val ym = YearMonth.of(year, month) val firstOfMonth = ym.atDay(1) val lastDayOfMonth = ym.atDay(ym.lengthOfMonth()) val dayDates = DayOfWeek.values().associateWith { mutableListOf<LocalDate>() }.toMutableMap() // 填充当月日期 DayOfWeek.values().forEach { day -> var ld = firstOfMonth.with(TemporalAdjusters.dayOfWeekInMonth(1, day)) do { dayDates[day]?.add(ld) ld = ld.plusWeeks(1) } while (YearMonth.from(ld) == ym) } // 补全上月日期 if (firstOfMonth.dayOfWeek != firstDayOfWeek) { val prevMonth = ym.minusMonths(1) var currentDate = prevMonth.atEndOfMonth() do { dayDates[currentDate.dayOfWeek]?.add(0, currentDate) currentDate = currentDate.minusDays(1) } while (currentDate.dayOfWeek != lastDayOfWeek) } // 补全下月日期 if (lastDayOfMonth.dayOfWeek != lastDayOfWeek) { val nextMonth = ym.plusMonths(1) var currentDate = nextMonth.atDay(1) do { dayDates[currentDate.dayOfWeek]?.add(currentDate) currentDate = currentDate.plusDays(1) } while (currentDate.dayOfWeek != firstDayOfWeek) } return dayDates }
步骤3:改造渲染逻辑,移除硬编码排序
直接用之前生成的本地化星期序列读取数据即可,不需要自定义Map排序:
@Composable fun TimetableCalendar() { val weekFields = WeekFields.of(Locale.getDefault()) val sortedDaysOfWeek = generateSequence(weekFields.firstDayOfWeek) { it.plus(1) }.take(7).toList() val dates = getDaysOfMonth(LocalDate.now().year, LocalDate.now().month) LazyVerticalGrid( cells = GridCells.Fixed(7), contentPadding = PaddingValues(16.dp) ) { // 展示本地化的星期缩写,自动适配系统语言 items(sortedDaysOfWeek) { dayOfWeek -> Text( text = dayOfWeek.getDisplayName(TextStyle.SHORT, Locale.getDefault()), textAlign = TextAlign.Center ) } // 按本地化顺序展示日期 items(sortedDaysOfWeek) { dayOfWeek -> Column( modifier = Modifier.padding(4.dp) ) { dates[dayOfWeek]?.forEach { day -> DateView(date = day.dayOfMonth.toString()) } } } } }
改造后的代码完全适配任意Locale的周首日规则,同时支持星期名称的本地化展示,不需要任何硬编码。
疑问3:是否需要切回旧Calendar API
完全不需要。java.time是官方指定的Calendar API替代方案,解决了旧API类型不安全、线程不安全、API设计混乱等大量问题,功能覆盖度远高于旧Calendar,仅用原生API就能实现所有日历相关需求,完全没有必要使用已被废弃的旧API。
内容的提问来源于stack exchange,提问作者user15187712
相关产品推荐
相关产品推荐

