使用enumerateDates未返回预期日期,请求排查原因(Swift)
问题分析与解决
核心原因
你的问题确实和时区有关,但更关键的是startingAfter参数的枚举逻辑:
- 时区转换的UTC显示差异:你期望的
2022-12-31 16:00:00 +0000,本质是本地时区(UTC+8)的2023-01-01 00:00:00——也就是1月1日的第一天,只是print(date)默认输出UTC时间导致显示差异。 startingAfter的枚举规则:当使用.backward方向时,startingAfter定义的是「第一个被枚举日期的后一个时间点」,枚举器不会包含startingAfter本身。你的endDate是本地时区的2023-01-01 12:00,本身属于1月1日(day=1),但因为是startingAfter的目标,直接被跳过,枚举器从它之前的第一个匹配日期(12月1日本地时间,对应UTC2022-11-30 16:00)开始输出。startDate的过滤逻辑:你的startDate是本地时区2022-10-02,对应UTC2022-10-01 16:00。当枚举到10月1日本地时间(UTC2022-09-30 16:00)时,该日期小于startDate,触发stop=true,因此也不会被输出。
修复方案
以下两种方案都能正确包含你期望的日期,同时枚举所有符合条件的月份第一天:
方案1:调整反向枚举的起始点
把startingAfter设置为endDate所在月份的下一个月第一天,让枚举器先找到endDate所在的月份第一天:
let calendar = Calendar.current let startDate = calendar.date(from: DateComponents(year: 2022, month: 10, day: 2))! let endDate = calendar.date(from: DateComponents(year: 2023, month: 1, day: 1, hour: 12))! // 获取endDate所在月份的下一个月第一天,作为startingAfter参数 let nextMonthFirstDay = calendar.date(byAdding: .month, value: 1, to: calendar.date(from: DateComponents(year: 2023, month: 1, day: 1))!)! let firstDayOfMonth = DateComponents(day: 1) calendar.enumerateDates(startingAfter: nextMonthFirstDay, matching: firstDayOfMonth, matchingPolicy: .strict, direction: .backward) { result, exactMatch, stop in guard let date = result else { return } if date < startDate { stop = true } else { // 可选:转换为本地时区字符串,查看更直观 let formatter = DateFormatter() formatter.dateStyle = .full formatter.timeStyle = .full formatter.timeZone = TimeZone.current print("UTC时间:\(date) | 本地时间:\(formatter.string(from: date))") } }
方案2:改用正向枚举(逻辑更直观)
从startDate所在月份的第一天开始,正向枚举到endDate:
let calendar = Calendar.current let startDate = calendar.date(from: DateComponents(year: 2022, month: 10, day: 2))! let endDate = calendar.date(from: DateComponents(year: 2023, month: 1, day: 1, hour: 12))! // 获取startDate所在月份的第一天作为枚举起始点 let startMonthFirstDay = calendar.date(from: calendar.dateComponents([.year, .month], from: startDate))! let firstDayOfMonth = DateComponents(day: 1) calendar.enumerateDates(startingAfter: startMonthFirstDay, matching: firstDayOfMonth, matchingPolicy: .strict, direction: .forward) { result, exactMatch, stop in guard let date = result else { return } if date > endDate { stop = true } else { let formatter = DateFormatter() formatter.dateStyle = .full formatter.timeStyle = .full formatter.timeZone = TimeZone.current print("UTC时间:\(date) | 本地时间:\(formatter.string(from: date))") } }
最终输出(两种方案一致)
UTC时间:2022-12-31 16:00:00 +0000 | 本地时间:2023年1月1日 星期日 中国标准时间 00:00:00 UTC时间:2022-11-30 16:00:00 +0000 | 本地时间:2022年12月1日 星期四 中国标准时间 00:00:00 UTC时间:2022-10-31 16:00:00 +0000 | 本地时间:2022年11月1日 星期二 中国标准时间 00:00:00 UTC时间:2022-09-30 16:00:00 +0000 | 本地时间:2022年10月1日 星期六 中国标准时间 00:00:00
内容的提问来源于stack exchange,提问作者Sheffield
相关产品推荐
相关产品推荐

