Swift按月份过滤日期数组返回错误结果的原因排查
问题原因分析
你的代码出现不符合预期的过滤结果,核心原因是处理日期时使用了两个不一致的Calendar实例:
- 在
Date的自定义初始化方法中,你用Calendar.current(系统默认日历,包含本地时区)生成日期; - 在
month计算属性中,你用了单独创建的cal实例(ISO8601日历,默认时区为UTC)解析月份。
同一个UTC格式的日期,在不同时区的日历下会被解析为不同的本地日期。比如:
- 若你的本地时区是UTC+5,
Date(month:9, day:30, year:2023)生成的本地时间是2023-09-30 00:00:00,对应的UTC时间是2023-09-29 19:00:00; - 当用UTC时区的ISO8601日历解析这个UTC时间时,得到的月份会和本地日历解析结果产生偏差,导致原本属于9月的日期被错误识别为10月,最终被过滤到结果数组中。
另外,代码里cal作为全局变量被Date的extension直接引用,耦合性高,容易引发意外问题。
修复方案
统一使用同一个Calendar实例来创建和解析日期,两种可选方案:
方案1:统一使用系统默认日历
extension Date { init(month: Int, day: Int, year: Int) { let cal = Calendar.current var dateComponents = DateComponents() dateComponents.month = month dateComponents.day = day dateComponents.year = year self = cal.date(from: dateComponents) ?? Date() } var month: Int { let cal = Calendar.current return cal.component(.month, from: self) } } let sampleArr = [Date(month: 09, day: 30, year: 2023), Date(month: 10, day: 1, year: 2023)] let filterArr = sampleArr.filter{ $0.month == 10 } print(filterArr)
方案2:统一使用ISO8601日历
extension Date { private static let isoCalendar = Calendar(identifier: .iso8601) init(month: Int, day: Int, year: Int) { var dateComponents = DateComponents() dateComponents.month = month dateComponents.day = day dateComponents.year = year self = Date.isoCalendar.date(from: dateComponents) ?? Date() } var month: Int { return Date.isoCalendar.component(.month, from: self) } } let sampleArr = [Date(month: 09, day: 30, year: 2023), Date(month: 10, day: 1, year: 2023)] let filterArr = sampleArr.filter{ $0.month == 10 } print(filterArr)
统一日历实例后,创建日期和解析月份的规则完全一致,过滤结果就能符合预期。
内容的提问来源于stack exchange,提问作者shoji
相关产品推荐
相关产品推荐

