You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Flutter中如何计算指定时间段内各星期几的对应天数

Flutter 实现指定时间段内各星期几天数统计(适配订阅计费场景)

不需要引入第三方依赖,直接用Dart内置的DateTime能力就能实现,核心逻辑是遍历时间区间内的所有自然日,按星期维度分组计数,最后匹配对应单日计费标准核算总金额。

核心实现步骤

  • 预处理时间边界:统一把起止时间的时、分、秒、毫秒、微秒清零,只保留年月日维度,避免时间精度、时区偏移导致的日期统计错误
  • 逐天遍历时间区间:从起始日期开始每次往后累加1天,直到日期超过结束日期,Dart内置的日期累加逻辑会自动处理跨月、跨年、闰年的日期进位
  • 按星期维度分组计数:遍历过程中通过DateTime.weekday属性获取当日对应的星期值,对应计数+1。Dart中该属性取值为1到7,分别对应周一到周日,符合ISO 8601标准
  • 核算总金额:拿到各星期几的实际天数后,和对应单日单价相乘求和,得到最终订单金额

可直接复用的工具代码

import 'package:flutter/foundation.dart';

/// 统计指定时间区间内各星期几的天数
/// [startDate] 区间开始日期(方法内部会自动清零时分秒精度)
/// [endDate] 区间结束日期(统计时包含结束当日,方法内部会自动清零时分秒精度)
/// 返回Map的key为星期值(1=周一,2=周二...7=周日),value为对应天数
Map<int, int> countWeekdaysInRange(DateTime startDate, DateTime endDate) {
  // 初始化周一到周日的默认计数为0
  final weekdayCount = <int, int>{
    1: 0,
    2: 0,
    3: 0,
    4: 0,
    5: 0,
    6: 0,
    7: 0,
  };

  // 统一处理为纯日期格式,消除时分秒带来的精度误差
  DateTime currentDate = DateTime(startDate.year, startDate.month, startDate.day);
  final targetEndDate = DateTime(endDate.year, endDate.month, endDate.day);

  // 异常边界处理:开始日期晚于结束日期时直接返回初始0计数
  if (currentDate.isAfter(targetEndDate)) {
    return weekdayCount;
  }

  // 逐天遍历统计
  while (!currentDate.isAfter(targetEndDate)) {
    weekdayCount[currentDate.weekday] = weekdayCount[currentDate.weekday]! + 1;
    // 日期往后推1天
    currentDate = currentDate.add(const Duration(days: 1));
  }

  return weekdayCount;
}

/// 基于星期天数统计结果计算订阅总金额
/// [weekdayCount] 各星期几的天数统计结果
/// [weekdayPriceConfig] 各星期几对应的单日计费标准,key规则同星期计数(1=周一...7=周日)
double calculateSubscriptionTotal(Map<int, int> weekdayCount, Map<int, double> weekdayPriceConfig) {
  double totalAmount = 0;
  weekdayCount.forEach((weekday, dayNum) {
    // 未配置单价的星期默认按0计费,可根据业务规则调整默认值
    final singleDayPrice = weekdayPriceConfig[weekday] ?? 0;
    totalAmount += dayNum * singleDayPrice;
  });
  return totalAmount;
}

调用示例

void testSubscriptionCalc() {
  // 示例:订阅周期为2024年6月1日至2024年6月10日
  final subscribeStart = DateTime(2024, 6, 1);
  final subscribeEnd = DateTime(2024, 6, 10);

  // 统计周期内各星期几的天数
  final dayCountResult = countWeekdaysInRange(subscribeStart, subscribeEnd);
  debugPrint('各星期天数统计:$dayCountResult');
  // 上述示例输出为:{1:2, 2:2, 3:1, 4:1, 5:1, 6:1, 7:2}

  // 配置计费规则:工作日(周一至周五)8元/天,周末(周六、周日)12元/天
  final priceRule = <int, double>{
    1: 8,
    2: 8,
    3: 8,
    4: 8,
    5: 8,
    6: 12,
    7: 12,
  };

  // 计算总金额
  final orderTotal = calculateSubscriptionTotal(dayCountResult, priceRule);
  debugPrint('订单应付总金额:$orderTotal 元');
  // 上述示例计算结果为:7*8 + 3*12 = 92元
}

落地注意事项

  • 不要为了精简代码用「总天数整除7算整周计数+剩余天数补算」的取模逻辑,这种方案在跨夏令时、时间带时分秒精度、跨闰年2月时很容易出现计数偏差。逐天遍历的性能完全足够——哪怕统计10年的周期也就循环3600多次,对Flutter来说几乎无性能开销,稳定性最高。
  • 如果业务定义周日为一周的第一天,不需要修改底层计数逻辑,只需要在配置计费规则、做星期文案映射时调整key和文案的对应关系即可,DateTime.weekday的原生取值规则是固定的。
  • 如果业务需要支持多时区,先把传入的起止时间转换为用户对应时区的DateTime对象,再做日期精度清零处理,避免跨时区导致的日期偏移。

内容的提问来源于stack exchange,提问作者Diificult Ranger

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.29 06:18:21