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

日期计算需排除周末,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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.20 10:40:41