日期计算需排除周末,DateComponentsFormatter无原生支持求替代方案
Great question! DateComponentsFormatter is indeed a fantastic tool for formatting date durations across languages, regions, and calendars—but it’s true that it doesn’t natively support excluding specific dates like weekends. Let’s walk through some solid solutions, both native and third-party, that can handle this requirement:
Native Implementation Options
If you want to avoid third-party dependencies, you can build this logic yourself using Apple’s existing Foundation APIs.
1. Manual Workday Counting (Simple Approach)
The core idea is to iterate through each day between your start and end dates, filter out weekends (or any custom excluded dates), then calculate the duration based on the valid workdays. You can still use DateComponentsFormatter to handle the final formatting.
Here’s a Swift example to get you started:
func calculateWorkdayDuration(from startDate: Date, to endDate: Date) -> DateComponents? { let calendar = Calendar.current var currentDate = startDate var totalWorkSeconds: TimeInterval = 0 while currentDate <= endDate { let dayStart = calendar.startOfDay(for: currentDate) let dayEnd = calendar.date(byAdding: .day, value: 1, to: dayStart)! // Skip weekends (respects regional calendar rules automatically) let isWeekend = calendar.isDateInWeekend(currentDate) if !isWeekend { // Calculate the actual time contribution for this day let dayStartClamped = max(currentDate, dayStart) let dayEndClamped = min(endDate, dayEnd) totalWorkSeconds += dayEndClamped.timeIntervalSince(dayStartClamped) } currentDate = dayEnd } return calendar.dateComponents([.day, .hour, .minute, .second], from: Date(), to: Date().addingTimeInterval(totalWorkSeconds)) } // Usage with DateComponentsFormatter let formatter = DateComponentsFormatter() formatter.unitsStyle = .full formatter.allowedUnits = [.day, .hour, .minute] if let workComponents = calculateWorkdayDuration(from: startDate, to: endDate) { if let durationString = formatter.string(from: workComponents) { print(durationString) // e.g. "2 days, 5 hours, 30 minutes" } }
This approach gives you full control over which dates to exclude (you can easily add a check for custom holidays too) and works across all Apple platforms. The only downside is minor performance overhead for very large date ranges, but it’s negligible for most use cases.
2. Optimized Calendar Enumeration
For iOS 10+/macOS 10.12+, you can use Calendar.enumerateDates to iterate more efficiently without manual loops:
func calculateWorkdayDurationOptimized(from startDate: Date, to endDate: Date) -> DateComponents? { let calendar = Calendar.current var totalWorkSeconds: TimeInterval = 0 calendar.enumerateDates(startingAfter: calendar.startOfDay(for: startDate), matching: DateComponents(hour: 0), matchingPolicy: .nextTime) { date, _, stop in guard let currentDay = date, currentDay <= endDate else { stop = true return } let dayStart = currentDay let dayEnd = calendar.date(byAdding: .day, value: 1, to: dayStart)! if !calendar.isDateInWeekend(currentDay) { let dayStartClamped = max(startDate, dayStart) let dayEndClamped = min(endDate, dayEnd) totalWorkSeconds += dayEndClamped.timeIntervalSince(dayStartClamped) } } // Don't forget the start day itself if it's a workday and has partial time let startDay = calendar.startOfDay(for: startDate) if !calendar.isDateInWeekend(startDay) && startDate > startDay { let dayEnd = calendar.date(byAdding: .day, value: 1, to: startDay)! let clampedEnd = min(endDate, dayEnd) totalWorkSeconds += clampedEnd.timeIntervalSince(startDate) } return calendar.dateComponents([.day, .hour, .minute], from: Date(), to: Date().addingTimeInterval(totalWorkSeconds)) }
This method is more efficient for large date ranges and leverages Apple’s optimized calendar APIs.
Third-Party Library Solutions
If you’d rather skip building the logic from scratch, there are mature libraries that handle workday calculations out of the box:
1. DateToolsSwift
A popular extension to Apple’s Date API that includes built-in workday counting, holiday exclusion, and more. It plays nicely with DateComponentsFormatter for formatting:
import DateToolsSwift let startDate = Date() let endDate = startDate.addingTimeInterval(10 * 24 * 3600) // Calculate workdays excluding weekends let workdayInterval = startDate.dateInterval(to: endDate, excludingWeekends: true) // Use DateComponentsFormatter to format the interval let formatter = DateComponentsFormatter() formatter.unitsStyle = .short if let durationString = formatter.string(from: workdayInterval) { print(durationString) }
You can also pass a custom array of excluded dates (like holidays) to the calculation methods.
2. SwiftDate
Another feature-rich date handling library with robust workday support, timezone management, and localization. Example usage:
import SwiftDate let startDate = Date() let endDate = startDate + 10.days // Calculate workday components excluding weekends let workComponents = Calendar.current.dateComponents([.day, .hour], from: startDate, to: endDate, excludingWeekends: true) // Format with DateComponentsFormatter let formatter = DateComponentsFormatter() formatter.unitsStyle = .full if let durationString = formatter.string(from: workComponents) { print(durationString) }
Key Notes
- Regional Weekend Rules: Don’t hardcode weekend days (like Saturday/Sunday)—use
Calendar.isDateInWeekend(_:)to respect the user’s regional calendar settings (some regions observe Friday/Saturday as weekends). - Holiday Exclusion: For either native or third-party solutions, you’ll need to maintain a list of holiday dates relevant to your user base, then add an extra check to exclude those days during calculation.
- Precise Time Calculations: If you need to account for partial days (e.g., starting at 2 PM on a workday and ending at 10 AM the next), make sure your logic calculates the exact time difference for each valid day instead of just counting full days.
内容的提问来源于stack exchange,提问作者Stephen

