Flutter DateTime计算10月天数异常显示为32天问题
问题根因
该异常和Flutter内置DateTime对象无关,是业务代码两处边界逻辑漏洞叠加导致,仅10月触发是因为北半球多数国家10月为夏令时结束节点,刚好命中bug触发条件:
- 第一处漏洞:计算当月天数时,未将DateTime的day参数重置为1,直接复用
actualDate.day构造日期实例。当actualDate存储的日期day值大于目标月份总天数时,Dart DateTime会自动向后进位到下一个月,此时调用getDaysInMonth的实例实际已经不属于目标月份。 - 第二处漏洞:大概率
getDaysInMonth扩展方法通过下月1号.difference(当月1号).inDays的时间差方式计算天数,该实现受夏令时影响极大:10月夏令时结束时钟会回拨1小时,当月总时长比普通月份多1小时,一旦计算时混用本地时间/UTC时间、或起止时间偏移,就会多算出1天得到32的错误结果。
其余月份显示正常是因为没有夏令时切换,且非月末切换时不会触发日期进位,刚好掩盖了逻辑漏洞。
修复方案
- 首先重写
getDaysInMonth扩展,放弃时间差计算逻辑,利用Dart DateTime「day传0自动返回上月最后一天」的原生特性实现,完全不受时区、夏令时影响:
extension DateTimeExtension on DateTime { /// 获取当月总天数,兼容所有时区、夏令时场景 int get getDaysInMonth { // 构造下个月第0天,自动返回当月最后一天,直接读取day属性即可 return DateTime(year, month + 1, 0).day; } }
- 修正日历核心日期计算逻辑,构造月份相关DateTime实例时固定传入day=1,从根源避免日期进位问题:
@override Widget build(BuildContext context) { final userModelData = Provider.of<UserModel>(context); final userModel = userModelData.items; final dateModelData = Provider.of<DateModel>(context); final actualDate = dateModelData.getActualSavedDate!; final anzahlColumns = userModel.length; // 固定以当月1号为基准构造日期,杜绝day溢出进位问题 final firstDayOfCurrentMonth = DateTime(actualDate.year, actualDate.month, 1); // 读取修正后的当月天数,不会再出现32天的异常值 final dateMonth = firstDayOfCurrentMonth.getDaysInMonth; // 当月1号的星期值,用于计算月份起始偏移 final wochenTag = firstDayOfCurrentMonth; List<User> listeColumns() { List<User> gesamteColumns = []; for (int d = 1; d <= anzahlColumns; d++) { gesamteColumns.add(userModel[d - 1]); } return gesamteColumns; } List<String> listeRows() { List<String> gesamteRows = []; for (int d = 1; d <= dateMonth; d++) { // 用当日日期实例做格式化,避免actualDate进位导致格式化结果错误 final currentDay = DateTime(actualDate.year, actualDate.month, d); gesamteRows.add(currentDay.format('$d')); } return gesamteRows; } // ...其余原有代码
验证说明
修复后打印10月的dateMonth值会稳定返回31,切换到任意年份、任意月份都不会出现天数异常。如果切换月份时需要保留用户此前选中的日期day,需要单独做边界判断:若目标月份总天数小于已存储的day值,自动将选中日置为目标月份最后一天即可。
内容的提问来源于stack exchange,提问作者Pauer Auer
相关产品推荐
相关产品推荐

