在Swift中如何获取指定日期范围内特定星期几的日期列表
实现方案
核心工具方法封装
首先通过Calendar系统API实现日期范围遍历、星期匹配能力,新增Date扩展如下:
import SwiftUI extension Date { /// 获取指定日期间隔内,匹配指定星期几的所有日期 /// - Parameters: /// - range: 统计日期区间 /// - matchingWeekdays: 要匹配的星期几集合,iOS系统默认1=周日、2=周一...7=周六 /// - Returns: 匹配的日期数组 static func getDates(in range: ClosedRange<Date>, matchingWeekdays: Set<Int>) -> [Date] { let calendar = Calendar.current var result: [Date] = [] var currentDate = calendar.startOfDay(for: range.lowerBound) let endDate = calendar.startOfDay(for: range.upperBound) while currentDate <= endDate { let weekday = calendar.component(.weekday, from: currentDate) if matchingWeekdays.contains(weekday) { result.append(currentDate) } currentDate = calendar.date(byAdding: .day, value: 1, to: currentDate)! } return result } /// 自动计算扣除固定休息日、法定节假日后的基础工作日数 static func calculateBasicWorkDays( start: Date, end: Date, restWeekdays: Set<Int>, publicHolidays: Set<Date> ) -> Int { // 先筛掉自定义固定休息日 let candidateDays = getDates(in: start...end, matchingWeekdays: Set(1...7).subtracting(restWeekdays)) // 再排除法定节假日 let validDays = candidateDays.filter { day in !publicHolidays.contains(Calendar.current.startOfDay(for: day)) } return validDays.count } }
优化后页面代码
在原有代码基础上新增日期选择、休息日配置、节假日配置模块,自动计算基础工作日数,完整代码如下:
struct ContentView: View { // 原有状态变量 @State private var basicDays = "" @State private var lateEntry = "" @State private var absentDays = "" @State private var otHours = "" @State private var midNights = "" @State private var nightShifts = "" @State private var medAllowance = "" @State private var dayOffOTHours = "" @State private var pHolidayBasicHours = "" @State private var pHolidayOTHours = "" // 新增自动计算工作日相关状态 @State private var startDate = Date() @State private var endDate = Calendar.current.date(byAdding: .month, value: 1, to: Date())! // 默认休息日为周日(1)、周六(7) @State private var restWeekdays: Set<Int> = [1,7] @State private var publicHolidays: Set<Date> = [] // 临时节假日选择变量 @State private var tempHoliday = Date() private let weekdayText = ["周日", "周一", "周二", "周三", "周四", "周五", "周六"] var netWage: Double { // 原有计算逻辑保持不变 let monthlyBasic = 1200.00 let dailyRate = monthlyBasic / 26 let hourlyRate = dailyRate / 8 let otRate = hourlyRate * 1.5 let dayOffRate = hourlyRate * 2 let publicHolidayRate = hourlyRate * 2 let publicHolidayOTRate = hourlyRate * 3 let basicDaysAmount = dailyRate * (Double(basicDays) ?? 0) let lateEntryAmount = hourlyRate * (Double(lateEntry) ?? 0) let absentDaysAmount = dailyRate * (Double(absentDays) ?? 0) let midNightAmount = 10 * (Double(midNights) ?? 0) let nightShiftAmount = 15 * (Double(nightShifts) ?? 0) let dayOffOTAmount = dayOffRate * (Double(dayOffOTHours) ?? 0) let publicHolidayAmount = (publicHolidayRate * (Double(pHolidayBasicHours) ?? 0)) + (publicHolidayOTRate * (Double(pHolidayOTHours) ?? 0)) let totalOTAmount = otRate * (Double(otHours) ?? 0) let holidayOTAmount = dayOffOTAmount + publicHolidayAmount let otAmount = totalOTAmount + holidayOTAmount let totalDeduction = lateEntryAmount + absentDaysAmount let allowances = midNightAmount + nightShiftAmount let totalWage = basicDaysAmount + allowances + otAmount let netWage = totalWage - totalDeduction return netWage } // 自动更新基础工作日数 private func updateBasicDays() { let count = Date.calculateBasicWorkDays( start: startDate, end: endDate, restWeekdays: restWeekdays, publicHolidays: publicHolidays ) basicDays = "\(count)" } var body: some View { NavigationView { Form { // 新增工作日配置分组 Group { Section(header: Text("统计周期选择")) { DatePicker("起始日期", selection: $startDate, displayedComponents: .date) DatePicker("结束日期", selection: $endDate, displayedComponents: .date) } Section(header: Text("自定义固定休息日")) { ForEach(1..<8) { index in HStack { Text(weekdayText[index-1]) Spacer() if restWeekdays.contains(index) { Image(systemName: "checkmark") .foregroundColor(.blue) } } .contentShape(Rectangle()) .onTapGesture { if restWeekdays.contains(index) { restWeekdays.remove(index) } else { restWeekdays.insert(index) } updateBasicDays() } } } Section(header: Text("法定节假日配置")) { DatePicker("新增节假日", selection: $tempHoliday, displayedComponents: .date) Button("添加") { let holiday = Calendar.current.startOfDay(for: tempHoliday) publicHolidays.insert(holiday) updateBasicDays() } ForEach(Array(publicHolidays.sorted()), id: \.self) { date in HStack { Text(date.formatted(date: .abbreviated, time: .omitted)) Spacer() Button("删除") { publicHolidays.remove(date) updateBasicDays() } .foregroundColor(.red) } } } Section(header: Text("自动计算基础工作日数")) { Text(basicDays) .onAppear(perform: updateBasicDays) .onChange(of: startDate) { _ in updateBasicDays() } .onChange(of: endDate) { _ in updateBasicDays() } } } // 原有输入项保持不变 Group { Section(header: Text("输入迟到总时长(小时)")) { TextField("", text: $lateEntry) .keyboardType(.decimalPad) } Section(header: Text("输入缺勤天数")) { TextField("", text: $absentDays) .keyboardType(.numberPad) } Section(header: Text("输入平时加班总时长")) { TextField("", text: $otHours) .keyboardType(.numberPad) } Section(header: Text("输入夜班(过零点)次数")) { TextField("", text: $midNights) .keyboardType(.numberPad) } Section(header: Text("输入夜班总次数")) { TextField("", text: $nightShifts) .keyboardType(.numberPad) } Section(header: Text("输入医疗报销金额")) { TextField("", text: $medAllowance) .keyboardType(.decimalPad) } Section(header: Text("输入休息日加班总时长")) { TextField("", text: $dayOffOTHours) .keyboardType(.numberPad) } Section(header: Text("输入法定节假日正常上班总时长")) { TextField("", text: $pHolidayBasicHours) .keyboardType(.numberPad) } Section(header: Text("输入法定节假日加班总时长")) { TextField("", text: $pHolidayOTHours) .keyboardType(.numberPad) } } Section(header: Text("实发工资")) { Text("\(netWage, specifier: "%.2f")") } } .navigationBarTitle("工资计算器") } } } struct ContentView_Previews: PreviewProvider { static var previews: some View { ContentView() } }
功能说明
- 核心方法
getDates直接返回指定日期范围内对应星期几的日期数组,满足基础需求 - 自动计算逻辑会在修改统计周期、休息日、节假日时实时更新基础工作日数,不需要用户手动输入
- 星期匹配规则遵循系统默认:1代表周日,2代表周一,依次到7代表周六,可根据需求自行调整顺序
内容的提问来源于stack exchange,提问作者BangaliBabu
相关产品推荐
相关产品推荐

