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

如何在FSCalendar中禁用过去日期?附当前实现代码

嘿,我来帮你搞定禁用过去日期的需求!其实思路很简单——我们只需要把今天之前的所有日期都加入到你的禁用日期列表里,同时保留原来按星期几禁用的逻辑就可以了。下面是具体的修改步骤和代码:

核心思路

  1. 先获取「今天」的UTC日期(和你现有代码里的日期格式保持一致,避免时区坑)
  2. 生成当月第一天到「昨天」的所有日期,把这些日期也加入禁用数组
  3. 最后对禁用数组去重,防止重复添加(比如原来的星期禁用逻辑可能已经包含了某些过去日期)

修改后的完整代码

我直接在你原有的getUserSelectedDates函数末尾添加处理过去日期的逻辑,同时调整了一些细节让代码更健壮:

func getUserSelectedDates(_ arrWeekDay: [Int], calender calenderVW: FSCalendar?) -> NSMutableArray {
    let arrUnAvailibilityDates = NSMutableArray()
    let currentDate: Date? = calenderVW?.currentPage // 获取日历当前页面日期
    let gregorianCalendar = Calendar.init(identifier: .gregorian)
    
    // 获取当前日期的年、月、日、星期组件
    var components: DateComponents? = nil
    if let aDate = currentDate {
        components = gregorianCalendar.dateComponents([.year, .month, .day, .weekday], from: aDate)
        print(aDate)
    }
    
    // 将日期组件的日设为1(当月第一天),并清零时间组件
    components?.day = 1
    components?.hour = 0
    components?.minute = 0
    components?.second = 0
    
    // 获取当月第一天的日期
    var firstDateOfCurMonth: Date? = nil
    if let aComponents = components {
        firstDateOfCurMonth = gregorianCalendar.date(from: aComponents)
    }
    
    // 获取当月第一天的星期组件
    var newcomponents: DateComponents? = nil
    if let aMonth = firstDateOfCurMonth {
        newcomponents = gregorianCalendar.dateComponents([.year, .month, .day, .weekday], from: aMonth)
    }
    let firstDateWeekDay: Int? = newcomponents?.weekday
    
    // 获取当月最后一天的日期
    let curMonth: Int? = newcomponents?.month
    newcomponents?.month = (curMonth ?? 0) + 1
    var templastDateOfCurMonth: Date? = nil
    if let aNewcomponents = newcomponents {
        templastDateOfCurMonth = gregorianCalendar.date(from: aNewcomponents)?.addingTimeInterval(-1)
    }
    // 下个月开始前一秒即为当月最后一刻
    
    var lastcomponents: DateComponents? = nil
    if let aMonth = templastDateOfCurMonth {
        lastcomponents = gregorianCalendar.dateComponents([.year, .month, .day, .weekday], from: aMonth)
    }
    lastcomponents?.hour = 0
    lastcomponents?.minute = 0
    lastcomponents?.second = 0
    
    var lastDateOfCurMonth: Date? = nil
    if let aLastcomponents = lastcomponents {
        lastDateOfCurMonth = gregorianCalendar.date(from: aLastcomponents)
    }
    
    var dayDifference = DateComponents()
    dayDifference.calendar = gregorianCalendar
    
    if arrWeekDay.count == 0 {
    } else if arrWeekDay.count == 1 {
        let wantedWeekDay = Int(arrWeekDay[0])
        var firstWeekDateOfCurMonth: Date? = nil
        
        if wantedWeekDay == firstDateWeekDay {
            firstWeekDateOfCurMonth = firstDateOfCurMonth
        } else {
            var day: Int = wantedWeekDay - firstDateWeekDay!
            if day < 0 {
                day += 7
            }
            day += 1
            components?.day = day
            firstWeekDateOfCurMonth = gregorianCalendar.date(from: components!)
        }
        
        var weekOffset: Int = 0
        var nextDate: Date? = firstWeekDateOfCurMonth
        repeat {
            let strDT: String = getSmallFormatedDate(convertCalendarDate(toNormalDate: nextDate))!
            arrUnAvailibilityDates.add(strDT)
            weekOffset += 1
            dayDifference.weekOfYear = weekOffset
            var date: Date? = nil
            if let aMonth = firstWeekDateOfCurMonth {
                date = gregorianCalendar.date(byAdding: dayDifference, to: aMonth)
            }
            nextDate = date
        } while nextDate?.compare(lastDateOfCurMonth!) == .orderedAscending || nextDate?.compare(lastDateOfCurMonth!) == .orderedSame
    } else {
        for i in 0..<arrWeekDay.count {
            let wantedWeekDay = Int(arrWeekDay[i])
            var firstWeekDateOfCurMonth: Date? = nil
            
            if wantedWeekDay == firstDateWeekDay {
                firstWeekDateOfCurMonth = firstDateOfCurMonth
            } else {
                var day: Int = wantedWeekDay - firstDateWeekDay!
                if day < 0 {
                    day += 7
                }
                day += 1
                components?.day = day
                firstWeekDateOfCurMonth = gregorianCalendar.date(from: components!)
            }
            
            var weekOffset: Int = 0
            var nextDate: Date? = firstWeekDateOfCurMonth
            repeat {
                let strDT = getSmallFormatedDate(convertCalendarDate(toNormalDate: nextDate))
                arrUnAvailibilityDates.add(strDT!)
                weekOffset += 1
                dayDifference.weekOfYear = weekOffset
                var date: Date? = nil
                if let aMonth = firstWeekDateOfCurMonth {
                    date = gregorianCalendar.date(byAdding: dayDifference, to: aMonth)
                }
                nextDate = date
            } while nextDate?.compare(lastDateOfCurMonth!) == .orderedAscending || nextDate?.compare(lastDateOfCurMonth!) == .orderedSame
        }
    }
    
    // -------------------------- 新增:禁用过去日期的逻辑 --------------------------
    guard let firstDay = firstDateOfCurMonth else {
        return arrUnAvailibilityDates
    }
    
    // 获取今天的UTC日期(和现有代码格式一致)
    let today = Date()
    guard let utcTodayString = getSmallFormatedDate(today),
          let utcToday = DateFormatter.utcDateFormatter.date(from: utcTodayString) else {
        return arrUnAvailibilityDates
    }
    
    // 遍历从当月第一天到昨天的所有日期
    var currentLoopDate = firstDay
    while currentLoopDate.compare(utcToday) == .orderedAscending {
        let dateStr = getSmallFormatedDate(currentLoopDate)!
        if !arrUnAvailibilityDates.contains(dateStr) {
            arrUnAvailibilityDates.add(dateStr)
        }
        // 每次加一天,继续循环
        guard let nextDay = gregorianCalendar.date(byAdding: .day, value: 1, to: currentLoopDate) else {
            break
        }
        currentLoopDate = nextDay
    }
    // ---------------------------------------------------------------------------
    
    return arrUnAvailibilityDates
}

// 新增一个静态的UTC日期格式化器,避免重复创建
extension DateFormatter {
    static let utcDateFormatter: DateFormatter = {
        let formatter = DateFormatter()
        formatter.timeZone = TimeZone(identifier: "UTC")
        formatter.dateFormat = "yyyy-MM-dd"
        return formatter
    }()
}

func getSmallFormatedDate(_ localDate: Date?) -> String? {
    guard let aDate = localDate else { return nil }
    return DateFormatter.utcDateFormatter.string(from: aDate)
}

func convertCalendarDate(toNormalDate selectedDate: Date?) -> Date? {
    let sourceTimeZone = NSTimeZone(abbreviation: "UTC")
    let destinationTimeZone = NSTimeZone.system as NSTimeZone
    guard let aDate = selectedDate else { return nil }
    
    let sourceGMTOffset = sourceTimeZone?.secondsFromGMT(for: aDate) ?? 0
    let destinationGMTOffset = destinationTimeZone.secondsFromGMT(for: aDate)
    let interval1 = TimeInterval(destinationGMTOffset - sourceGMTOffset)
    
    return Date(timeInterval: interval1, since: aDate)
}

关键细节说明

  • 我新增了一个静态的utcDateFormatter,避免重复创建格式化器,提升性能
  • 处理过去日期时,严格使用UTC格式和原有代码对齐,避免时区差异导致的错误判断
  • 遍历日期时做了去重判断,防止和原来的星期禁用日期重复
  • 用guard let处理了所有可选值,避免潜在的崩溃问题

这样修改后,你的日历就会同时禁用用户选中的星期对应的日期和所有过去的日期啦!

内容的提问来源于stack exchange,提问作者mrs.bassim

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.27 07:36:03